Nordhavn / specs / convene / core / Relay

convene / coreport

Relay

Purpose

The delivery swarm — the one capability everything sealed travels through, and nothing central plays it. The relay carries sealed material from a sender to the recipients it is for, and lets a recipient collect what is theirs; that is the whole of the contract. It names its endpoints only as Members — "deliver this to Karin", "give Karin what is hers" — and says nothing about how the delivery is addressed, routed, or recognised on the wire. That silence is deliberate: addressing is a transport concern, and the whole point of a port is that the wire mechanism can be as strong as the threat model demands without any of it entering the domain. A sender never posts to a place a recipient also posts to — it delivers to each recipient, and each recipient collects only what is theirs — so no rendezvous the two of them share ever exists for an observer to cluster a group by. The relay carries and forgets nothing, reads nothing, orders nothing, and refuses nothing — order rides inside the sealed messages themselves, deduplication and assembly are the reader's work, and arrival is eventual, not immediate. This is a port: it lowers to an interface the core emits and a host implements; the contexts hold it as a role player and the app binds an adapter.

Operations

post

Given a recipient Member and an EncryptedMessage, delivers the copy to that recipient; fire and forget, the swarm carries what it cannot read. Result: None

fetch

Given a recipient Member, produces what the swarm holds for them, in no promised order — what has arrived this far, not necessarily everything sent. A member collects only what is theirs. Result: the EncryptedMessages held for the collecting member

post wrapped key

Given a recipient Member and a WrappedKey, delivers the key delivery to that recipient. Result: None

fetch wrapped keys

Given a recipient Member, produces what the swarm holds for them, in no promised order. Result: the WrappedKeys held for the collecting member

post revision

Given a recipient Member and a SealedRevision, delivers the sealed epoch record to that recipient, alongside their key delivery. Result: None

fetch revisions

Given a recipient Member, produces what the swarm holds for them, in no promised order — the reader unseals what it holds the key for and assembles the chain by parent references; a fork in what it can read is evidence, not a merge to attempt. Result: the SealedRevisions held for the collecting member

Notes

The port has two bindings, and the distance between them is the point. The gate binds a trivial in-memory swarm — a map from recipient to the sealed things waiting for them — fast, deterministic, and exactly enough to run the scenarios. The production binding is the modeled mix crate, the transport domain: a traffic-analysis-resistant mix that realises "deliver to a recipient" so an observer of the whole wire cannot tell who sent to whom. Its design is not hidden below this port — it is a domain of its own, specified in mix/ in the transport professional's vocabulary (cells, tumblers, detection, routing).

At the seam a typed value becomes opaque bytes: an EncryptedMessage becomes a Payload, an epoch GroupKey becomes a SealKey, a recipient Member becomes the Peer edge they collect at. That conversion is the whole of the coupling between the two layers, and it lives in the host glue.

spoken of by