diff options
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 48 |
1 files changed, 44 insertions, 4 deletions
@@ -121,7 +121,8 @@ that produced it. | Looking for | Read | |---|---| -| What a label means (`C1`, `H3`, `NS6`, `T3`, `C5b`, `W2`, `E9`, `F1`, …) | `docs/MESHBAY_DESIGN.md` §13 | +| What a label means (`C1`, `H3`, `NS6`, `T3`, `C5b`, `W2`, `E9`, `F1`, `AV4`, …) | `docs/MESHBAY_DESIGN.md` §13 | +| What one member can cost the others (`AV1`–`AV8`) | §13.5b — the newest category, and the one the first three reviews had no question for | | Trust model, and what the project may and may not claim | §2 | | Identity, devices, admission, recovery, the keypair bundle | §3 | | Cryptography, key hierarchy, the group and chat envelopes | §4 | @@ -144,9 +145,16 @@ These are about working on the tree rather than about the design: `/a/<hash>/` against `meshbay_hub.api.webapp.ASSET_V` before concluding a fix is missing. `site/` and the Caddy config have never been deployed - **One UI source.** `packages/meshbay-hub/src/meshbay_hub/static/` is the - interface, for the web and the app alike; `packages/meshbay-client/build/ - sync-ui.js` copies it and CI fails if the copy drifts — **never edit - `packages/meshbay-client/ui/` by hand** + interface, for the web and the app alike; `packages/meshbay-client/scripts/ + sync-ui.js` copies it (`npm run sync-ui`) and CI fails if the copy drifts — + **never edit `packages/meshbay-client/ui/` by hand** +- **Before adding an endpoint or a message, ask who pays.** 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 — the + gap between `_authorize_node_ws` and `update_groups` was one message wide. + `docs/MESHBAY_DESIGN.md` §13.5b is the register; `test_availability_between_ + members.py` is where a new case goes, and every test in it is two accounts, + because a one-member test proves a one-member property - **Never change the KDF parameters in one place.** `keyderive.js`, `keyderive.py`, the QE harness and `test_bundle_kdf_parity.py` are held byte-identical by that test, and a mismatch does not look like an error — it @@ -590,6 +598,38 @@ do. Read them before writing anything that touches the same mechanism. needs a person with a large file. Confirmed by the operator on 2026-08-15: Firefox, 180 MB, written to disk. Nothing multi-gigabyte has been measured +- **An empty claim read as a claim on everything, and a list taken as a + ranking.** Two defaults, in two codebases, a month old and harmless + separately. The hub resolved a node's group claim with `set(claimed_groups or + authorized)`, and the node omitted `group_ids` entirely when it hosted + nothing — so "I host no groups" arrived as "I host all of this account's + groups", other members' included. Such a node can serve none of them: it has + no GEK, and its own handshake refuses them. But `/v1/groups/{id}/nodes` + answers in **registration order**, `_node_groups` is in-memory, and + GroupPage took `nodes[0]` with no fallback — so the group opened or did not + depending on who reconnected first after a hub restart. On 2026-09-11 a hub + deploy at 20:14 did exactly that: this node needed 16 s to come back (502 + while the hub restarts, then its access token had expired during the outage, + so a refusal and two 5 s backoffs), a second member's unconfigured node won + the race, and a group went dark for everyone with its only real host online + the whole time. **A member could take any of their groups down, by accident, + by leaving an empty node running.** + Four things worth keeping. The node's own logs and audit database were the + evidence that cleared it — every handshake OK, no refusal recorded since + August — which is what said the refusal came from somewhere else entirely; + believing the error message names the machine you are standing on is how an + hour goes. `git log -S` on the three suspect lines dated them all to August, + which is what turned "what did I just break" into "what changed around it": + the answer was a restart, not a commit. The read-only probe that settled it + is four lines — the node's own keystore, a node-scoped token, `/v1/groups/mine` + and `/v1/groups/{id}/nodes` — and node-scoped tokens are accepted by + `get_current_user`, so a node can ask the hub what the hub thinks of it. + And the rule under all of it: **a falsy empty collection must never mean + "unspecified"** — `if gids:` and `or authorized` are the same mistake written + twice, and the ceiling that made C2 hold on connection was stepped over one + message later by `update_groups`, which assigned its list verbatim. A limit + enforced on one path is not enforced + **Corrections that used to live here** — `punch_nat()` is not a traversal stack, the node keystore's Argon2id parameters, what group chat actually uses, and what is sealed on the wire — are now design statements in `docs/MESHBAY_DESIGN.md` |