meshSeam
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 close 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.
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
This peer's identity. Sent in the MeshHello preamble on each conn.
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).
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.
Source of per-connection nonces. Production defaults to Random.Default; tests pass a seeded Random so the dedup tiebreak is deterministic.
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.
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.