Skip to content

feat(protocol): a configured node names no application on the wire - #690

Closed
bahdotsh wants to merge 1 commit into
feat/app-inbound-gatefrom
feat/sealed-app-id
Closed

bahdotsh wants to merge 1 commit into
feat/app-inbound-gatefrom
feat/sealed-app-id

Conversation

@bahdotsh

@bahdotsh bahdotsh commented Nov 4, 2026

Copy link
Copy Markdown
Member

Step 9 of the shared-network plan (ADR 0037). Stacked on #689.

What changes

A node configured with an application publisher key no longer names its application in the clear.

  • Wire: every frame the node originates carries app_id = "offline" (WIRE_APP_ID, in offline-protocol-sealed). The TransportManager stamps it at the send boundary, next to the codec stamp, and only when the sender is this node. A frame it forwards keeps the app_id its origin gave it.
  • Sealed: the real id goes into RichPayloadV1.app_id. Toward a rich-capable recipient, a configured node seals it on every message, in an id-only body when nothing else needs sealing. Group sends do the same.
  • Receive: a sealed app_id that is a valid AppId overrides the wire copy, so MessageReceived reports the real application.
  • Unconfigured nodes: byte for byte unchanged.

What it does not hide

  • Acknowledgements, control frames and plaintext sends say only offline.
  • Toward a recipient that has not advertised rich payload support, the id is lost (the recipient reports offline), not sent in the clear. This is the same downgrade rule as every other sealed extra.
  • app_id was never in a signed payload, so stamping after signing changes no signature.

Tests

  • a_configured_node_stamps_the_constant_and_seals_the_real_application_id: two members with a real MLS session. The wire frame says offline, and the receiver reports com.example.chat. Mutation-checked: disabling the stamp fails the test, and disabling the restore fails it too.
  • an_unconfigured_node_stamps_its_own_application_id_as_before.
  • the_wire_app_id_is_stamped_only_on_frames_this_node_originates (transport manager): a relayed frame keeps its origin's id.
  • cargo test -p offline-protocol --lib: 2214 passed. cargo clippy --workspace -- -D warnings is clean.

Spec

  • wire-format.md: the configured-node paragraph.
  • encryption-envelopes.md: the app_id field and restore rule 5.

The threat-model update (R17) lands with step 12.

CI does not run on a PR whose base is not main. It will run once the stack below has merged.

A node configured with an application publisher key stamps the constant
"offline" as the wire app_id on every frame it originates, and seals the
real id inside RichPayloadV1.app_id, which a receiver restores as
authoritative. A forwarded frame keeps its origin's app_id. Unconfigured
nodes are unchanged (ADR 0037 step 9).
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant