Init with specs
This commit is contained in:
81
specs/002-p2p-networking/README.md
Normal file
81
specs/002-p2p-networking/README.md
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
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
|
||||
Reference in New Issue
Block a user