summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--devel-phases-next.md69
-rw-r--r--tmp-decisions.md48
2 files changed, 69 insertions, 48 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
diff --git a/tmp-decisions.md b/tmp-decisions.md
index 97a27ea..347d771 100644
--- a/tmp-decisions.md
+++ b/tmp-decisions.md
@@ -1,7 +1,8 @@
-# Open decisions — client architecture
+# Client architecture — decisions
-> Working note, not a spec. Created 2026-08-13 after the second security review.
-> Delete or fold into `docs/meshbay-draft-v5.md` once decided.
+> Created 2026-08-13 after the second security review. D1/D2/D3 decided the same day;
+> D4 (hub minimization) deferred. Fold into `docs/meshbay-draft-v5.md`.
+> The analysis below is kept as the rationale behind the decisions, not as open questions.
---
@@ -9,18 +10,35 @@
| # | Decision | State |
|---|---|---|
-| D1 | Does the hub keep serving the web UI? | **Open** — leaning yes |
-| D2 | Browser extension, native desktop client, or both? | **Open** — needs time |
+| D1 | Does the hub keep serving the web UI? | ✅ **DECIDED 2026-08-13 — yes** |
+| D2 | Browser extension, native desktop client, or both? | ✅ **DECIDED 2026-08-13 — native client, offered alongside the hub-served SPA** |
| D3 | Transport: aiortc primary, QUIC at parity, TCP+HTTP removed | ✅ Decided 2026-08-13 |
+| D4 | Hub minimization (old Phase 12) | ⏸️ **Deferred, may be dropped** |
-**Neither D1 nor D2 blocks anything right now.** Phase 11.5 (security remediation),
-Phase 12 (hub minimization), Phase 14 (node CLI) and Phase 15 (Sender Keys) are entirely
-client-agnostic — every finding they close is node-side or hub-side. Phase 11.5 is in
-progress on that basis.
+**What was decided.** The hub keeps serving the web UI — that is the zero-install path
+and it stays. A native desktop client is offered *in addition*, not as a replacement.
+Hub minimization is off the critical path and may be dropped entirely.
----
+**What that means, stated once and then respected.** Keeping the hub in the trusted path
+is a legitimate product call, and this project is not obliged to defend against its own
+operator. But two consequences should be carried deliberately rather than by accident:
+
+1. **T3 is accepted permanently for browser users.** A hub that serves the code can
+ exfiltrate keys from the page regardless of what the protocol does. The native client
+ gives users who care an alternative; browser users are trusting meshbay.org, and the
+ docs should say so plainly rather than claiming end-to-end integrity.
+2. **H3 was the last open High finding and its only fix lived in the dropped phase.**
+ The hub is the public key directory: substituting a key during an invite hands it the
+ group key, silently, with no forgery and no code injection. So key transparency and
+ safety numbers were kept and are now Phase 12.1 — everything else from hub
+ minimization is dropped. If Phase 12 is later dropped too, H3 stays open by choice,
+ and "unreadable by other parties, even the hub" stops being a claim the project can
+ make about an adversarial hub.
+
+The honest framing that survives all of this: **the hub cannot read your content unless
+it actively attacks you.** That is still a strong property, and it is defensible.
-## Why these are open
+## Rationale — why the native client is not a T3 fix
The second review recommended a native client and claimed *"T3 disappears — code integrity
stops depending on the hub."* **That claim was wrong and has been corrected** in
@@ -52,12 +70,12 @@ It should not be justified as the fix for T3 unless 18.7 ships with it.
---
-## D1 — Should the hub keep serving the UI?
+## D1 rationale — hub keeps serving the UI ✅
Keeping it is defensible. It is how anyone tries the platform without installing anything,
and it stays the fallback when a device has no client installed.
-What must be true if it stays (all already scheduled in Phase 12.6):
+What must be true now that it stays (Phase 12.2/12.3):
- strict CSP and Subresource Integrity on the bundle
- the hub publishes a **signed digest** of the served bundle, so any third party — an
@@ -69,7 +87,7 @@ The honest framing: hub-served SPA is a **convenience tier**, not the secure tie
---
-## D2 — Extension vs native: what each actually covers
+## D2 rationale — native chosen; extension not taken up
Three shapes, cheapest first:
@@ -85,7 +103,7 @@ hub operator does not control**. Manifest V3 forbids remote code, which works in
the structure enforces exactly what we want. Keys live in extension storage, isolated from
page JS. Moderate effort.
-**Option C — Native desktop client (pywebview + aiortc)**
+**Option C — Native desktop client (pywebview + aiortc)** ← **CHOSEN**
Phase 13. Full control, durable keys in an OS keystore, QUIC, hub-less access, best UX.
Highest effort, and the security argument depends on 18.7.