forked from quic-issues/427e7578-d7bf-49c8-aee9-2dd999e25316
status, created, tags, priority, created_at, depends_on, updated_at, completed_at, completed, transitions
| status | created | tags | priority | created_at | depends_on | updated_at | completed_at | completed | transitions | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| complete | 2026-03-16 |
|
high | 2026-03-16T07:51:47.888Z |
|
2026-03-16T10:01:26.362Z | 2026-03-16T10:01:26.362Z | 2026-03-16 |
|
P2P Networking Layer
Status: ✅ Complete · Priority: High · Created: 2026-03-16 · Tags: p2p, core
Overview
Build the P2P networking layer using PeerJS. This handles connection lifecycle, message passing, and data synchronization between peers. Every poll operates as a "room" where the owner's peer ID is the room identifier.
Design
- Connection model: Star topology per poll—owner acts as relay hub; participants connect to owner
- Peer ID: Derived from user's public key (deterministic, resumable)
- Messages: JSON-based protocol over PeerJS data channels
- Reconnection: Auto-reconnect with exponential backoff
- Sync: On connect, owner sends full poll state snapshot; subsequent changes are incremental messages
Message Protocol
type Message =
| { type: 'poll:state'; payload: Poll }
| { type: 'poll:vote'; payload: Vote }
| { type: 'poll:option:add'; payload: Option }
| { type: 'poll:role:update'; payload: RoleUpdate }
| { type: 'user:profile'; payload: UserProfile }
| { type: 'peer:discovery'; payload: PeerInfo[] }
| { type: 'ack'; payload: { commandId: string; revision: number } }
| { type: 'error'; payload: { commandId: string; message: string } }
| { type: 'sync:request'; payload: { pollId: string } }
Offline Behavior
When the poll owner is offline, the poll is unreachable. Participants can view their local cached copy but cannot submit new votes until the owner reconnects.
Outbox & Acknowledgment
- Every mutation message includes a
commandId(UUID) for deduplication - Sender persists the command in a local IndexedDB outbox before sending
- Owner responds with
ack { commandId, revision }on success orerror { commandId, message }on failure - On reconnect, client resends any unacked commands from the outbox
- Owner deduplicates by
commandId - UI shows "Pending sync" indicator for unacked mutations
Plan
- Create PeerJS service (singleton, manages connection lifecycle)
- Implement message send/receive with typed protocol
- Add connection state management (connecting, connected, disconnected)
- Implement auto-reconnect with backoff
- Add "room" concept—join a poll by connecting to owner's peer ID
- Handle peer disconnect/cleanup
- Implement outbox (IndexedDB-backed pending command queue)
- Implement ack/error response handling
- Implement resend-on-reconnect for unacked commands
Test
- Two browser tabs can establish a PeerJS connection
- Messages round-trip correctly
- Reconnection works after simulated disconnect
- Unacked commands are resent after reconnect
- Owner deduplicates commands by commandId
Notes
- Star topology keeps it simple—owner must be online for live interaction
- Could explore gossip/mesh topology later for resilience, but adds complexity