aboutsummaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_DESIGN.md
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-03 09:22:45 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-03 09:22:45 +0200
commit71af64ec3e393219895e47b35582d4310ffc6cd3 (patch)
tree0a9a258b7e3f11110c138f2c63af9c179808118a /docs/MESHBAY_DESIGN.md
parent178c4fa21b9c0591132f23246af80795b2d081df (diff)
downloadmeshbay-71af64ec3e393219895e47b35582d4310ffc6cd3.tar.gz
docs(design): rewrite §2 trust model human-first; formal model to §13.9
- §2 now describes who you trust in plain terms: no adversary grid, no red crosses - adversary table, claim matrix and refused over-claims move to §13.9 for auditors - repoint cross-references and the concordance to match Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
-rw-r--r--docs/MESHBAY_DESIGN.md256
1 files changed, 117 insertions, 139 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index 3672da2..3ce0a67 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -134,155 +134,67 @@ paths, no filenames**.
---
-## 2. Trust model
+## 2. Who you trust
-### 2.1 Adversaries
+MeshBay lets you share things with the people you choose, and keeps everyone else
+out. In plain terms, here is who can see your group — and the one caveat worth
+knowing.
-Every claim below is written against one of these, and they are the only ones the
-document uses:
+### 2.1 Inside your group
-| Adversary | What they can do |
-|---|---|
-| **Passive hub** | Read everything the hub legitimately stores and relays |
-| **Active hub** | Also lie: forge tokens, invent accounts, substitute values it publishes, ship modified client code to a browser |
-| **Malicious node operator** | Read and alter everything on their own machine, including the plaintext files they host |
-| **Malicious group member** | Everything a member may do, plus anything the protocol fails to refuse |
-| **Network attacker** | Observe and tamper with traffic between any two parties |
-| **Local attacker** | Reach loopback services and files on a client or node machine |
-| **Registered hub user with no membership** | Reach every hub endpoint that does not check membership |
-| **Federated peer hub** | Push directory rows and revocations over MHP |
-
-### 2.2 Security claims — and how to read them
+A group lives on a node, run by its operator. Everyone in the group — the operator
+and the members — shares its files, its index and its chat. That is what a group
+*is*: the people you decided to let in, the same as a shared folder or a team
+workspace. They are the people you trust, and they are the only ones who can read
+what you put there.
-Every collaboration product carries the structural limits this section lists.
-Most never write them down. What follows states ours plainly and separates three
-things a single ❌ usually hides — a **definition**, a **guarantee** and an
-**accepted risk** — and says, where it matters, how a comparable product fares on
-the same point. Read it with one rule: **where you see a limit here, assume the
-mainstream closed product has it too and does not tell you.**
+Two things follow from that by design — they are the shape of a shared space, not
+gaps in it:
-The verdict first. Against anyone *outside* your group — the hub, the network,
-another group's operator — your content is unreadable. *Inside* your group, your
-operator and fellow members read it, because that is what a group is. The one
-place the protocol cannot protect a browser is **T3** (below); the native client
-turns that from an invisible attack into a publicly verifiable one.
+- Your operator hosts your files, so they can change what they host. A node's
+ content is the operator's to keep and to serve (§7).
+- Leaving a group removes you on the hub, but copies already on a node stay with
+ its operator (§7.7) — as with anything you have handed to someone in person.
-#### What a group inherently means
+### 2.2 Everyone else sees nothing
-These are not defeats of the design; they are what *a shared, hosted group* is.
-Each would read as ❌ in any honest table, for any product in this class:
+Outside your group nobody can read what you share — not the hub that connects you,
+not anyone watching the network, not the operator of any *other* group:
-- Your **operator and fellow members read the group's content** — files, index
- and chat. You chose them when you joined; they are your trust boundary, not the
- hub's.
-- Your **operator hosts the files, so they can alter what they host.** Authority
- over a node's content is the operator's by design (§2.4, §7).
-- **Leaving clears you hub-side; copies on a node you were hosted on stay** with
- that operator (§7.7).
-
-On the one point every product in this class shares — who can read your content —
-stated the way an honest table would:
-
-| Who can read your content | MeshBay | A mainstream E2EE messenger | A mainstream cloud suite |
-|---|---|---|---|
-| The people in your group | Yes — operator + members | Yes — every member | Yes — members + workspace admin |
-| The server / host itself | No (native) · T3 (browser) | No — but a web build has the same client-code exposure, unstated | Yes, routinely |
-| Is this limit written down for you? | Here, explicitly | Rarely | Almost never |
+- Your content never passes through the hub in the clear. The hub introduces nodes
+ to one another and relays sealed traffic; it does not hold what you share (§5).
+- The hub holds no key of yours. It cannot read your group, add a device to your
+ account, or act as you — and it cannot hand your group's key to anyone, save
+ where you ask it to (an open-join group, or an invitation you asked it to mail;
+ §7.3, §3.4).
+- Each node carries its own identity keys, so a key that somehow leaked would open
+ that one node and no other (§3), and a copy of a node's storage without its
+ unlock key reveals none of its chat (§4.5).
-#### What MeshBay guarantees
+### 2.3 Your operator hosts you, but cannot become you
-Stated as guarantees, not as the absence of failure, against the adversaries in
-§2.1:
+Keeping your content is the deal you made with your operator. Turning that into
+*being* you is the line they cannot cross. The identity bundle stored on their
+node opens only with your passphrase and a secret the hub releases to no one but a
+session that has already proved the passphrase or a device key; the only attempts
+left to an operator are ordinary sign-ins, which the hub counts and locks out. And
+reading what *they* host never reaches what *other* operators host (§3.2).
-- Your **content never transits the hub** — the hub relays, it does not hold your
- plaintext (§5).
-- **No party outside your group reads your files, index or chat**: not the hub,
- not the network, not the operator of any *other* group (§3.2, §4).
-- The **hub holds no user key and cannot countersign**: it cannot add a device to
- your account, become *you*, or obtain your group key — except where you ask it
- to (open-join, mailed invitations; §7.3, §3.4).
-- A **node cannot be impersonated**, and content one node holds cannot be forged
- into another's (§5).
-- Your **identity keys are per-node**: a bundle that leaks opens that one node's
- bundle and no other (§3, §2.4).
-- **Chat is unreadable from a copy of a node's storage** that lacks its unlock
- key (§4.5).
-
-#### Residual risks, and how they are checked
-
-Two risks are real, named and accepted — each with the control that bounds it:
-
-- **T3 — a compromised hub and the browser.** The hub serves the browser SPA, so
- an active hub can ship modified code to a browser user and lift their keys. No
- protocol prevents this; it is accepted for browsers (§2.3). The **native client
- removes it**: its code ships in the package, not from the hub, so a malicious
- hub cannot alter it **without the change being publicly verifiable** — anyone
- can rebuild from source and compare hashes (reproducible builds). The attack
- moves from invisible-and-per-user to an artifact the whole community can check.
- This is *verifiable*, not merely *detectable*: integrity someone actively
- confirms, not something a victim might happen to notice.
-- **C4 — keypair bundles.** For an account with browser access, each node holds
- the bundle, sealed under the passphrase *and* a pepper the hub alone holds: no
- operator can search it offline, only an active hub can — which is T3's adversary
- already. For an account the desktop app keeps (browser access off, §3.7),
- nothing of it sits on any node. An account is only as strong as its weakest
- client.
-
-#### The full claim matrix
-
-For auditors: the complete claim-by-adversary matrix. The sections above are the
-reading; this is the reference.
-
-| Claim | Passive hub | Active hub | Malicious node operator | Malicious member | Network attacker |
-|---|---|---|---|---|---|
-| Data never transits the hub | ✅ | ✅ | — | — | ✅ |
-| File content is unreadable | ✅ | ❌ **T3** (browser) · ✅ native | ❌ by design — the operator hosts the files | ❌ members share the group key | ✅ |
-| The file index is unreadable | ✅ | ❌ T3 · ✅ native | ❌ | ❌ | ✅ |
-| Chat content is unreadable | ✅ | ❌ T3 · ✅ native | ❌ — the operator is a member | ❌ | ✅ |
-| Chat is unreadable **from a copy of the node's storage that lacks its unlock key** — not from a whole disk by default (§4.5) | ✅ | ✅ | — the operator holds the unlock key | ✅ | ✅ |
-| Content cannot be modified | ✅ | ✅ | ❌ by design | ✅ | ✅ |
-| The node cannot be impersonated | ✅ | ✅ | — | ✅ | ✅ |
-| Client code integrity | ❌ **T3, accepted** (browser) · ✅ ships in the package (native) | ❌ T3 · ⚠️ native: **publicly verifiable, not prevented** | ✅ | ✅ | ✅ |
-| The hub cannot obtain the group key | ✅ | ✅ **except** in an open-join group, where it can join legitimately (§7.3), and for an invitation the inviter asked the hub to mail, whose code it then holds (§3.4) | — | — | ✅ |
-| Node content authority | ✅ | ✅ | ✅ sovereign | ✅ | ✅ |
-| Devices cannot be added by the hub | ✅ | ✅ — the hub holds no user key and cannot countersign | ⚠️ a node adds a device only to itself, where it already reads everything | ✅ | ✅ |
-| Chat senders are authenticated to each other | ✅ | ✅ | ⚠️ only for accounts the reader has already seen (§3.3) | ✅ | ✅ |
-| Keypair bundles (**C4**) | ✅ it holds the pepper and no bundle | ⚠️ it can fetch a bundle with a token it mints and holds the pepper: an offline passphrase search, as T3 already concedes for browsers · none to fetch for an account without browser access | ✅ no offline search: the bundle does not open without the hub's pepper — only sign-in attempts, bounded and audited · none on disk for an account without browser access | — | — |
-| Deleting your account erases you | ✅ hub-side | ✅ hub-side | ❌ files, pinned identity and bundle stay on the node (§7.7) | — | — |
-| Your identity keys stay yours | ✅ | ⚠️ as the row above | ✅ the bundle they hold does not open without the pepper, and a leaked bundle key opens **that node's** bundle only | ✅ | ✅ |
-
-### 2.3 What the project must not claim
-
-Three sentences are forbidden, each for a deliberate reason:
-
-- **"Everything is encrypted and unreadable by other parties, even the hub."**
- A hub that ships the code can lift keys from the page regardless of protocol
- design (**T3**). That is an artifact-level attack, not a silent directory lie,
- and it is removed for native clients — not for browsers.
-- **"A native client makes the hub untrusted."** 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. A build signed with a key the hub operator holds
- *relocates* trust; it does not remove it.
-- **"C4 is closed."** It is closed for an account whose identities the desktop
- application keeps (browser access off, §3.7): nothing of them is on any node.
- For an account with browser access the bundle is still on each node, sealed
- under the passphrase **and** a pepper the hub holds — no operator can search it
- offline, but an active hub can, and that is T3's adversary already. **An account
- is only as strong as its weakest client.**
+### 2.4 The one honest caveat: the browser
-"End-to-end" here describes **client ↔ node**, never client ↔ client. Members and
-the operator read everything in their group; that is what a group is.
+Used in a web browser, MeshBay is a page the hub sends you on each visit — so a hub
+that had been taken over could send an altered page and lift your keys from it.
+This is true of **every** web application; most simply never say so, and no
+protocol can prevent it, because the attack is in the code itself rather than in
+the messages. So we never claim your content is unreadable *even by the hub* when
+you use a browser.
-### 2.4 One boundary worth naming
-
-An operator hosts your content by design. They should not be able to become
-*you*. The bundle on their disk does not let them try: it opens only with the
-passphrase and a pepper the hub hands to nobody but a session that proved the
-passphrase or a device key, so the only guesses left to them are sign-ins, which
-the hub counts and locks out. And were a bundle key to leak all the same, it
-opens **the bundle on that node** and no other. Reading what they host is by
-design; reading what *other* operators host is not, and does not follow (§3.2).
+The desktop and mobile apps close this. Their code is installed once, from a
+package anyone can rebuild from source and check against a published hash, so a
+tampered build is caught instead of silently trusted. For the strongest assurance,
+use the app. This is the project's single standing limit for browser use — tracked
+as **T3** — and the full, adversary-by-adversary analysis behind every statement
+in this section is in §13.9 for readers who want it.
---
@@ -2043,7 +1955,7 @@ an operator-signed op.
Hub minimisation was considered and **deferred, and may be dropped** (decision D4).
The hub keeps serving the web UI and remains in the trusted path by choice. That is
a legitimate product call; what follows from it is carried deliberately rather than
-by accident (§2.3).
+by accident (§2.4).
**Stores:** accounts (username, encrypted email, status, role), the group registry
and membership, IP logs (one year, legal retention), node registrations, refresh
@@ -3632,7 +3544,7 @@ Two structural recommendations from that review stand as rules:
|---|---|
| **T1** | **The password split.** The hub never sees a passphrase; it holds a verifier for a client-derived `auth_key`. The passphrase floor can therefore only be enforced client-side (§3.1) |
| **T2** | The hub was the key directory. **Closed** by admission redesign, not by safety numbers: the invite path reads no directory at all (§3.4). Reclassified as **H3** |
-| **T3** | **The hub serves the SPA. Accepted permanently for browser users.** It is the only remaining way an active hub reads content, it is an artifact-level attack rather than a silent lie, and it does not exist for a native client — whose value is realised by reproducible builds, not by packaging (§2.3, §8.2) |
+| **T3** | **The hub serves the SPA. Accepted permanently for browser users.** It is the only remaining way an active hub reads content, it is an artifact-level attack rather than a silent lie, and it does not exist for a native client — whose value is realised by reproducible builds, not by packaging (§13.9, §8.2) |
### 13.5b Availability between members (`AV`)
@@ -3765,6 +3677,72 @@ had already been asked.
| **O13** | **Hub identity pinning.** The client points at a hub by URL and nothing pins that hub's identity. Bounded, because a substituted hub can neither read content nor ship the code to a native client — worth doing all the same |
| **V1–V13**, **P1–P5** | Per-application open items: wording of a disabled-service state, whether artwork reuses the chunk path, cache TTL, multi-track surfacing, HEIC/RAW support, a fuller EXIF panel, lightbox preloading, album-boundary behaviour, cover selection |
+### 13.9 Formal adversary model
+
+§2 states the trust model for a human reader. This restates the same ground
+formally, for auditors and implementers: the adversaries every claim is written
+against, the claim-by-adversary matrix, and the over-claims the project refuses to
+make. Nothing here is new — it is §2 with the proofs shown.
+
+"End-to-end" throughout describes **client ↔ node**, never client ↔ client. The
+operator and the members read everything in their group; that is what a group is.
+
+#### Adversaries
+
+Every claim is written against one of these, and they are the only ones the
+document uses:
+
+| Adversary | What they can do |
+|---|---|
+| **Passive hub** | Read everything the hub legitimately stores and relays |
+| **Active hub** | Also lie: forge tokens, invent accounts, substitute values it publishes, ship modified client code to a browser |
+| **Malicious node operator** | Read and alter everything on their own machine, including the plaintext files they host |
+| **Malicious group member** | Everything a member may do, plus anything the protocol fails to refuse |
+| **Network attacker** | Observe and tamper with traffic between any two parties |
+| **Local attacker** | Reach loopback services and files on a client or node machine |
+| **Registered hub user with no membership** | Reach every hub endpoint that does not check membership |
+| **Federated peer hub** | Push directory rows and revocations over MHP |
+
+#### Claim matrix
+
+| Claim | Passive hub | Active hub | Malicious node operator | Malicious member | Network attacker |
+|---|---|---|---|---|---|
+| Data never transits the hub | ✅ | ✅ | — | — | ✅ |
+| File content is unreadable | ✅ | ❌ **T3** (browser) · ✅ native | ❌ by design — the operator hosts the files | ❌ members share the group key | ✅ |
+| The file index is unreadable | ✅ | ❌ T3 · ✅ native | ❌ | ❌ | ✅ |
+| Chat content is unreadable | ✅ | ❌ T3 · ✅ native | ❌ — the operator is a member | ❌ | ✅ |
+| Chat is unreadable **from a copy of the node's storage that lacks its unlock key** — not from a whole disk by default (§4.5) | ✅ | ✅ | — the operator holds the unlock key | ✅ | ✅ |
+| Content cannot be modified | ✅ | ✅ | ❌ by design | ✅ | ✅ |
+| The node cannot be impersonated | ✅ | ✅ | — | ✅ | ✅ |
+| Client code integrity | ❌ **T3, accepted** (browser) · ✅ ships in the package (native) | ❌ T3 · ⚠️ native: **publicly verifiable, not prevented** | ✅ | ✅ | ✅ |
+| The hub cannot obtain the group key | ✅ | ✅ **except** in an open-join group, where it can join legitimately (§7.3), and for an invitation the inviter asked the hub to mail, whose code it then holds (§3.4) | — | — | ✅ |
+| Node content authority | ✅ | ✅ | ✅ sovereign | ✅ | ✅ |
+| Devices cannot be added by the hub | ✅ | ✅ — the hub holds no user key and cannot countersign | ⚠️ a node adds a device only to itself, where it already reads everything | ✅ | ✅ |
+| Chat senders are authenticated to each other | ✅ | ✅ | ⚠️ only for accounts the reader has already seen (§3.3) | ✅ | ✅ |
+| Keypair bundles (**C4**) | ✅ it holds the pepper and no bundle | ⚠️ it can fetch a bundle with a token it mints and holds the pepper: an offline passphrase search, as T3 already concedes for browsers · none to fetch for an account without browser access | ✅ no offline search: the bundle does not open without the hub's pepper — only sign-in attempts, bounded and audited · none on disk for an account without browser access | — | — |
+| Deleting your account erases you | ✅ hub-side | ✅ hub-side | ❌ files, pinned identity and bundle stay on the node (§7.7) | — | — |
+| Your identity keys stay yours | ✅ | ⚠️ as the row above | ✅ the bundle they hold does not open without the pepper, and a leaked bundle key opens **that node's** bundle only | ✅ | ✅ |
+
+#### Over-claims the project refuses to make
+
+Three sentences are forbidden, each for a deliberate reason:
+
+- **"Everything is encrypted and unreadable by other parties, even the hub."**
+ A hub that ships the code can lift keys from the page regardless of protocol
+ design (**T3**). That is an artifact-level attack, not a silent directory lie,
+ and it is removed for native clients — not for browsers.
+- **"A native client makes the hub untrusted."** 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. A build signed with a key the hub operator holds
+ *relocates* trust; it does not remove it.
+- **"C4 is closed."** It is closed for an account whose identities the desktop
+ application keeps (browser access off, §3.7): nothing of them is on any node.
+ For an account with browser access the bundle is still on each node, sealed
+ under the passphrase **and** a pepper the hub holds — no operator can search it
+ offline, but an active hub can, and that is T3's adversary already. **An account
+ is only as strong as its weakest client.**
+
---
## 14. Decisions that are not revisited
@@ -3909,7 +3887,7 @@ superseded document kept under `docs/`, a note somebody holds elsewhere.
| Cited as | Read |
|---|---|
-| `draft-v5 §2`, `draft-v6 §4` — security claims | §2.2 |
+| `draft-v5 §2`, `draft-v6 §4` — security claims | §13.9 |
| `draft-v5 §3` — transport, NAT traversal | §5.1 |
| `draft-v5 §4`, §4.1–4.4 — handshake, transcript, channel binding, mutual auth | §5.2 |
| `draft-v5 §5.1`, `draft-v6 §2.3`, `§2.4b` — privileged operations, key activation, authorship | §5.4 |