diff options
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 33 |
1 files changed, 31 insertions, 2 deletions
@@ -72,7 +72,12 @@ dans le bon `pyproject.toml`. - All three packages share the same version number (released together) ### Protocol versions (independent) -- MNP: `0.4` → bumped independently of package version +- MNP: `1.0` → bumped independently of package version. **`handshake.py`'s + `MNP_MIN_SUPPORTED` is the other half**: both peers declare `v` and `v_min` on + the handshake and refuse each other with a code (`version_too_old` / + `version_too_new` / `version_unreadable`), so a mismatch is a refusal rather + than a field that turns up missing. Shipped with 1.0 because that flag day was + already being paid for; the next breaking change now costs a refusal message - 0.2 added `PING`/`PONG` and backward chat paging (`before` / `has_more`). Additive, so an 0.1 peer still works: it sends no `before` and is answered with the newest page, which is what it wanted @@ -685,7 +690,29 @@ anything that assumes one key per person. `GroupSenderKeyStore.add_sender` currently does `self._states[dist.sender_id] = ...`, so a second device under the same `sender_id` silently overwrites the first. Revoking a device must rotate, like revoking a member. See `docs/devel-phases-next.md` §15.0b -- Chat is plaintext on the wire and at rest; the index is plaintext on the WebRTC path +- ~~Chat is plaintext on the wire and at rest; the index is plaintext on the WebRTC + path.~~ **The index half changed 2026-09-03 (MNP 1.0).** `index_sync`, + `index_delta` and the `handshake_ack` configuration payload are sealed under a + GEK-derived subkey (`meshbay_common/groupbox.py`, mirrored by `sealGroup`/ + `openGroup` in `crypto.js`); only `type`, `v`, `group_id` and the ack's own + `node_pk`/`proof`/`sig` stay in clear, because a receiver must route and + **authenticate** before it would trust a decryption. Chat is unchanged and out of + scope by decision. Read `MESHBAY_NODE_PROTOCOL.md` §11.1a before touching either + message. Three things to keep straight: + - **The ack line is integrity, not confidentiality.** `handshake_transcript` + names no ack field, so `is_node_admin`, `enabled_apps`, `video_root` and the + rest were authenticated by the DTLS channel alone. The AEAD tag comes from a + key the hub does not hold + - **The index line is defence in depth against our own next bug**, of a class + already shipped twice: C1 (node HTTP API served the index on `0.0.0.0` + unauthenticated) and C6 (TCP accepted a bare JWT with no GEK proof). It buys + nothing against an observer, the hub, or a member. That is the whole claim + - **A payload that does not open ends the session**, never a default: an + unopenable `enabled_apps` reads as "the operator disabled every app" and an + unopenable index as "the group is empty", both indistinguishable from + legitimate states. `index_progress` is deliberately *not* sealed (counters + only, every 2 s) — the reason lives next to the code, re-read it before + changing it ## Known calibration TODOs @@ -731,6 +758,8 @@ SFR residential Fedora 44 → meshbay.org OVH VPS: | Need | Module | File | |---|---|---| | Chunk encryption (prod) | `meshbay_common.crypto` | `crypto.py` | +| Sealing a payload under the GEK | `meshbay_common.groupbox` | `groupbox.py` + `sealGroup`/`openGroup` in `static/crypto.js`. `index_sync`, `index_delta`, `handshake_ack` — one envelope, purpose-separated subkeys, AAD = `"<msg_type>\|<group_id>"`. **Never reuse `chunk_key_aes` with a pseudo-file for this** | +| MNP version range | `meshbay_common.handshake` | `MNP_MIN_SUPPORTED`, `check_version` — read by both servers and both clients | | Key derivation from password | `meshbay_common.keyderive` | `keyderive.py` | | Key bundle (web) | `meshbay_common.keyderive` | `keyderive.py` + `static/keyderive.js` | | GEK wrap/unwrap (ECIES) | `meshbay_common.crypto` | `crypto.py` | |