Docs Menu

Specs

spec/appendix-worked-examples.md · shown exactly as it appears in the repository

Appendix: Worked Examples

License: This text is CC-BY-4.0; the field names, enum values, and other machine-readable schema elements defined here are also MPL-2.0. See LICENSE-DOCS and LICENSE.

This appendix is non-normative. It traces the message-level mechanics of 02-connections.md against concrete, illustrative data, so a reader can see the field shapes actually flow instead of only reading the normative rules in isolation. Domain identifiers, keys, and codes below are placeholders; example.chat is not a real Bonafide domain.

A. Connection handshake

Two parties, the inviter and the joiner (terminology from 02-connections.md), both run apps built on the example.chat domain.

Step 1: Inviter generates a key pair and a connection code

The inviter generates an X25519 key pair and a connection code, then registers the code with the router before sharing it out-of-band (e.g. as a QR code):

POST /send-key/register
{
  "sendKey": "7bf483tnrpq2",
  "receiveUrl": "https://inviter.example.chat/a2a/receive",
  "routingToken": "ag3F9...opaque, encrypted with the deployment key...",
  "expiryDate": "2026-10-27T00:00:00.000Z",
  "domain": "example.chat"
}

Here sendKey is the connection code itself: the router's registry doesn't distinguish a "code" from an ordinary key; it's simply the value registered first. The inviter's local connection record is saved with cxnStatus: "codeSent".

Step 2: Joiner checks liveness, generates its own key pair

The joiner checks the code is live:

GET /send-key/7bf483tnrpq2
→ { "isCxnCode": true }

It then generates its own X25519 key pair and registers a fresh key with the router, the key the inviter will use to reach it from now on:

POST /send-key/register
{
  "sendKey": "01933b3e-9a44-7f12-bd03-1c2d3e4f5061",
  "receiveUrl": "https://joiner.example.chat/a2a/receive",
  "routingToken": "k3nQ1...opaque, encrypted with the deployment key...",
  "expiryDate": null,
  "domain": "example.chat"
}

The joiner's local connection record is saved with cxnStatus: "cxnSent".

Step 3: Joiner sends cxnEstablished

The joiner shapes a message: shapeMessage produces an envelope/payload pair per 01-messaging.md:

{
  "envelope": {
    "version": 1,
    "messageId": "f3a1c2e4-...",
    "sendKey": "7bf483tnrpq2",
    "domain": "example.chat"
  },
  "payload": {
    "type": "connection",
    "operation": "cxnEstablished",
    "messageContent": [{
      "sendKey": "01933b3e-2f3d-7c91-9a21-8e1a9b7c0d44",
      "receiveKey": "01933b3e-9a44-7f12-bd03-1c2d3e4f5061",
      "cxnCode": "7bf483tnrpq2",
      "myCxnName": "Sam",
      "alias": {"cxnAliasCode": "..."},
      "dhPublicKey": "k9f2VqT3...joiner's X25519 public key, base64..."
    }]
  }
}

The envelope's sendKey is still the connection code: this message is addressed to whatever row the router has for it, still the inviter's codeSent registration at this point. Per 07-security.md, cxnEstablished is encrypted with the bootstrap deployment key, not a DH-derived secret; none exists yet. Only payload is encrypted (into encryptedPayload); what actually crosses the wire to the router is:

POST /a2a-messaging/send
{
  "version": 1,
  "messageId": "f3a1c2e4-...",
  "sendKey": "7bf483tnrpq2",
  "domain": "example.chat",
  "encryptedPayload": "U2FsdGVkX1...opaque ciphertext..."
}

The router looks up (sendKey, domain), finds the inviter's row, and forwards it, appending the stored routingToken, which it never decrypts or inspects:

POST https://inviter.example.chat/a2a/receive
{
  "version": 1,
  "messageId": "f3a1c2e4-...",
  "sendKey": "7bf483tnrpq2",
  "domain": "example.chat",
  "encryptedPayload": "U2FsdGVkX1...",
  "routingToken": "ag3F9..."
}

Step 4: Inviter replaces its registration and computes the secret

The inviter decrypts the payload (with the deployment key), then atomically repoints its router registration from the connection code to the key the joiner generated on its behalf:

POST /send-key/replace
{
  "oldSendKey": "7bf483tnrpq2",
  "newSendKey": "01933b3e-2f3d-7c91-9a21-8e1a9b7c0d44",
  "domain": "example.chat"
}

It computes dhSharedSecret from its own private key and the joiner's dhPublicKey, discards its private key, and updates its connection record. Note the key swap: the joiner's receiveKey (from step 3) becomes the inviter's own sendKey (how it addresses the joiner from now on), and the joiner's sendKey becomes the inviter's receiveKey:

{
  "sendKey": "01933b3e-9a44-7f12-bd03-1c2d3e4f5061",
  "receiveKey": "01933b3e-2f3d-7c91-9a21-8e1a9b7c0d44",
  "cxnStatus": "cxnEstablished",
  "theirDhPublicKey": "k9f2VqT3...",
  "dhSharedSecret": "n4Qp8r...base64, never transmitted, computed locally...",
  "dhPrivateKey": null
}

Step 5: Inviter sends complete

{
  "envelope": {
    "version": 1,
    "messageId": "a7c9d1f2-...",
    "sendKey": "01933b3e-9a44-7f12-bd03-1c2d3e4f5061",
    "domain": "example.chat"
  },
  "payload": {
    "type": "connection",
    "operation": "complete",
    "messageContent": [{
      "myCxnName": "Jordan",
      "isA": "connection",
      "dhPublicKey": "vR7mZx...inviter's X25519 public key, base64..."
    }]
  }
}

Still encrypted with the bootstrap deployment key, for the same reason as step 3: complete carries the very public key the joiner needs to compute the shared secret for the first time.

Step 6: Joiner completes, sends refreshSubscriptions

The joiner computes the same dhSharedSecret from its own private key and the inviter's dhPublicKey, discards its private key, sets cxnStatus: "cxnEstablished", and sends subscription / refreshSubscriptions. From this message onward, every message on this connection is encrypted with dhSharedSecret rather than the bootstrap key. The inviter replies with its own alias on this same secret, completing the one-way-then-the-other alias exchange from step 3.