summaryrefslogtreecommitdiffstats
path: root/tmp-decisions.md
blob: 76d33d2fbb507dda69f5b6ea41fc36c1b5eafefb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# Client architecture — decisions

> 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.

---

## Status

| # | Decision | State |
|---|---|---|
| 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.** Shell revised 2026-08-17: **Electron**, see `docs/desktop-client-v1.md` |
| D3 | Transport: aiortc primary, QUIC at parity, TCP+HTTP removed | ✅ Decided 2026-08-13. Unchanged for the **node**; the desktop client uses Chromium's WebRTC rather than aiortc, and QUIC via a Python sidecar |
| D4 | Hub minimization (old Phase 12) | ⏸️ **Deferred, may be dropped** |

> **2026-08-17 — the browser-extension options are no longer live.** 12.2 (signed digest
> of the served bundle) is what Option A needed, and it remains scheduled; nothing else
> from A or B is planned. The desktop client is the answer to T3 for users who want one.

**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.

## 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
`second-review.md` §9.

If the hub operator is the adversary, a native client downloaded from `meshbay.org` and
signed with a key that operator holds does not remove the trust — it relocates it from "the
JS they serve" to "the binary they serve." What actually changes is **detectability**:

- **Browser:** an attack is one HTTP response, targeted at one user, leaving no artifact.
  Undetectable in principle.
- **Native:** an attack requires shipping a build. That build is an artifact — hashable,
  archivable, comparable between users, reversible. Targeting one user means giving them a
  different binary, which reproducible builds and published hashes make detectable.

That is a real improvement, but **the value lives in the verification machinery
(reproducible builds, published hashes, independent rebuilds — Phase 18.7), not in the
packaging format.** Without it, a native client from meshbay.org is only marginally more
trustworthy than the SPA from meshbay.org.

Native also has real costs that were under-weighted: loss of the browser sandbox (a Python
process with full user privileges vs a seccomp-confined renderer), ownership of patch
velocity for WebKitGTK and every bundled dependency, and new attack surface (loopback media
server, IPC bridge, update client).

**Conclusion recorded:** the native client is justified on *product* grounds — durable keys,
no browser tab, background connectivity, better video, hub-less `group://` access over QUIC.
It should not be justified as the fix for T3 unless 18.7 ships with it.

---

## 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 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
  extension, a native client, a curious user — can verify it
- `/app/` carries an explicit, visible "reduced trust: this hub serves this code" notice
- the docs never claim end-to-end integrity for the hub-served SPA path

The honest framing: hub-served SPA is a **convenience tier**, not the secure tier.

---

## D2 rationale — native chosen; extension not taken up

Three shapes, cheapest first:

**Option A — Extension as a verifier (hub still serves the UI)**
The extension does not ship the UI. It hashes the bundle the hub served and compares it
against a digest signed by the project. Mismatch → visible alarm, optionally block.
Converts a silent targeted injection into a loud one. Small effort, keeps today's
architecture, compatible with D1 = yes.

**Option B — Extension ships the UI (hub serves the API only)**
The UI lives in the extension, distributed and signed by Mozilla/Chrome — a channel **the
hub operator does not control**. Manifest V3 forbids remote code, which works in our favour:
the structure enforces exactly what we want. Keys live in extension storage, isolated from
page JS. Moderate effort.

**Option C — Native desktop client** ← **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.

> **Shell revised 2026-08-17: Electron, not pywebview** (+ an optional Python sidecar for
> `group://` over QUIC). See `docs/desktop-client-v1.md` §2. The comparison table below
> was written against pywebview and **two of its rows are wrong for the chosen shell**:
>
> - *Browser sandbox* — Electron with `sandbox` and `contextIsolation` **keeps** the
>   Chromium renderer sandbox. The row reads "❌ None (partial under Flatpak)"; that was
>   WebKitGTK's property, not native's.
> - *Crypto available* — the client keeps WebCrypto **and** gains local Argon2id/ChaCha20
>   in the main process; the row implied WebCrypto is lost with the browser engine.
>
> The row that gets *worse*: **patch velocity**. "You own it" is more true of Chromium
> than of a distro-maintained WebKitGTK, and the renderer parses attacker-controlled
> content from nodes. That is the real cost and it is accepted deliberately.

### Comparison

| | Hub-served SPA (today) | A: extension verifies | B: extension ships UI | C: native desktop |
|---|---|---|---|---|
| Code distribution channel | Hub (the adversary) | Hub, but **verified** | Store (independent) | Hub download + own signing key |
| Silent targeted injection | Undetectable | **Detected** | Not possible | Detectable *if* 18.7 |
| Browser sandbox | ✅ Full | ✅ Full | ✅ Full | ❌ None (partial under Flatpak) |
| Patch velocity | Browser auto-updates | Browser auto-updates | Browser + store review latency | **You own it** (WebKitGTK, Python deps) |
| Key storage | IndexedDB, page-reachable | unchanged | Extension storage, page-isolated | **OS keystore** |
| Crypto available | WebCrypto only (no ChaCha20/Argon2id) | unchanged | unchanged | **Full** (ChaCha20, Argon2id 256 MB) |
| Transport | WebRTC | WebRTC | WebRTC | WebRTC **+ QUIC** |
| Large file → disk | Chrome only (FS Access API) | unchanged | unchanged | **Native, unlimited** |
| Hub-less `group://` | ❌ | ❌ | ❌ | ✅ |
| New attack surface | — | negligible | negligible | loopback server, IPC, updater |
| Platforms to maintain | 0 | 2 stores | 2 stores | 3 OSes |
| Effort | 0 | Low | Moderate | High |

### Observations for the decision

- **A and B are not exclusive with C.** A/B protect browser users; C serves users who want a
  real application. "Both" is coherent — just sequence them.
- **B gives most of C's security benefit at a fraction of the cost**, because the win was
  never the packaging format — it was getting the code off the adversary's distribution
  channel — and the extension keeps the browser sandbox while doing it.
- **Store review latency is the one place B is worse than C**: a critical fix waits on
  Mozilla/Google. Mitigate with a version-pinned kill switch.
- **For node operators specifically, Phase 14 (CLI) beats all three.** The operator is the
  highest-value target — holds the GEK, is the content authority — and today must use
  hub-served JS to initialize GEKs and invite members. The CLI removes that dependency at a
  fraction of any client's cost. If only one thing gets built for T3, it should be the CLI.
- If **D1 = yes** (hub keeps serving the UI), Option A is the natural companion and is nearly
  free once 12.6 publishes the signed digest.

### Not yet investigated

- Whether AMO/Chrome Web Store policy accepts an extension whose purpose is P2P file sharing
- Whether MV3 service-worker lifetimes can hold a long-lived WebRTC DataChannel (may need an
  offscreen document — worth a spike before committing to B)
- Safari/iOS: no extension route comparable to AMO; likely out of scope either way

---

## Impact on the roadmap

| Decision | If yes | If no |
|---|---|---|
| D1 hub serves UI | Phase 12.6 as written (CSP, SRI, signed digest, reduced-trust notice) | 12.6 shrinks to removing `/app/`; hub becomes API-only |
| D2 = A | Small new phase; 12.6 is a prerequisite | — |
| D2 = B | New phase, ~Phase 13-sized; 13.1 platform split is reused directly | — |
| D2 = C | Phase 13 as written (13.2–13.11), + 18.7 for the security claim to hold | Phase 13 reduces to 13.1 only |

**13.1 (platform adapter split) is worth doing regardless of D2.** It is pure refactoring
whose acceptance criterion is "the browser SPA behaves identically," and it is the
prerequisite for A, B and C alike.