summaryrefslogtreecommitdiffstats
path: root/devel-phases-next.md
diff options
context:
space:
mode:
Diffstat (limited to 'devel-phases-next.md')
-rw-r--r--devel-phases-next.md69
1 files changed, 36 insertions, 33 deletions
diff --git a/devel-phases-next.md b/devel-phases-next.md
index 04d7b9b..6dbf1dd 100644
--- a/devel-phases-next.md
+++ b/devel-phases-next.md
@@ -11,7 +11,8 @@
> and an active hub can obtain any group key through the key directory it controls (H3).
>
> **Phases renumbered 2026-08-13** (old → new): 12→14, 13→15, 14→16, 15→17, 16→18, 17→19.
-> New: 11.5 (security remediation), 12 (hub minimization), 13 (native desktop client).
+> New: 11.5 (security remediation), 12 (client key verification — reworked 2026-08-13,
+> hub minimization deferred by operator decision), 13 (native desktop client).
---
@@ -693,44 +694,47 @@ cannot delete or overwrite another member's file, and cannot change the group ke
---
-## Phase 12 — Hub minimization: registrar and nothing more
+## Phase 12 — Client key verification + served-SPA integrity
-**Objective:** reduce the hub to its legitimate role and make that reduction *structural*
-rather than a matter of good behaviour. The hub must not be able to see private keys,
-unencrypted content, or file listings — not "does not currently", but "cannot".
+> **Reworked 2026-08-13 by operator decision.** This phase was "Hub minimization:
+> registrar and nothing more". That work is **deferred and may be dropped** — see
+> decisions D1/D2 in `tmp-decisions.md`. The hub will keep serving the web UI, and a
+> native client will be offered *in addition to* it, not as a replacement.
+>
+> Two items are kept here because the decision makes them *more* relevant, not less:
+> the hub stays in the trusted path, so what it can substitute and what code it serves
+> both still matter. Everything else from the old Phase 12 (route blindness test,
+> opaque private-group metadata, chat_notify minimization, schema cleanup) is dropped
+> from the plan; the swarm item already shipped in 11.5.18.
-### What the hub is allowed to know
+**Objective:** make the hub's two remaining powers over confidentiality *detectable*,
+given that it stays in the trusted path by choice.
-| Category | Allowed | Notes |
-|---|---|---|
-| Account: username, encrypted email, public keys, status, role | ✅ | Required to be a registrar |
-| Group registry: id, admin, visibility, join policy, membership | ✅ | Required to issue the `groups` claim |
-| Public group name + description | ✅ | Required for discovery |
-| IP logs | ✅ | Legal retention, 1 year |
-| Signaling relay (SDP/ICE, in-memory, seconds) | ✅ | Never persisted |
-| **Private keys, keypair bundles, GEK bundles** | ❌ | Removed in Phase 12 (old); 13.3 removes the last copies |
-| **File content, file names, file hashes, index** | ❌ | H7 was leaking hashes; 11.5.18 closes it |
-| **Message content or per-message metadata** | ❌ | `chat_notify` currently leaks it — 12.3 |
-| **Private group name / description** | ❌ (target) | 12.5 |
+### Why these two survive
+
+**H3 is the last open High finding, and nothing else fixes it.** The hub is the public
+key directory: when a member invites someone, the inviter fetches the invitee's
+`pk_x25519` from the hub and wraps the GEK for it. A hub that returns its own key gets
+the group key, decrypts everything, and nothing in the protocol notices. This needs no
+JWT forgery and no code injection. Deferring Phase 12 wholesale would leave it open
+indefinitely, so it moves here rather than disappearing.
+
+**Serving the SPA is now a deliberate choice, not a residual risk.** A hub that ships
+the code can exfiltrate keys from the page whatever the protocol does (T3). That is
+accepted — but it should be labelled honestly and made verifiable where possible.
### Milestones
| # | Component | Description |
|---|---|---|
-| 12.1 | Route inventory + blindness test | Enumerate every hub route; assert no response body can contain key material, content, a file name or a content hash. Runs in CI, fails the build on regression |
-| 12.2 | **Key transparency + safety numbers** [H3] | Append-only, hub-signed key log; clients pin the key they first saw and audit the log; key change raises a blocking warning; safety-number comparison UI between two members. This is the fix for the last structural way a hub can read content |
-| 12.3 | Chat metadata minimization | `chat_notify` (`webrtc_server.py:634-644` → `revocation.py:101-128`) currently tells the hub *who* posted in *which* group and *when*. Drop `sender_name`, make notification opt-in per group, coalesce and delay to blunt timing correlation |
-| 12.4 | Swarm hardening | Enforce 11.5.18 at the API layer too: reject registration for a group the hub knows is private; authenticate `GET /v1/swarm/{hash}` |
-| 12.5 | Opaque private-group metadata | For `visibility == "private"`, store name/description as a member-encrypted blob; the hub holds an opaque value and an id. Public groups unchanged (discovery needs plaintext) |
-| 12.6 | SPA integrity + honest labelling | Strict CSP, SRI on the bundle, hub publishes a signed digest of the served bundle that native clients and extensions can verify; `/app/` carries an explicit "reduced trust — this hub serves this code" notice |
-| 12.7 | Remove dead crypto plumbing | Drop residual columns/migrations/constants from the pre-Phase-12 GEK era so the schema cannot be quietly repopulated |
-| 12.8 | Written threat model | One page: passive hub, active hub, malicious node operator, malicious member, network attacker, local attacker — and for each claim, which adversary it holds against. Referenced from draft-v5 |
+| 12.1 | **Key transparency + safety numbers** [H3] | Hub-signed append-only key log; clients pin the key they first saw for a contact and audit the log; a key change raises a blocking warning before any GEK is wrapped for it; safety-number comparison UI between two members. Applies to the SPA and the native client alike |
+| 12.2 | Served-SPA integrity | Strict CSP, Subresource Integrity on the bundle, and a signed digest of the served bundle published by the hub so a native client or extension can verify what the browser was given |
+| 12.3 | Honest labelling | `/app/` states plainly that the hub serves this code and what that implies. Docs stop claiming end-to-end integrity for the hub-served path — the claim that holds is "the hub cannot read your content unless it actively attacks you" |
+| 12.4 | Written threat model | One page: passive hub, active hub, malicious node operator, malicious member, network attacker, local attacker — and for each claim, which adversary it holds against. This is what stops the overclaiming pattern the second review kept finding |
-**Acceptance criteria:** a hub operator holding root on the server, the full PostgreSQL
-database, the Ed25519 signing key, and the ability to forge any JWT can obtain: no private
-key, no GEK, no file content, no file name, no content hash, no message content, and no
-private group name. Every remaining capability is on the list above and is documented in
-12.8. Any attempt to substitute a public key is detectable by clients via 12.2.
+**Dropped from the old Phase 12** (recorded so the intent is not lost if it returns):
+route-inventory blindness test, opaque private-group name/description, chat_notify
+metadata minimization, residual schema cleanup.
---
@@ -1002,11 +1006,10 @@ community developers. Core functionality must be complete and stable first.
```
Phase 11.5 (Security remediation) ⛔ BLOCKING — nothing else starts
Phase 13.1 (Platform adapter split) ← free refactor, unblocks every D2 option
-Phase 12 (Hub minimization) ← makes "the hub cannot read" structural
+Phase 12 (Key verification) ← H3 safety numbers + served-SPA integrity
Phase 14 (Node CLI) ← best security-per-effort answer to T3
Phase 15 (Sender Keys) ← chat encryption; 15.0 decision first
-── decision point D2: extension / native / both ──
-Phase 13.2–13.11 (Desktop client) ← product-driven; needs 18.7 for the security claim
+Phase 13.2–13.11 (Desktop client) ← DECIDED: offered alongside the browser SPA
Phase 16 (Android) ← reuses the Phase 13 design
Phase 17 (Resilience) ← optional, edge cases only
Phase 18 (Packaging + CI) ← distro repos; 18.7 gates 13's security argument