Transport and the mailbox
1. SSP/1 specifies no transport binding
Section titled “1. SSP/1 specifies no transport binding”SSP/1 defines message semantics. It does not define how bytes move.
That is deliberate. A predecessor protocol bound its versioning to a specific peer-to-peer stream identifier, which meant its entire version story lived in a transport string and a change of transport was a change of protocol version. Keeping the two apart lets a mesh choose a transport on operational grounds without renegotiating what the protocol is.
Two roles must exist. They are not substitutes for one another.
| Role | Purpose |
|---|---|
| Live transport | Direct exchange between nodes that are online at the same time |
| Mailbox | Store-and-forward for nodes that are never online simultaneously |
A mesh MAY run either alone, and MUST run at least one. A mesh with only a live transport cannot serve intermittently connected nodes; a mesh with only a mailbox pays store-and-forward latency for peers sitting next to each other.
2. What a binding MUST supply
Section titled “2. What a binding MUST supply”| Requirement | Detail |
|---|---|
| Confidentiality | Payloads MUST NOT be readable by any party outside the mesh, including the mailbox operator |
| Integrity | Tampering MUST be detectable |
| Peer authentication | A live binding MUST authenticate the peer’s channel key, and the trust resumption rule then applies. A binding that cannot MUST say so, and event authorship then rests solely on per-event signatures |
| Framing | One request and one response per exchange. A binding claiming realtime mode MUST additionally carry unsolicited one-way bodies |
| Version negotiation | See messages |
A binding MUST document which of these it provides and how. A consumer inherits the weakest guarantee in the chain, and cannot reason about that without the statement.
Note the interaction with event signatures: because every event is individually signed, a binding that fails to authenticate its peer degrades confidentiality and availability but not event authorship. That is the property that makes an untrusted intermediary a routing detail.
3. The mailbox contract
Section titled “3. The mailbox contract”A mailbox is an asynchronous channel moving opaque payloads between nodes of one mesh. It is the same abstraction the MLS architecture calls a Delivery Service ([RFC 9750]), and where SOP/1 is deployed — which requires a Delivery Service for MLS anyway — one service SHOULD fill both roles rather than running two store-and-forward systems side by side.
| Property | Requirement |
|---|---|
| Opacity | The operator MUST NOT be able to read payload plaintext |
| Addressing | Payloads are addressed to a site_id within the mesh |
| Durability | An accepted payload MUST be retained until delivered or until a stated retention bound elapses |
| Retention bound | MUST be stated and finite |
| Delivery | At-least-once; duplicates MUST be permitted |
| Ordering | None guaranteed; consumers MUST NOT depend on it |
| Quota | An operator MAY impose one, and MUST reject rather than silently drop |
The retention bound is a privacy control as much as an operational one: an unbounded mailbox is an indefinite archive of traffic metadata held by a party outside the trust boundary.
4. What the mailbox operator can and cannot do
Section titled “4. What the mailbox operator can and cannot do”The operator is inside the availability boundary and outside the confidentiality boundary.
It can withhold, delay, reorder, duplicate and drop payloads, and it observes payload
sizes, timing, and which site_id is corresponding with which. Those are real
attacks and this contract does not prevent them.
It cannot read payloads, forge events, or forge membership decisions.
The consequence for a consumer: mailbox silence is inconclusive. It never constitutes evidence that no message was sent. Where a decision depends on having seen everything, the consumer needs independent evidence rather than an absence.
5. Padding
Section titled “5. Padding”Where a binding pads payloads, it SHOULD use a target-length function whose overhead is bounded and whose bucket boundaries are not ad hoc, rather than inventing size classes. Padding reduces length leakage; it does not eliminate traffic analysis, and an implementation SHOULD NOT describe it as doing so.
6. Default binding profile
Section titled “6. Default binding profile”The default live binding is mutually authenticated QUIC: TLS 1.3 where each endpoint authenticates with an Ed25519 channel key rather than a certificate chain — the raw-public-key shape of [RFC 7250]. This is deliberately the transport family SOP/1 already brings, so a mesh running both layers carries one transport stack.
The channel key is not the identity key. IK_priv may live in a secure element
that signs rarely and deliberately; a channel key negotiates connections constantly
and may rotate freely. The two are joined by a transport binding record:
TransportBinding = COSE_Sign1( protected = { alg : EdDSA, content type : "sovm/ssp-binding-v1", kid : site_id (16 bytes) }, payload = deterministic CBOR { channel_pub : bstr 32, generation : unsigned }, external_aad = mesh_id (16 raw bytes))Rules:
- The record is signed by the node’s root identity key, so it asserts “this channel
key speaks for this
site_id” with the same authority as the identity itself. generationis monotonic persite_id; a record supersedes all lower generations for that node. Channel-key rotation is therefore a new record, not a new identity —site_idnever changes.- Records travel in the sync exchange’s
recordsmember, through the mailbox, or in-band during connection establishment. They are self-verifying.
7. Trust resumption
Section titled “7. Trust resumption”Pairing happens once. Every connection after it is authenticated by the binding, with no human in the loop and no second ceremony:
- The binding authenticates the peer’s channel key.
- The channel key MUST resolve, through a valid transport binding record, to a
site_idwhose identity key is in the local trusted-peer keyring. - Any failure — unknown channel key, no valid binding record,
kidabsent from the keyring, or a resolved identity that differs from the expected peer — is a hard fail: terminate the connection and surface the event.
The hard fail is the security property. An implementation MUST NOT respond to a resolution failure by falling back to a pairing prompt, because a prompt on mismatch is exactly how a machine-in-the-middle converts a detected attack into a second chance. Recovery from a genuine re-provisioning is an explicit, human-initiated re-pair, never an inline dialog.
The mailbox path needs no resumption at all: mailbox traffic carries signed events and self-verifying records, so there is no session trust to resume.
Removal composes cleanly: revoking a node removes its keyring entry, after which step 2 fails for every binding record it ever published — its live connections die with its identity.