OverlayServer
One server's view of the two-tier overlay — the piece that survives a client's failover to a different server (slice 5D).
In a federation, three servers form a fully-meshed core and each game's players connect through whichever server is nearest them. Two facts about a player have to stay in step on the server they are behind:
the attachment directory must say "this player's packets flow through me" (AttachmentDirectory.attach), so the rest of the core routes messages for them here; and
the routed-unicast router must hold the player's local link (RoutedUnicastRouter.registerLocalSpoke), so a message that arrives across the core is actually handed down to them.
Publishing one without the other is a bug: attach-without-register makes the core route to a server that then drops the frame; register-without-attach leaves a reachable player nobody routes to. admit and evict do both together so that invariant is structural — a caller cannot do half of it.
Why this is all failover needs — retry-any-server
Durable game membership ("is this player in game G?") lives in consensus and can never be lost, so a client whose entry server dies does not need to be found — it simply reconnects to any surviving server and re-announces itself, and that server re-admits it straight from the Raft core state. The overlay consequence of that re-admission is exactly one admit call on the new server:
AttachmentDirectory.attach writes
player → newServerinto the replicated directory, which supersedes the oldplayer → deadServerentry under last-writer-wins and re-homes every server's routing to the new server the moment the write converges; andRoutedUnicastRouter.registerLocalSpoke makes the new server able to deliver locally the instant a re-homed frame arrives.
There is no directory lookup on the failover path and no consensus round per reconnect — the directory update is a consequence of re-admission, not a step the client drives.
The stale window is safe by construction
Directory replication is eventually consistent, so between a client's old server dying and its re-admission converging, a sender may still route a unicast to the stale (now dead, or simply wrong) server. That is safe: RoutedUnicastRouter misroutes such a frame to one server which drops it — it is never fanned to a second recipient and never leaks. The guarantee the sender relies on is resend-on-convergence: once the directory names the new server, the same unicast, resent, lands. This type adds no buffering or replay of its own — it only keeps the directory and the router in step so that a resend can land; the resend itself is the sender's concern.
Thread-safety
OverlayServer holds no mutable state of its own — it delegates to AttachmentDirectory (an kotlinx.atomicfu.atomic write clock plus a lock-guarded us.tractat.kuilt.quilter.Quilter) and RoutedUnicastRouter (a reentrantLock-guarded spoke map). Both are individually correct under a multi-threaded dispatcher, so admit/evict/route are too. Construct one per server via overlayServer.
See also
for construction and wiring over the inter-server seams.
for the replicated player → server directory.
for the single-addressee cross-core delivery.
Functions
Publish client → self into the replicated attachment directory only, without registering a per-connection unicast spoke — the deliberate exception to admit's attach-and-register invariant.
Retract client's attachment from the replicated directory only — the detach counterpart of attachDirectoryOnly, with no unicast spoke to remove (there was none). A last-writer-wins tombstone, so peers stop routing here once it converges. See attachDirectoryOnly and #1384.
Route payload to exactly recipient, crossing the core if they are behind another server. Delegates to RoutedUnicastRouter.route: a stale/absent directory entry drops the frame at exactly one destination (never fanned); the caller resends on convergence.