aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-12 09:47:46 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-12 16:36:54 +0200
commit7c3a1d6fd765ef0421d0f3d85e512e10ea2f6f87 (patch)
tree00d05d94cbe1216085ffa94173b6643cd50c8b1e /docs
parentbf7ff9ec311660318c8562abe03ef4db62475c98 (diff)
downloadmeshbay-7c3a1d6fd765ef0421d0f3d85e512e10ea2f6f87.tar.gz
docs: availability between members is a finding category
The first three reviews asked who can read what, who can impersonate whom, and what a hostile node can forge. None asked what a legitimate but misconfigured or careless member costs everyone else — which is the question a group platform lives on, because every member was invited by someone who trusted them and none of them is an attacker. C2 had asked "can a node claim a group its owner is not in?" and the answer was correctly no. Nobody had asked what happens when a node claims one its owner *is* in but does not host, which is how a group went dark for all of its members with its real host online throughout. §13.5b is the register, AV1 to AV8. The lens, for anything reviewed from here: a participant supplies input; if anyone other than the sender bears the cost, there is a ceiling to write, and it goes on every path that writes the state. CLAUDE.md gets the working rule and the incident as a lesson, and the stale path to sync-ui.js corrected — it lives under scripts/, not build/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4YmK41VsEURWFdop4EEeT
Diffstat (limited to 'docs')
-rw-r--r--docs/MESHBAY_DESIGN.md29
1 files changed, 29 insertions, 0 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index f125825..bda33f1 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -2471,6 +2471,35 @@ Two structural recommendations from that review stand as rules:
| **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) |
+### 13.5b Availability between members (`AV`)
+
+**Added 2026-09-12, after the finding that produced it.** The first three
+reviews asked who can read what, who can impersonate whom, and what a hostile
+node can forge. None asked **what a legitimate but misconfigured or careless
+member costs everyone else** — and that is the question a group platform lives
+on, because every member was invited by someone who trusted them and none of
+them is an attacker. `C2` had asked "can a node claim a group its owner is not
+in?" and the answer was correctly no; nobody had asked what happens when a node
+claims one its owner *is* in but which it does not host, which is how a group
+went dark for all of its members on 2026-09-11 with its real host online
+throughout.
+
+The lens, for anything reviewed from here: **a participant supplies input; who
+else bears the cost?** Where the answer is "someone other than the sender",
+there must be a ceiling, and it must apply on every path that writes the state
+— not only the one where the ceiling was first thought of.
+
+| Label | The rule it names |
+|---|---|
+| **AV1** | **An empty claim is a claim on nothing.** A node's group set is `authorized ∩ claimed`, and an absent or empty `group_ids` registers it for no group rather than all of its owner's — on registration and on `update_groups` alike (§7.2) |
+| **AV2** | **A client treats the hub's node list as candidates, not a ranking**, and tries the next one on a `not_hosted` refusal (`MESHBAY_NODE_PROTOCOL.md` §6.3) |
+| **AV3** | **A node speaks only for the groups it is registered for.** `chat_notify` names a group and is checked against that node's set before a notification is written for anyone, and it is rate-limited per node — the fan-out is one write per member |
+| **AV4** | **Nobody names a third party's address.** A swarm source publishes a transport and a port, never a host; where a peer is comes from its node record, stamped with the address its announce arrived from. The number of hashes one account may claim is bounded |
+| **AV5** | **An answer is accepted only from the node the offer was sent to.** A `peer_id` is bound to its node, so no connected node can resolve another's pending offer |
+| **AV6** | **A relay proves possession of its approved key.** A public key is not a password, and the register call is unauthenticated by design — it is not a user — so the proof is the only thing standing between a stranger and where nodes send relayed traffic |
+| **AV7** | **A node bounds how many peers it holds and how long an unproven one lasts.** The hub's cap is per calling account, which is a limit on each member and not on the machine, so without this an operator's exposure grew with the size of their groups |
+| **AV8** | **One account cannot make the hub mail another at will.** The invitation email's subject comes from the group row, never from the request, and the endpoint is metered |
+
### 13.6 Chat design findings
| Label | The rule it names |