From 8d74b63b11f216a98b1d44f1d59d882d82fe60f6 Mon Sep 17 00:00:00 2001
From: George Coles
Date: Tue, 15 Sep 2026 11:46:11 -0400
Subject: [PATCH] Registration page: full organization application, private
MA-side storage
---
AGENTS.md | 4 +-
DEPLOY.md | 2 +-
src/commands.rs | 172 +++++++++++++++++++++++++++++++++++++++-----
src/main.rs | 2 +
src/registry.rs | 45 ++++++++++++
tests/onboarding.rs | 74 ++++++++++++++++++-
6 files changed, 276 insertions(+), 23 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index 5a295f2..8c206de 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -32,7 +32,7 @@
- Relay backpressure is global: any member's full queue 429s every publisher until drained (visible per §3, but one lagging member can stall the firehose — revisit before scale).
- Member authority (Draft 0.5 §6): the MA-signed registry snapshot is authoritative when configured (`[node] registry` = file path or URL, `ma_key` pinned; monotonic version — rollback and forgery close the node; file path is mtime-reloaded, URL is fetched at start + every 60s and cached to `/registry-cache.json`, so outage fails static). Keys carry optional validity windows (`not_before`/`not_after`); rotation = `registry add-key` then `revoke-key`.
- `/members.toml` (name, pubkey, class, `previous` keys, mtime-reloaded) is a dev/local fallback used only when no registry is configured; empty directory without a registry is open bootstrap only when `dev_bootstrap = true` (RFC §6: explicit dev flag). Receivers drop content-bearing responses from enrichment-class senders (metadata-only, §6).
-- MA tooling: `frxd registry init|add|add-key|revoke-key|remove|list|set-relays|show|serve` (signed `registry.json` + `ma-key.hex` in `--dir`); `frxd init --id/--registry/--ma-key`; `frxd key show|rotate`; `member add --previous ` for the fallback path. A node with no `[node] relays` discovers them from the registry snapshot (`doc.relays`).
+- MA tooling: `frxd registry init|add|add-key|revoke-key|remove|list|applications|set-relays|show|serve` (signed `registry.json` + `ma-key.hex` in `--dir`); `frxd init --id/--registry/--ma-key`; `frxd key show|rotate`; `member add --previous ` for the fallback path. A node with no `[node] relays` discovers them from the registry snapshot (`doc.relays`).
- Aggregate semantics are our implementation choices from a terse spec: requests are `aggregate` envelopes carrying only `period`; replies carry `sent` (broadcasts that month) / `passed` (responses consumed from that member); granularity floor is enforced as YYYY or YYYY-MM only (finer rejected), yearly rolls up months. Revisit with §10 sufficiency review.
## Known gaps (Phase 2/3, intentional — don't fake them)
@@ -50,6 +50,6 @@
- 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 floor: boundary tokenizer (`src/tokenizer.rs` — letter/digit splits so `5555` matches `DLEX5555`, lowercase, ASCII fold, English stopwords+stemmer) → coverage gate (`[match] min_coverage`, default 0.4; 1–2 term queries require all terms) → title boost 2.0 + phrase boost 3.0 + query-time snippets. Schema changes require a fresh index dir (`open_or_create` errors on mismatch).
- Engine seam: `src/engine.rs` `SearchEngine` trait (`search` → `EngineOutput { hits, total: Option }`, `doc_count`); `respond()` in `src/node.rs` is the conformance wrapper (budget clamp, truncation from engine total — unknown total forces `truncated = true`). Power users can implement the trait (HTTP adapter or subprocess to an external engine).
-- Onboarding: `frxd --onboarding` runs a wizard consuming a credential block (`id=.. token=.. registry=.. ma_key=..`) issued by the MA's signup endpoint (`registry serve --signup-code --registry-url`; HTML page at `/`, `POST /v1/signup` → one-time invite token, `POST /v1/enroll` binds keys and re-signs). Identity registration stays MA-side; the wizard never creates identities, only binds locally generated keys. Invites live in `/invites.json`. Prompts accept empty input as the default; scripted stdin works for tests.
+- Onboarding: `frxd --onboarding` runs a wizard consuming a credential block (`id=.. token=.. registry=.. ma_key=..`) issued by the MA's signup endpoint (`registry serve --signup-code --registry-url`; HTML page at `/`, `POST /v1/signup` → one-time invite token, `POST /v1/enroll` binds keys and re-signs). Identity registration stays MA-side; the wizard never creates identities, only binds locally generated keys. Invites live in `/invites.json`; the form's organization/representative/contact/payment fields are stored privately in `/applications.json` (mode 600, MA contract data — never in the signed snapshot), and the two acknowledgement checkboxes are required by the endpoint. Prompts accept empty input as the default; scripted stdin works for tests.
- Next matching steps: eval harness with a small golden set (precision@k + false-silence rate), then a dense recall leg (model2vec-rs 0.2.1 exists but needs `default-features = false, features = ["fancy-regex", "local-only"]` for musl/airgapped; verify crate + model licenses before bundling), then an optional cross-encoder reranker. Embeddings are for recall; reranking is the precision tier.
- Identity/registry (RFC Draft 0.5 §4/§6): MA-hosted FQDN identifiers first (`
Everything else — matching, ranking, retention, trust — is local.
-
1. Register
+
1. Register with the membership authority
+
Registering here creates your identifier with the membership authority (MA); the
+onboarding wizard in step 4 then binds your node's keys to it. The public registry publishes only
+your identifier, class, keys, and the federation's relays. The organization details below are kept
+privately by the MA for the membership contract — they are never published and never travel on the
+wire.
-
Your identifier is <label>.frx.federatedsearch.org — no domain or DNS of
-your own is needed. Registration returns a one-time credential block:
-id=... token=... registry=... ma_key=....
2. Download
Static Linux x86_64 binaries (musl — no runtime dependencies):