Repository navigation
Conversation
Step 7c of the shared network plan (ADR 0037, "Service discovery"), the services half. - The sealed layer gains the discovery constants, in their one home (ADR 0022): the body key label `offline-app-svc-v1`, the 16-byte salt, the sentinel `__sealed` a configured node writes in the cleartext `service_id` (a service id may not begin with `__`, so no provider can register it), and `discovery_bound_body`, the bytes a discovery proof binds: `u16be(len(query_id)) || query_id || originator(21) || sealed`. Pinned by a new `discovery` block in the membership vectors, and the discovery query vector now binds that body. no_std clean. - `DiscoverySealer`: what the engine lends a configured node (the application key, the certificate, the identity key, a clock), so this crate links no cipher and knows nothing of MLS. - With a sealer, a query carries only `query_id`, `originator` and the hop budget in the clear; the service asked for, `issued_at` and the originator's identity key travel sealed, with a proof of kind 4. A provider answers only a proven query, only while `|now - issued_at|` is within the 60 s lifetime, once per `query_id`, addressed to the originator. An originator reports only a response whose sealed provider is its sender and whose proof verifies. Forwarding is unchanged either way, and the sealed fields ride through every forwarder, configured or not. - Without a sealer every frame is byte for byte what it was. The proof travels in a `member_proof` payload field, not frame metadata: every forwarder builds a new frame, so metadata would not survive a hop.
…ation's rules Step 7c of the shared network plan (ADR 0037, "Service discovery"), the engine half. - `MembershipKeys`: this node's membership borrowed apart from the rest of the engine, the one place the end-to-end proof is made and checked for session-opening frames and discovery alike. It implements `DiscoverySealer`: the body under `K_app` with a fresh salt, the proof of kinds 4 and 5, and an identity key carried in a sealed body that must derive to the address it speaks for. A node with the key and no certificate sends random bytes in the proof's place. - With a publisher key set, discovery fans out to the mesh neighbours only, never to `known_peers`, which membership makes an admitted set. - The inbound gate no longer passes every `__SVC_DISC` frame: a query, and a response on its way to someone else, pass whoever sent them; a response that ends here passes only from an admitted sender or with its provider's proof. The acknowledgement point follows the same rule, so a member acknowledges gossip from anyone (no ack pattern marks its fellow members on the link) and a proven answer is not resent to its expiry. - The control-message chapter states the configured node's discovery wire: the sentinel, the sealed body, the bound body, where the proof travels and why, and the freshness and once rules. Tests over a line of nodes on the real send, relay and receive paths: two members three hops apart find each other's service through a member of another application and an unconfigured node, which carry both frames, never see the service id, and neither answer nor report; a stranger's query is forwarded and never answered; a forged answer to a member gets no event and no acknowledgement; an unconfigured originator reports as before. Disabling the response gate or the query proof check fails one each.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step 7c of the shared-network plan (ADR 0037, "Service discovery"). Stacked on #693.
On a configured node, service discovery stops naming services in the clear. A provider answers only queries proven by a member of its own application. This replaces the
__SVC_DISC*exemption that #689 left in the inbound gate.Wire
HKDF(K_app, salt, "offline-app-svc-v1")assalt(16) || ChaCha20-Poly1305(nonce 0, no AAD).service_idis the sentinel__sealed. NoServiceIdcan be that value, andregister_servicerefuses it.{issued_at, identity_key, service_id?}.{provider, identity_key, service_id, version, capabilities, hop_count}.member_proofpayload field. It is not in frame metadata, because every forwarder builds a new frame. The proof bindsu16be(len(query_id)) || query_id || originator(21) || sealed, which no hop changes. The hop budget is not bound, because every forwarder rewrites it.derive_address(identity_key)matches the address the proof speaks for.skip_serializing_if).discoveryblock inapp-membership-v1.vectors.json.Rules
|now - issued_at| <= 60 s. One answer perquery_id, held in an answered map untilissued_at + 60. At its cap the map refuses rather than evicts, so the failure mode is a lost result, never a double answer.Tests
tests/membership_discovery.rs): 4 tests on a line of nodes, run over the real paths.a_forged_answer_to_a_member_is_not_acknowledgedfail.Rebase
Rebased onto #693.
generate.py --checkpasses.membership_configured(both stacks added it) was removed.cargo clippy --workspace -- -D warnings: pass.cargo test --workspace --lib: pass, except the local-onlyno_shipped_file_names_a_relay, whose offenders are all under a stale.claude/worktrees.Known
sealed/member_prooffields, so configured discovery does not cross older nodes.CI runs only once the stack below merges into main.