meshSeam

suspend fun meshSeam(selfId: PeerId, connections: List<Connection>, dispatcher: CoroutineContext, random: Random = Random.Default, policy: DeliveryPolicy = DeliveryPolicy.Reliable, admission: LinkAdmission = LinkAdmission.AcceptAll, drainBound: Duration = DEFAULT_MESH_DRAIN_BOUND, orderingHoldCapacity: Int = DEFAULT_MESH_ORDERING_HOLD_CAPACITY, onDisplacement: (MeshDisplacement) -> Unit = {}): Mesh

Deprecated

meshSeam is ambiguous — it carries hub semantics (never self-torns on drain) under a name that reads like a generic mesh. Choose by role: a peer/spoke session whose life ends when its last/only peer leaves wants peerMesh (latches Torn on drain, honouring the incoming-completes-on-Torn contract); a start-empty-and-grow host that legitimately sits empty between joiners wants hubMesh (the byte-identical successor to this behaviour). Pre-1.0: no reason to carry three names for two behaviours. The mechanical replacement is hubMesh (behaviour-preserving); switch to peerMesh where the site is a genuine spoke.

Replace with

hubMesh(selfId, connections, dispatcher, random, policy, admission)

Build a fully-connected N-peer mesh Seam from a set of raw point-to-point connections.

For each Connection in connections, meshSeam exchanges a MeshHello preamble (this peer's id plus a random per-connection nonce) to learn the remote PeerId. Both sides of a link exchange their preamble concurrently, so all exchanges in connections run in parallel — this function suspends until every handshake completes.

Dedup (cross-node agreement): if two connections resolve the same remote id (duplicate links from a simultaneous dial), both ends keep the link with the lexicographically smallest link nonce — a canonical, order-independent function of the two per-connection nonces. Because both ends see both nonces, they derive the same survivor and displace the same loser, with no coordination. The old self-relative selfId < remoteId rule could leave a link half-open (the two ends disagreed on the survivor); the nonce-based rule cannot.

The loser is drained, not closed (#2485): it keeps a read loop, is sent one in-band goodbye, and is disposed of only once the remote's goodbye comes back — so the frames a remote that deduped first had already written are still delivered, ahead of the surviving link's, rather than dying at a close. This is the same treatment Mesh.addLink gives a later duplicate; passing duplicate connections here is supported, not merely tolerated.

Per-link failure: if a link's remote peer disconnects or errors, that peer is removed from Seam.peers and the mesh continues operating. The seam remains SeamState.Woven until Seam.close is called.

Dynamic join: admit a link that arrives later via Mesh.addLink.

Parameters

selfId

This peer's identity. Sent in the MeshHello preamble on each conn.

connections

Raw Connections to each prospective peer. These must be fresh and unread. Each is wrapped with singleCollection before reading, so the preamble read and the per-link read loop share ONE collection of Connection.incoming — a cold, single-collection connection (a stream fabric's framed()) works as well as a hot channel-backed one (us.tractat.kuilt.test.fabric.connectionPair).

dispatcher

The scope for the per-link readLoop coroutines (scheduling only — see the thread-safety note on the returned seam). Production callers pass Dispatchers.Default; test callers pass a dispatcher derived from the test scheduler so seam internals share the same virtual clock as the test's withTimeout.

random

Source of per-connection nonces. Production defaults to Random.Default; tests pass a seeded Random so the dedup tiebreak is deterministic.

policy

Delivery policy for the seam's inbound Spool. Defaults to DeliveryPolicy.Reliable (bounded, backpressured). Pass DeliveryPolicy.Lossy for a lossy radio-style fabric or DeliveryPolicy.Strict in tests that assert no overflow.

admission

Per-link admission policy, applied after each link's handshake and before it is published — both to construction-time connections and to every later Mesh.addLink. Defaults to LinkAdmission.AcceptAll (byte-identical to today's open behaviour); once supplied, the policy is authoritative for every link, including unattested ones. Reject-and-continue: a construction-time connection the policy declines is closed and dropped, and the mesh is still built from the surviving links — one rejected joiner never fails construction nor cancels the concurrent sibling handshakes. A rejected link is never published, so it can never contend in dedup, join Seam.peers, or land in the attestedPrincipals roster.

drainBound

how long a deduplicated loser is drained before the seam gives up on the remote's goodbye and disposes of the link anyway (#2474). A zombie-link backstop, never the mechanism: the healthy path terminates in-band on the goodbye, milliseconds after the swap, with no timer involved. Production default DEFAULT_MESH_DRAIN_BOUND; tests inject a small value and drive it with advanceTimeBy. Runs on dispatcher, so it advances under virtual time.

orderingHoldCapacity

how many live-link frames a peer's receiver ordering hold buffers before it releases early and accepts the reorder (#2474). Bounded, never unbounded; backpressuring instead would stall staging on a release only the drained link can perform. Production default DEFAULT_MESH_ORDERING_HOLD_CAPACITY.

onDisplacement

raised when a displaced link finishes draining, and when a peer's ordering hold overflows. kuilt-core is logger-free, so this is how those surface to a consumer's own logger. Best-effort and non-suspending; defaults to a silent absorb. See MeshDisplacement.