6.9 KiB
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.
class MigrationCoordinator {
constructor(options: {
localPeerId: string;
localMeshId: string;
bus: NetworkBus;
});
initiateMigration(request: MigrationRequest): Promise<MigrationResult>;
acceptMigration(request: MigrationRequest): Promise<MigrationResult>;
}
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:
- Request —
MigrationCoordinatorsendsmigration:requestto the target mesh's assignment controller. - Accept — Target mesh admits the peer via
AdmissionController. - Snapshot — Old host serializes peer state (registry entry, topology position, session memberships) and sends it to the new host.
- Transfer — New host installs the peer into its local registry and topology.
- Tunnel — Old host starts a forwarding tunnel for messages addressed to the migrating peer.
- Cutover — Peer disconnects from old mesh and connects to new mesh.
- Drain — Old host drains the forwarding tunnel (default 30s or until empty).
- Complete — Old host removes peer from its registry.
migration:completedis emitted on both meshes' buses.
2. StateTransferProtocol
Serializes and deserializes peer state for migration.
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.
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<void>;
forward(envelope: MessageEnvelope): void;
getBufferedCount(): number;
}
Behaviour:
- All messages addressed to
peerIdthat arrive at the old host are forwarded to the new host viaRelayManager. - If the buffer exceeds
maxBufferedMessages, oldest messages are dropped (application is notified). - After
drainTimeoutMs, the tunnel is closed regardless of buffer state. ForwardingTunnelis only used forgracefulmigrations.urgentmigrations skip the tunnel and accept message loss.
Error codes
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<MigrationErrorCode>.
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—MigrationCoordinatorexecutes a 8-step migration flow (request, accept, snapshot, transfer, tunnel, cutover, drain, complete).NET-100—StateTransferProtocolserializes peer registry, topology, and session state using@suaveplan/bitpacker.NET-101—ForwardingTunnelbuffers messages for the migrating peer and forwards them to the new host.NET-102—ForwardingTunneldrops oldest messages when buffer exceedsmaxBufferedMessages.NET-103—ForwardingTunnelcloses afterdrainTimeoutMsregardless of buffer state.NET-104—urgentmigrations skip the forwarding tunnel and accept message loss.NET-105—migration:completedis 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.