SessionMigration
SessionMigration
StatusActive ReconnectingOutbound/Inbound + IsTransportOnlyReconnect pending
Phases1-MigrationInitiation Β· 2-MigrationDelivery Β· 3-TransportReconnect Β· 4-Recovery
OrchestratorsPresenceDomainOrchestrator Β· DirectConnectionDomainOrchestrator Β· RcsDomainOrchestrator Β· PmsDomainOrchestrator Β· ConnectionBlueprintOrchestrator
SubprocessesNone β calls into PeerSessionEstablishment transport paths but does NOT invoke the handshake sub-sequence
Called byStandalone β triggered by OnConnectivityChanged on the migrating device
Cross-refPeerSessionEstablishment Β· InitiateSessionHandshakeAsync and ConfirmHandshakeAsync are explicitly NOT part of this process
Overview
What it is: Session migration is the process by which two devices maintain their encrypted conversation when one of them changes networks β moving from WiFi to mobile data, switching towers, or changing IP address. The session key is still valid; only the transport path needs to change.
Who's involved: Two trusted peers with an established Ready session. One peer (the migrator) detects the network change. The other peer (the recipient) receives an inbound signal that the migrator has moved.
What success looks like: Both devices are back to SessionStatus.Ready with the same session key and the same PeerSecret they had before the migration. No new handshake has run. No secret has been rotated. Messages queued during the transition deliver automatically.
What failure looks like: Migration pings fail on all paths within the migration window. The session evicts on both sides. Full re-establishment is required β see PeerSessionEstablishment Phase 3b-SessionRecovery. This is recoverable but involves a full handshake and new secret generation.
Why this process exists separately from session establishment
When a peer changes networks, the instinct is to treat it like a new connection β tear down the old session, build a new one, run the full handshake. That is the wrong approach and it is the root cause of the most persistent bugs in this system.
The session key was derived from the relationship key (RK) and committed on both sides. It has not been invalidated. The PeerSecret was committed during the last successful handshake and both sides hold the same value. Running a new handshake generates a new PeerSecret β and if two handshakes run concurrently (which happens when both sides independently try to reconnect), each side commits the result of whichever one arrived last. Those results differ. The secrets diverge. Every subsequent ping from the diverged side fails PeerSecret resolution. The connection is permanently broken until restart.
InitiateSessionHandshakeAsync and ConfirmHandshakeAsync are not part of this process. Any code path that calls them during migration is a bug. These methods are annotated with PeerSessionEstablishment only β never with SessionMigration.
The asymmetry problem
P2P without a server means the two devices are always in different states during a network transition. This asymmetry must be represented explicitly in the state model β shared states that mean different things depending on which side you are on are the source of guard logic failures.
The migrating device (outbound) knows its network has changed. It knows its key is still valid. It is firing migration pings to find a new transport path to the peer. It should not attempt a new handshake β it is waiting for the peer to acknowledge its new address.
The receiving device (inbound) receives a migration ping from a peer it already has a session with. It knows the peer has moved networks. Its key is still valid. It needs to reach the peer at their new address. It also should not run a handshake β the session is already established.
ReconnectingOutbound β migrating device state. Blocks PresenceConnect from firing. No new handshake from this state.
ReconnectingInbound β receiving device state. Allows ConnectPeerAsync through even when SessionStatus==Ready. IsTransportOnlyReconnect=true passed to DC β skips InitiateSessionHandshakeAsync.
User Journey
Participants
| Role | Orchestrator | What they do in this process |
|---|---|---|
| Migration decision maker | PresenceDomainOrchestrator | Detects network change via DC event, sets session to Degraded, fires migration pings, receives inbound migration signal and responds with transport-only reconnect |
| Transport executor | DirectConnectionDomainOrchestrator | Sends migration pings across all transport candidates, handles inbound pings, re-registers session at new endpoint, respects IsTransportOnlyReconnect flag to skip handshake |
| Relay delivery | RcsDomainOrchestrator | Delivers migration ping via RCS queue when direct paths fail; polls for inbound relay-queued pings on heartbeat |
| Endpoint exchange | PmsDomainOrchestrator | Facilitates encrypted endpoint hint exchange via PMS rendezvous channel when direct paths fail and both sides need to learn each other's current network address |
| Candidate ordering | ConnectionBlueprintOrchestrator | Recompiled on migration signal with peer's new endpoint prioritised; provides candidate sequence for transport reconnect |