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 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 instead. This lets one pairwise relationship serve multiple applications at once (see 00-overview.md for the domain/application distinction).
Nothing requires two parties to extend to every domain they share; either can instead keep a separate connection scoped to one domain, established the ordinary way (02-connections.md). There's no special mechanism for this, just a choice.
The domain field
A connection record holds a domain field that is an array, not a single value: it starts with the one domain the connection was established under and gains an entry each time it's successfully extended. The domain array MUST include every domain the connection is currently registered under; it's 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. Whenever it's 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 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, creates acxnDomainInforecord for it with statuspending, and sends aconnection/cxnDomainRequestmessage (the new domain plus its display name/image) addressed via the connection's existingsendKey, routed under whichever domain the two parties already share (the peer hasn't registered under the new domain yet, so it can't be reached there). It then registers its own key a second time, now scoped to the new domain, pointing at whichever backend serves it there. - Peer receives the request and simply records it: appends the new domain to its
domainarray and creates acxnDomainInforecord with statuspending. An implementation MUST NOT auto-accept or auto-decline: extending to a new domain needs the same explicit consent as establishing the connection itself. Accepting or declining is a separate, later user action. - Peer accepts: registers its own key under the new domain first (safe immediately, since the requester already registered there in step 1), marks its
cxnDomainInfofor the new domain ascxnEstablished, and sends acxnDomainAcceptedmessage back, routed under the new domain itself. - Peer declines: removes the new domain from its
domainarray, deletes thecxnDomainInforecord, and sends acxnDomainDeclinedmessage back, routed the same way the original request was (the domain the parties already share): the peer never registered anything under the rejected domain, so it can't 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
domainarray, deletes thecxnDomainInforecord, and deregisters the key it proactively registered under that domain in step 1; the same MUST applies: 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 sendKey alone (see 02-connections.md). The same key can therefore be registered independently once per domain, each registration pointing at whichever backend and routing information is correct for that domain, possibly an entirely different application than the one that first established the connection. Extending to a new domain is just 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), while step 1's request can register after sending cxnDomainRequest (routed under the domain the parties already share, not the new one).