Specs
spec/03-cross-domain.md · shown exactly as it appears in the repository
03 — Cross-Domain Connections
Status: Draft, v0.1.
License: CC-BY-4.0 — see LICENSE-DOCS.
Overview
A connection established under one domain may be extended to cover
additional domains without creating a second connection. Extending a
connection MUST reuse the existing key pair and shared secret,
registered a second time under the new domain — generating new key
material would make it a second, distinct connection, not an extension of
the first. This lets one pairwise relationship serve multiple applications
at once (see 00-overview.md for the distinction between a domain and the
application implementing it).
Nothing requires two parties to extend their connection to every domain
they share. Either party can instead keep a separate connection scoped to
one domain, established the ordinary way (02-connections.md) — there's
no special mechanism for this, the user can choose which option to take.
The domain field
A connection record holds a domain field that is an array, not a single
value — initially containing the one domain it was established under, and
gaining one entry each time it is successfully extended to a new domain.
The domain array MUST include every domain the connection is
currently registered under — it is the authoritative record of which
domains the connection spans.
Per-domain display information — a party's name and image, which may
differ by domain — is optional; a user is not required to set it per
domain. Whenever it is set, it MUST be stored in a cxnDomainInfo
record scoped to that (connection, domain) pair, never on the
connection record itself. Two domains sharing a connection may display
entirely different names for the same underlying relationship.
Extending a connection to a new domain
- Requester reuses the connection's existing key pair unchanged. It
appends the new domain to its own
domainarray and creates acxnDomainInforecord for it with statuspending. It sends aconnection/cxnDomainRequestmessage — containing the new domain and its display name/image for that domain — addressed via the connection's existingsendKey, but routed under whichever domain the two parties already share (the peer has not registered anything under the new domain yet, so it cannot yet be reached there). It then registers its own key a second time, now scoped to the new domain, pointing at whichever backend serves that domain for it. - Peer receives the request and simply records it — appending the new
domain to its own
domainarray and creating acxnDomainInforecord with statuspending. An implementation MUST NOT auto-accept or auto-decline the request — extending to a new domain requires the same explicit consent as establishing the connection itself. Accepting or declining is a separate, later action taken by the user. - Peer accepts: registers its own key under the new domain first —
safe to do immediately, since the requester already registered there in
step 1 — then marks its
cxnDomainInfofor the new domain ascxnEstablishedand sends acxnDomainAcceptedmessage back, routed under the new domain itself. - Peer declines: removes the new domain from its own
domainarray, deletes thecxnDomainInforecord, and sends acxnDomainDeclinedmessage back, routed the same way the original request was (the domain the two parties already share) — the peer never registered anything under the rejected domain, so it cannot route through it. An implementation MUST fully remove this pending-domain state on decline, the same complete-removal requirement as ending a connection (see "Ending a connection" in02-connections.md). - Requester receives acceptance: marks its
cxnDomainInfofor the new domain ascxnEstablished. No further registration is needed — it already registered in step 1. - Requester receives decline: removes the new domain from its own
domainarray, deletes thecxnDomainInforecord, and deregisters the key it had proactively registered under that domain in step 1 — the same MUST applies here: nothing is left behind from the declined extension.
Why domain-scoped registration, not a new connection
The router's registry is keyed on (sendKey, domain), not on sendKey
alone (see 02-connections.md). The same key value can therefore be
registered independently once per domain, each registration pointing at
whichever backend and routing information is correct for that domain —
which may belong to an entirely different application than the one that
first established the connection. Extending a connection to a new domain
is a matter of registering an already-existing key a second time, not
minting new key material or performing a new handshake.
Invariant: register before you can be addressed
A party MUST register a key under a domain before sending any message
routed under that domain — never after. This is why step 3's acceptance
registers before sending cxnDomainAccepted (routed under the new domain
itself), while step 1's request can register after sending
cxnDomainRequest (routed under the domain the parties already share, not
the new one) — the rule only binds the sender of a message to the domain
that message is actually routed under. The same rule governs the
connection handshake in 02-connections.md.