-
Notifications
You must be signed in to change notification settings - Fork 7
Description
Problem
When a peer reconnects with the same peer ID but has lost its state (e.g., external peer ID config + DB loss), we can't detect this. Promises waiting for resolution from that peer will hang forever since the remote no longer knows about them.
Solution
Add an incarnation ID (UUID) generated at kernel start that is exchanged via handshake. If incarnation changes on reconnection, reject promises from the old incarnation.
Key insight: Kernel state normally persists across restarts. Incarnation detection is for the edge case where peer ID is preserved but state is lost.
Dependency
Blocked by #744 - Must wait for message sequencing PR to merge first.
PR #744 introduces PeerConnectionState class which is the ideal place for incarnation tracking. It also provides rejectAllPending() method we can reuse for promise rejection on incarnation change.
Implementation Plan
1. Add Handshake Message Type
File: packages/ocap-kernel/src/remotes/RemoteHandle.ts
Extend RemoteMessageBase to include:
{ method: 'handshake'; params: { incarnationId: string } }{ method: 'handshakeAck'; params: { incarnationId: string } }
2. Generate Incarnation ID in Kernel
File: packages/ocap-kernel/src/Kernel.ts
- Generate
incarnationId = crypto.randomUUID()in constructor - Store in memory only (intentionally NOT persisted)
- Pass to RemoteManager / network layer
3. Extend PeerConnectionState for Incarnation Tracking
File: packages/ocap-kernel/src/remotes/PeerConnectionState.ts (from #744)
Add #remoteIncarnationId field and methods:
setRemoteIncarnation(id)- returns true if incarnation changedgetRemoteIncarnation()- getter
4. Handle Handshake in network.ts
File: packages/ocap-kernel/src/remotes/network.ts
- Add
localIncarnationIdparameter toinitNetwork() - Handle incoming
handshake→ store incarnation, reply withhandshakeAck - Handle incoming
handshakeAck→ store incarnation - On incarnation change → call
peerState.rejectAllPending() - Send
handshakewhen connection established (outbound initiates)
5. Wire Up Incarnation ID
remote-comms.ts- Pass incarnation ID from Kernel to network layerRemoteManager.ts- Also reject kernel promises via existing#handleRemoteGiveUppattern
Files to Modify
| File | Changes |
|---|---|
Kernel.ts |
Generate incarnation ID |
PeerConnectionState.ts |
Add incarnation tracking |
network.ts |
Handshake protocol, incarnation change detection |
remote-comms.ts |
Pass incarnation ID |
RemoteHandle.ts |
Add handshake message types |
RemoteManager.ts |
Reject kernel promises on incarnation change |
Edge Cases
- Both sides reconnecting - Outbound initiates handshake, inbound responds
- Messages before handshake - Queue in
PeerConnectionStateuntil handshake completes - First connection - No previous incarnation, just store (no rejection)
- Same incarnation - No rejection, normal operation
- Handshake timeout - Treat as connection failure, trigger reconnection
Acceptance Criteria
- Incarnation ID generated at kernel start
- Handshake exchanged on connection establishment
- Incarnation changes detected on reconnection
- Pending messages rejected via
rejectAllPending()on incarnation change - Kernel promises rejected via
#handleRemoteGiveUppattern - New connections work normally after incarnation change
- Unit tests for incarnation tracking and handshake
- Integration test for incarnation change detection