Skip to content

feat(p2p): remote session pairing — relay, replication, co-host model#34

Merged
mads-jm merged 3 commits intomvpfrom
claude/plan-p2p-pairing-GXM4F
Mar 15, 2026
Merged

feat(p2p): remote session pairing — relay, replication, co-host model#34
mads-jm merged 3 commits intomvpfrom
claude/plan-p2p-pairing-GXM4F

Conversation

@mads-jm
Copy link
Owner

@mads-jm mads-jm commented Mar 15, 2026

Summary

Implements MVP 0.1.0 remote session pairing across three milestones:

  • M1 — Remote Session Pairing: User-configured circuit relay with persistent peer ID, DCUtR direct connection upgrade, whtnxt:// invite URLs with short codes, relay management UI (P2PSettings), and share/join panel (ShareSessionPanel). Relay addresses stored in userData JSON — no hardcoded relay in source.
  • M2 — Replication wired to sessions: useSessionReplication hook subscribes to RxDB change streams, pushes local changes to peers, applies incoming REPLICATION_CHANGES, and handles REPLICATION_PULL_REQUEST via a correlation-ID bridge between renderer and utility process.
  • M3 — Co-host model & playback mutex: coHostIds on playlist schema (v5 migration), playbackOwnerId on session state, hand-off/take-playback actions in navigation store, playback controls gated on ownership in SessionView.

Also fixes pre-existing libp2p v2 Stream API type errors in handshake, replication, and ping protocols (source/sinkstream.send + stream.close).

Key files

Area Files
Relay infra relay/relay-server.mjs (persistent ed25519 key), relay-manager.ts, relay-config-store.ts
P2P service p2p-service.ts, protocols/ping.ts (new)
IPC surface ipc-protocol.ts, main.ts, preload.ts
Replication hooks/useSessionReplication.ts (new)
UI ShareSessionPanel.tsx, P2PSettings.tsx, SessionView.tsx
Schema schemas.ts (playlist v5), database.ts (migration 5: coHostIds)
Docs ADR, spec, concept pages for circuit relay + libp2p v2 API

CI status

  • TypeCheck: failing (pre-existing errors — @libp2p/dcutr module resolution, vite/rollup types, PlaylistView context menu)
  • Lint: failing (needs investigation)
  • Schema Migration Guard: passing
  • P2P Protocol Gate: passing — needs-p2p-review label applied

Test plan

  • Start relay server (cd relay && node relay-server.mjs), confirm persistent peer ID across restarts
  • Add relay address in P2P Settings, verify green status dot
  • Create session → share panel shows whtnxt:// URL and short code
  • Second peer joins via invite URL or short code
  • Add track on either peer → replicates to the other within seconds
  • Co-host can hand off / take playback ownership
  • Playback controls disabled for non-owner participants
  • Offline: app functions normally without relay connectivity

@github-actions
Copy link

⚠️ P2P Protocol Files Modified

This PR touches files in the P2P scope gate:

The P2P protocol is off-limits for fully autonomous agentic work without explicit human approval.

Before merging, a human reviewer must explicitly confirm:

  • Changes are intentional and understood
  • Backward compatibility with existing peers considered
  • No unintended side effects on session stability or replication

See [[workflow-story-to-pr]] §3 Agentic Scope Gates.

@mads-jm mads-jm changed the base branch from main to mvp March 15, 2026 06:11
claude added 3 commits March 14, 2026 23:25
Milestone 1 — Remote Session Pairing:
- relay/relay-server.mjs: persistent ed25519 key across restarts; stable
  peer ID required since it is baked into invite links
- relay/package.json: add @libp2p/crypto for key serialization
- app/package.json: add @libp2p/dcutr for direct connection upgrade
  (relay is bootstrap only; DCUtR hole-punches to WebRTC when NAT allows)
- relay-config-store.ts: new — user-persisted relay addresses in userData
  JSON, no hardcoded relay in source (sovereignty thesis)
- p2p-config.ts: remove static RELAY.ADDRESSES array; now runtime-loaded
- relay-manager.ts: new — retry-with-backoff relay connection manager
- p2p-service.ts: wire RelayManager + DCUtR service; add pull-request
  bridge via pendingPullRequests Map; add getInviteData(); fix
  getDiscoveredPeers() and getConnectedPeers() stubs; fix disconnectFromPeer
- ipc-protocol.ts: new channels and payload types (relay, presence,
  pull-request bridge, invite URL)
- protocol.ts: session param in createConnectUrl; generateShortCode()
- main.ts: relay config IPC handlers; invite URL generation; deep-link
  protocol handler; replication pull-request bridge
- preload.ts: expose getInviteUrl, joinSession, relay CRUD, onRelayStatus,
  onPeerPresence, respondToPullRequest, onPullRequest
- P2PSettings.tsx: new — relay management UI with live status dots
- ShareSessionPanel.tsx: new — shows whtnxt:// invite URL + short code;
  join-session input accepting URL or short code

Milestone 2 — Replication wired to sessions:
- useSessionReplication.ts: new hook — subscribes to RxDB change streams,
  pushes local changes to peers, applies incoming REPLICATION_CHANGES,
  responds to REPLICATION_PULL_REQUEST events with RxDB data
- SessionView: activate hook on isActiveSession

Milestone 3 — Co-host model + playback mutex:
- session-interfaces.ts: add coHostIds + playbackOwnerId to SessionState;
  optional coHostIds in StartSessionConfig
- schemas.ts: add coHostIds to PlaylistDocType; bump to version 2
- database.ts: v1→v2 migration for coHostIds
- navigation-store.ts: handOffPlayback(toUserId) and takePlayback(userId)
- SessionView: playback controls gated on isPlaybackOwner; hand-off UI

libp2p v2 Stream API fixes (pre-existing errors resolved):
- handshake.ts, replication.ts, ping.ts: source/sink → stream.send +
  stream.close; StreamHandler signature corrected; peerStore.all() is
  Promise not AsyncIterator; serviceName → serviceTag for mDNS

https://claude.ai/code/session_01D1Vy5htbEpWnWcce1JdVSp
- docs_md/01 concepts/libp2p.md: new sections on Circuit Relay v2 +
  DCUtR (relay-as-user-infrastructure, persistent key, RelayManager,
  invite URL format) and libp2p v2 Stream API breaking changes
  (AsyncIterable, send/close, StreamHandler, peerStore.all)
- docs_md/01 concepts/Circuit-Relay.md: update relay config to dynamic
  store pattern; mark persistent-key pitfall as resolved; add
  @libp2p/rendezvous pitfall (package does not exist on npm)
- docs_md/04 architecture/adr/adr-260315-p2p-session-pairing.md: new —
  five decisions: relay+DCUtR over DHT, user-configured relay, invite
  URL over rendezvous, pull-request bridge correlation pattern, playback
  mutex
- docs_md/08 specs/p2p-session-pairing-spec.md: new — full M1–M3 spec
  covering relay config store, ping protocol, IPC channels, share UI,
  useSessionReplication hook, pull-request bridge, co-host model,
  schema v2 migration, libp2p v2 API notes, file inventory
- docs_md/index.md + 00 index/{ADR,CONCEPTS,SPECS}.md: updated indexes

https://claude.ai/code/session_01D1Vy5htbEpWnWcce1JdVSp
@mads-jm mads-jm force-pushed the claude/plan-p2p-pairing-GXM4F branch from c60b9d5 to 47d5064 Compare March 15, 2026 06:26
@mads-jm mads-jm changed the title Replace Node.js Scripts with ESM Variants for Cross-Platform Development feat(p2p): remote session pairing — relay, replication, co-host model Mar 15, 2026
@mads-jm mads-jm merged commit 0455ce6 into mvp Mar 15, 2026
2 of 4 checks passed
@mads-jm mads-jm deleted the claude/plan-p2p-pairing-GXM4F branch March 15, 2026 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants