From d30e612be88a961ac4f67674153436e4613c9cfe Mon Sep 17 00:00:00 2001 From: George Coles Date: Tue, 15 Sep 2026 05:54:52 -0400 Subject: [PATCH] Phase B: identifier+key on the wire, JCS envelope signing (FRX/0.5), registry binding --- AGENTS.md | 5 ++-- src/crypto.rs | 35 +++++++++++++++--------- src/lib.rs | 2 +- src/message.rs | 8 ++++-- src/node.rs | 65 ++++++++++++++++++++++++++++++++------------ src/relay.rs | 2 +- tests/common/mod.rs | 13 +++++++-- tests/concurrency.rs | 4 +-- tests/conformance.rs | 54 ++++++++++++++++++++---------------- tests/phase1.rs | 6 ++-- tests/purges.rs | 22 +++++++-------- tests/registry.rs | 32 ++++++++++++++-------- 12 files changed, 159 insertions(+), 89 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f9e4289..e8bb264 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,8 @@ - I2 is not blanket anti-centralization: shared coordination (identity, admission, contract) is centralized in the MA because common state is cheaper held once; decisions that consume local information (matching, relevance, sharing, retention) stay local. Off-wire conduct (link handling, retention, gating) is contract, not conformance. ## Implementation notes -- Draft 0.5 specifies JCS (RFC 8785) envelope signing over `{type, from, key, ts, nonce, body}` with `from` = MA-hosted identifier and `key` = pubkey. The code still signs the provisional `FRX/0.4` scheme (`src/crypto.rs`) until Phase B lands — never present current code as interoperable. +- Wire format matches Draft 0.5: envelope `{type, from, key, ts, nonce, body, sig}` with `from` = identifier, `key` = pubkey; signatures cover the JCS canonical form of the unsigned envelope under prefix `FRX/0.5` (`src/crypto.rs`). Our `canonical_json` is JCS-compatible only for the restricted schema (ASCII keys, integers, no floats) — golden vectors in `tests/conformance.rs` pin the bytes and signature; revisit before claiming interop with non-Rust stacks. +- Relay addresses transport mailboxes by `key` (unicast `to` = recipient pubkey; queues keyed by pubkey); the identifier is protocol identity only. Registry binding checks `map[key].id == from`. - Built: envelope/query/response, Tantivy index, aggregates, member directory. Not built: dashboard UI, directory watching, TLS, lineage/delegation. Responses travel relay-mediated unicast; transport is HTTP long-poll, not SSE. - Economics is out of protocol scope (I4: aggregates advise, contracts govern): no receipt, citation, pricing, or settlement fields or message types exist or may be added. - Relay verifies signatures and ±300s timestamp skew, carries only `query` broadcasts, holds no history (queue drained on poll), and returns 429 + Retry-After under backpressure — never silent drops. Mailbox polls require proof of key possession: `GET /v1/challenge` then a signed single-use nonce, so knowing a pubkey is not enough to drain its queue. @@ -45,4 +46,4 @@ - frxd modes (one binary, config toggles, no code required of publishers): querier (broadcast/local-first search), responder (match incoming queries against shared collections, sign), local index (watch dirs, extract text, explicit shared marking per I9). Use RFC terms querier/responder, not "subscriber/publisher". - Roles are not exclusive: a single node may issue queries and answer them concurrently (I5, §3 "any member"). Implement querier/responder as independent enable flags — never an exclusive mode enum or fixed deployment role. - Matching accuracy is a project-health concern: start lexical (Tantivy), plan a hybrid cheap lexical gate + optional local embedding rerank (two-stage ingestion, Appendix A); embedding model stays local and replaceable (I2/I5). -- Identity/registry (RFC Draft 0.5 §4/§6): MA-hosted FQDN identifiers first (`