From 50ebb4f2e620dad8e1fbca8307b97c5e10e7e6c0 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Tue, 18 Aug 2026 02:14:39 +0200 Subject: docs: settle the desktop client, and draft v6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A design discussion on 2026-08-17 settled Phase 13 and, in doing so, changed four things the spec states. v6 restates only those; v5 stays authoritative for everything it does not touch, per the convention v5 itself used with v4. What changed: * The native shell is **Electron**, not pywebview — structural decision 18 reversed. The SPA depends on Chromium-class APIs (WebRTC, WebCrypto X25519/Ed25519, MSE, Service Workers), so keeping Chromium keeps transport.js, crypto.js, keyderive.js, downloads.js and sw.js *as the client*. A system webview meant reimplementing ~2500-3000 lines. The old "69 % reused" figure was measured against an app.js of ~2600 lines; it is 4586. * A group's content is **several named roots**, not one directory, because the planned video and audio libraries will not live in one folder on one disk. * **Device linking**: one person may hold several devices on a node, admitted by a key the node already pinned and bound by a one-time code the new device generates. Without it a native client is refused where a browser is not, and an account created natively could never be opened in a browser. * **Authorship is authenticated, not asserted** — chat senders sign, uploads have a provable owner, and delete authorization moves from the uploading key to the account. And one rule v5 assumed without writing down: **group-related server state lives on the node.** Verified for multi-root — SwarmSource carries hashes and endpoints, no paths. Also here: the Caddy configuration, which was a snippet in the roadmap that would have broken the SPA (it predates /a// asset versioning and would have 404ed /sw.js, silently killing streamed downloads on Firefox and Safari); and downloads.html, which becomes a security page once a release key exists. Phase 15 was re-read against device linking and is wrong as written: a sender key must be per **device**, never per person, or two devices sharing a chain produce key and nonce reuse — C1 again, one level down. senderkeys.py already fails this silently. Co-Authored-By: Claude Opus 5 --- docs/meshbay-draft-v5.md | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) (limited to 'docs/meshbay-draft-v5.md') diff --git a/docs/meshbay-draft-v5.md b/docs/meshbay-draft-v5.md index b03328b..e4218ee 100644 --- a/docs/meshbay-draft-v5.md +++ b/docs/meshbay-draft-v5.md @@ -1,8 +1,15 @@ # MeshBay — Architecture Draft v5 +> **SUPERSEDED 2026-08-17 by `docs/meshbay-draft-v6.md`.** v6 restates only what +> changed; everything below that v6 does not touch is still authoritative — the +> handshake (§4), node authority (§5), the hub's role (§6), cryptography (§7) and +> the testing posture (§10). Read v6 first, then this. +> > Status: **Phase 11.5 (security remediation) complete. Invite redesign complete > (2026-08-14) — H3 and M3 closed.** See `devel-phases-next.md` and, for the invite -> and pairing design, `docs/invite-pairing-v1.md`. +> and pairing design, `docs/invite-pairing-v1.md`. For the desktop client — shell, +> device linking, account creation, node management — `docs/desktop-client-v1.md` +> (2026-08-17) is authoritative and supersedes §8.2 here. > Supersedes `meshbay-draft-v4.md`. Sections not restated here are unchanged from v4. > > v5 exists because the second security review (`second-review.md`, 2026-08-13) found @@ -220,7 +227,7 @@ was 32 opaque random bytes signed blind — an unbound signing oracle. |---|---| | `file_delete` | Node operator, or the user who uploaded the file (verified by the key recorded at upload) | | `invite_create` | **Node operator only** (delegation designed, deferred) | -| GEK activation | **Local admin UI or CLI only** — no MNP message can activate a GEK | +| GEK activation | **Local admin UI or CLI only** — no MNP message can activate a GEK. The rule targets *key material arriving from outside* (C5b), not the instruction: a future operator-signed `gek_rotate` where **the node generates the key itself** is a different shape and is allowed (`docs/desktop-client-v1.md` §6.2). The initial `gek-init` stays local — with no GEK there is no completed session to carry a signed op | | ~~`gek_bundle_store`~~ | **Message deleted.** No member ever hands the node key material | `gek_bundle_store` previously accepted any member's bundle and auto-activated one @@ -513,17 +520,40 @@ end-to-end *integrity* for that path. ### 8.2 Native client -pywebview + aiortc, sharing ~69% of the existing UI unchanged (the SPA contains no direct -`crypto.subtle` or `RTCPeerConnection` usage; everything goes through three injected -globals and one fetch helper). Assets ship **inside the package** and load from disk — a -shell pointing its WebView at the hub's `/app/` would be a browser with a different icon -and would fix nothing. +**Electron** (decided 2026-08-17 — see `docs/desktop-client-v1.md`, which is authoritative +for the desktop client and supersedes this section's earlier pywebview form), with an +optional Python sidecar reusing `quic_client.py` for hub-less `group://`. + +Assets ship **inside the package** and load from disk — a shell pointing its WebView at the +hub's `/app/` would be a browser with a different icon and would fix nothing. That +constraint is unchanged and is the whole point. + +What changed is the engine. The SPA does not depend on "the web" in general but on +Chromium-class APIs: `RTCPeerConnection`, WebCrypto X25519/Ed25519, MSE, Service Workers, +File System Access. Keeping Chromium keeps `transport.js`, `crypto.js`, `keyderive.js`, +`downloads.js`, `sw.js` and the MSE player **unchanged**; replacing it with a system webview +meant reimplementing ~2500–3000 lines plus a loopback media server and native dialogs. The +"~69 % reused" figure quoted here previously was measured in August against an `app.js` of +~2600 lines; it is 4586 lines as of 2026-08-17, and `app.js` now contains 2 direct +`crypto.subtle` calls (`_pkFromSk`), so the three-globals seam had already leaked. Native does **not** remove trust in the hub operator; it converts an undetectable, per-request, per-user attack into a persistent artifact that can be hashed and compared. That value is realised by reproducible builds and published hashes, not by the packaging format. +Two properties of the desktop client bear on this document directly: + +- **Identity keys are generated and kept locally**, never bundled anywhere. C4 closes for a + native device unconditionally — but **not for an account that also uses a browser**, which + still needs a bundle on each node (§7.1). An account is only as strong as its weakest + client, and the docs must say so. +- **A second device is admitted by device linking**, not by an operator code: the + already-pinned key countersigns, bound by a one-time code the new device generates. The + hub cannot produce that countersignature — it has stored no user keys since 2026-08-14 + (§6.1) — so this adds no hub-reachable authority. `identities` gains a device dimension; + see `docs/desktop-client-v1.md` §4 before touching the roster. + --- ## 9. Open items @@ -546,7 +576,7 @@ format. | # | Item | Status | |---|---|---| -| C4 | Remote keypair bundles | Bounded (4 pre-proof fetches, audited); closes in Phase 13.3 when the native client stops storing them remotely | +| C4 | Remote keypair bundles | Bounded (4 pre-proof fetches, audited). **Closes for a native device unconditionally** (its keys are never bundled), but **stays open for any account that also uses a browser** — a browser has no durable storage of its own and needs a bundle on each node. Full closure needs the opt-out in `docs/desktop-client-v1.md` §5.1, signed by a pinned key so the hub cannot set it | | — | Chat encryption (Sender Keys) | Phase 15. Distribution must be **pairwise to identity keys**, never GEK-derived | | — | Chat attachments | Plaintext on the node; decide in Phase 15.7 | | — | Delegation | Designed, deferred: a group admin who does not run the node still cannot invite | -- cgit v1.2.3