# OpenSpec Change: network-plugin-migration - **Change ID**: network-plugin-migration - **Package**: `@suaveplan/network-plugin-migration` (new) - **Status**: PROPOSED - **Date**: 2026-05-01 --- ## Summary Implement peer state migration between meshes. When `network-plugin-assignment` rebalances a peer from an overloaded mesh to an underloaded one, or when a peer manually requests to join a different mesh, there is currently no mechanism to transfer state or maintain message continuity during the handoff. This plugin provides the migration protocol: state snapshot transfer, graceful handoff with a forwarding tunnel, and session persistence across mesh boundaries. --- ## Why **User requirement**: *"automatically place a new connection in a tiered mesh with available spaces"* implies rebalancing, which implies migration. **Current state**: No migration logic exists anywhere in the network suite. **What's missing**: - State transfer when a peer moves from Mesh A to Mesh B - Graceful handoff: old host forwards messages to new host during transition - Session persistence across mesh boundaries --- ## Core components ### 1. `MigrationCoordinator` Orchestrates the end-to-end migration flow. ```ts class MigrationCoordinator { constructor(options: { localPeerId: string; localMeshId: string; bus: NetworkBus; }); initiateMigration(request: MigrationRequest): Promise; acceptMigration(request: MigrationRequest): Promise; } interface MigrationRequest { peerId: string; fromMeshId: string; toMeshId: string; reason: string; // e.g. "rebalance", "user-request", "region-optimization" priority: "graceful" | "urgent"; } interface MigrationResult { success: boolean; peerId: string; newMeshId: string; transferredBytes: number; durationMs: number; forwardedMessages: number; } ``` **Migration flow**: 1. **Request** — `MigrationCoordinator` sends `migration:request` to the target mesh's assignment controller. 2. **Accept** — Target mesh admits the peer via `AdmissionController`. 3. **Snapshot** — Old host serializes peer state (registry entry, topology position, session memberships) and sends it to the new host. 4. **Transfer** — New host installs the peer into its local registry and topology. 5. **Tunnel** — Old host starts a forwarding tunnel for messages addressed to the migrating peer. 6. **Cutover** — Peer disconnects from old mesh and connects to new mesh. 7. **Drain** — Old host drains the forwarding tunnel (default 30s or until empty). 8. **Complete** — Old host removes peer from its registry. `migration:completed` is emitted on both meshes' buses. ### 2. `StateTransferProtocol` Serializes and deserializes peer state for migration. ```ts class StateTransferProtocol { serialize(peerId: string, registry: RegistrySnapshot, topology: TopologyPosition, sessions: SessionMembership[]): Uint8Array; deserialize(bytes: Uint8Array): PeerStateSnapshot; } interface PeerStateSnapshot { peerId: string; registryEntry: PeerRegistryEntry; topologyPosition: TopologyPosition; sessionMemberships: SessionMembership[]; version: number; // monotonic, for conflict detection } ``` Uses `@suaveplan/bitpacker` for compact binary encoding. Max snapshot size: 64 KiB (peers with larger state must use lazy rehydration). ### 3. `ForwardingTunnel` Temporary message forwarding from old host to new host during cutover. ```ts class ForwardingTunnel { constructor(options: { fromHostId: string; toHostId: string; peerId: string; relay: RelayManager; // from network-plugin-relay drainTimeoutMs: number; // default 30000 maxBufferedMessages: number; // default 1000 }); open(): void; close(): Promise; forward(envelope: MessageEnvelope): void; getBufferedCount(): number; } ``` **Behaviour**: - All messages addressed to `peerId` that arrive at the old host are forwarded to the new host via `RelayManager`. - If the buffer exceeds `maxBufferedMessages`, oldest messages are dropped (application is notified). - After `drainTimeoutMs`, the tunnel is closed regardless of buffer state. - `ForwardingTunnel` is only used for `graceful` migrations. `urgent` migrations skip the tunnel and accept message loss. --- ## Error codes ```ts const MIGRATE_CODES = defineCodes("MIGRATE", [ "TARGET_MESH_REJECTED", "STATE_TRANSFER_FAILED", "TUNNEL_OVERFLOW", "TUNNEL_TIMEOUT", "CUTOVER_FAILED", "MIGRATION_ABORTED", "INVALID_SNAPSHOT", ] as const); ``` `MigrationError extends SuaveplanError`. --- ## Dependencies - **Consumes**: `@suaveplan/network` (`NetworkNode`, `NetworkBus`), `@suaveplan/network-plugin-relay` (`RelayManager`, cross-mesh forwarding), `@suaveplan/network-plugin-assignment` (`AdmissionController`, `MeshBalancer`), `@suaveplan/network-plugin-registry` (`RegistrySnapshot`), `@suaveplan/network-plugin-topology` (`TopologyPosition`), `@suaveplan/network-plugin-session` (`SessionMembership`), `@suaveplan/bitpacker` (snapshot encoding), `@suaveplan/error`, `@suaveplan/types`, `@opentelemetry/api`. - **Blocked on**: `network-core` (shipped), `network-plugin-relay`, `network-plugin-assignment`, `network-plugin-registry`, `network-plugin-topology`, `network-plugin-session`. --- ## Verification approach - `src/coordinator/coordinator.spec-conformance.test.ts` — pins full migration flow: request → accept → snapshot → transfer → tunnel → cutover → drain. - `src/state-transfer/state-transfer.spec-conformance.test.ts` — pins serialize/deserialize roundtrip, version tracking, size limits. - `src/tunnel/forwarding-tunnel.spec-conformance.test.ts` — pins buffer limits, overflow drop, drain timeout. - `tests/integration/migration.spec-conformance.test.ts` — end-to-end: peer moves from Mesh A to Mesh B, messages continue to arrive. - 100/100/100/100 Istanbul coverage. --- ## Conformance requirements (proposed) - `NET-99` — `MigrationCoordinator` executes a 8-step migration flow (request, accept, snapshot, transfer, tunnel, cutover, drain, complete). - `NET-100` — `StateTransferProtocol` serializes peer registry, topology, and session state using `@suaveplan/bitpacker`. - `NET-101` — `ForwardingTunnel` buffers messages for the migrating peer and forwards them to the new host. - `NET-102` — `ForwardingTunnel` drops oldest messages when buffer exceeds `maxBufferedMessages`. - `NET-103` — `ForwardingTunnel` closes after `drainTimeoutMs` regardless of buffer state. - `NET-104` — `urgent` migrations skip the forwarding tunnel and accept message loss. - `NET-105` — `migration:completed` is emitted on both the source and target mesh buses. --- ## Out of scope - **Live migration without disconnect** → v2; v1 requires a brief disconnect/cutover. - **Migration of in-flight game state** → application-level; v1 transfers session memberships only. - **Rollback on failure** → v2; v1 reports failure and leaves application to retry.