--- status: planned created: '2026-03-16' tags: - p2p - core priority: high created_at: '2026-03-16T07:51:47.888Z' depends_on: - 001-project-setup updated_at: '2026-03-16T07:52:03.104Z' --- # P2P Networking Layer > **Status**: 🗓️ Planned · **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 ```typescript 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 or `error { 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