From 4b3e8c3b8b9d10c8ac333dd8db614a7569052472 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Mon, 10 Aug 2026 03:07:56 +0200 Subject: feat: Phase 7 — Node v2 (multi-group, Sender Keys, 0-RTT, chat, denylist) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements all 8 milestones (7.0-7.7): - 7.0: JWT carries `groups` claim; node verifies group membership at MNP handshake (QUIC + TCP+TLS). Resolves security review C2. - 7.1: QUIC 0-RTT session resumption via stored session tickets (17-21ms reconnect vs 47ms cold). - 7.2: Hub→node WebSocket signaling for NAT punch coordination (`client_incoming`/`punch_ready`) + jti denylist push. Denylist class blocks revoked users/jtis at handshake. - 7.3: Multi-group daemon — one QUIC port serves N groups with per-group GEK, shared_root, and index routing. - 7.4: HLS streaming via QUIC (STREAM_SEGMENT message type, ffmpeg segment extraction). - 7.5: Sender Keys protocol for group chat (Signal Groups approach). Each member has own sending chain key, HKDF chain ratchet, AES-256-GCM encryption, Ed25519 signing. Resolves security review C1. - 7.6: Chat store (SQLite via aiosqlite), CHAT_MESSAGE MNP wire type with peer broadcast, web UI with WebSocket push. - 7.7: Argon2id calibration CLI. First security review included (first-review.md). 109 tests, demo-v3 validated against meshbay.org production hub. Co-Authored-By: Claude Opus 4.6 --- devel-phases-next.md | 163 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 96 insertions(+), 67 deletions(-) (limited to 'devel-phases-next.md') diff --git a/devel-phases-next.md b/devel-phases-next.md index ff161d6..f4793df 100644 --- a/devel-phases-next.md +++ b/devel-phases-next.md @@ -1,88 +1,116 @@ -# MeshBay — Phases d'implémentation suivantes +# MeshBay — Next Implementation Phases -> Base : Phases 1-6 terminées. demo-v2 NAT QUIC validée. -> Référence architecture : docs/meshbay-draft-v3.md +> Base: Phases 1-6 complete. demo-v2 NAT QUIC validated. +> Architecture reference: docs/meshbay-draft-v3.md +> First security review: first-review.md (2026-08-10) --- ## Phase 7 — Node v2 : production, streaming, chat -**Objectif :** un node utilisable quotidiennement — multi-groupe, streaming fluide, -chat intégré, reconnexion rapide. +**Objective:** a node usable for daily operations — multi-group, smooth streaming, +integrated chat, fast reconnection. -### Décisions architecturales (arrêtées) +### Prerequisites (from first security review, 2026-08-10) -**Multi-groupe → multiplexage sur un seul port QUIC** -Un node expose un seul port QUIC (ex. 19010). Tous les groupes hébergés -partagent ce port. Le groupe est identifié dans le handshake MNP par le `group_id` -contenu dans le JWT. Avantages : un seul trou NAT à maintenir, une seule redirection -de port manuelle si nécessaire. Le serveur QUIC route chaque connexion vers -l'IndexGroup/GEK du bon groupe après vérification du JWT. +Before writing Phase 7 production code, two critical design gaps must be +addressed — see `first-review.md` for full analysis: + +1. **[C2] JWT must carry group membership claims.** Add `"groups": [group_ids]` + to `issue_access_token()`. Node MNP handshake must verify the requested + group_id is in the JWT's groups claim before serving any content. Without + this, any authenticated user can access any group on the node. + → Implement in 7.3 (multi-group daemon) before any other milestone. + +2. **[C1] Chat encryption: Sender Keys, not shared Double Ratchet.** The Double + Ratchet is a pairwise (1:1) protocol — sharing a single ratchet state across + N group members causes key/nonce reuse (AEAD catastrophic failure). The + architecture now uses **Sender Keys** (Signal Groups approach): each member + has their own sending chain key, O(N) state. + → Implement in 7.5. The existing `ratchet.py` is kept for future 1:1 DM. + +### Architectural decisions (settled) + +**Multi-group → multiplexing on a single QUIC port** +A node exposes a single QUIC port (e.g. 19010). All hosted groups share this +port. The group is identified in the MNP handshake by the `group_id` in the +JWT. Advantages: one NAT hole to maintain, one manual port forward if needed. +The QUIC server routes each connection to the appropriate IndexGroup/GEK +after JWT verification **and group membership authorization check**. **Signaling punch/connect (via hub WebSocket)** -Actuellement, le node punchs aveuglément au démarrage → 12.7s de handshake -(trou NAT vieillit avant que le client arrive). Solution : +Currently the node punches blindly at startup → 12.7s handshake (NAT hole ages +before the client arrives). Solution: ``` -Client → Hub (HTTPS) : "je vais connecter node X, je viens de IP:PORT" +Client → Hub (HTTPS) : "I'm about to connect to node X, I'm at IP:PORT" Hub → Node (WS) : message "client_incoming: {peer_ip, peer_port}" -Node → NAT (UDP) : punch_nat(peer_ip, peer_port) immédiat +Node → NAT (UDP) : punch_nat(peer_ip, peer_port) immediately Node → Hub (WS) : "punch_ready" -Hub → Client (HTTPS) : "connecte-toi maintenant" -Client → Node (QUIC) : < 2s après le probe → trou frais → < 200ms +Hub → Client (HTTPS) : "connect now" +Client → Node (QUIC) : < 2s after probe → fresh NAT entry → < 200ms ``` -Le canal hub→node WebSocket existe déjà (`hub/api/revocation.py`). -Il suffit d'ajouter le type de message `client_incoming` / `punch_ready`. -Ce mécanisme s'appuie sur l'ICE simplifié (Interactive Connectivity Establishment). - -**Chat — entre forum et Signal** -Pas un chat temps-réel éphémère (Signal) ni un forum lourd. -Modèle : **fil de discussion chiffré E2E, persistant sur le node**. -- Messages courts + pièces jointes (comme Signal groupe) -- Fils/topics optionnels pour structurer (comme un forum léger) -- Historique stocké sur le node (pas éphémère) -- Push pour membres connectés, pull pour hors-ligne -- Double Ratchet (déjà implémenté) pour le chiffrement -- Scope : par groupe (pas par paire d'utilisateurs) -- Pas de suppression automatique (l'admin du groupe gère la rétention) +The hub→node WebSocket channel already exists (`hub/api/revocation.py`). +Just add `client_incoming` / `punch_ready` message types. +This mechanism is a simplified ICE (Interactive Connectivity Establishment). + +The same WebSocket channel also carries **jti denylist push** (security review +S3): when the hub revokes a token, it pushes the jti to all connected nodes. +Nodes maintain an in-memory set and check it during MNP handshake. + +**Chat — between a forum and Signal** +Not a real-time ephemeral chat (Signal) nor a heavy forum. +Model: **E2E encrypted discussion thread, persisted on the node**. +- Short messages + attachments (like Signal group) +- Optional threads/topics for structure (like a light forum) +- History stored on the node (not ephemeral) +- Push for connected members, pull for offline +- **Sender Keys** protocol for encryption (security review C1 — Double Ratchet + is pairwise only, cannot be shared across group members) +- Scope: per group (not per user pair) +- No automatic deletion (group admin manages retention) ### Milestones -| # | Composant | Fichier(s) | Priorité | +| # | Component | File(s) | Priority | |---|---|---|---| -| 7.1 | QUIC 0-RTT session resumption | `transport/quic_server.py` + `quic_client.py` | Haute | -| 7.2 | Signaling `client_incoming`/`punch_ready` | `hub/api/revocation.py` + `node/hub_client.py` | Haute | -| 7.3 | Daemon multi-groupe (multiplexage 1 port) | `node/daemon.py` — N IndexGroups, 1 QuicChunkServer | Haute | -| 7.4 | HLS streaming via QUIC | `node/transport/hls.py` — segments en QUIC streams | Moyenne | -| 7.5 | Chat : stockage + wire protocol MNP | `node/chat/store.py` + `common/protocol.py` | Moyenne | -| 7.6 | Chat : UI web locale + push WS members | `node/ui/app.py` WebSocket pour notifications | Moyenne | -| 7.7 | Calibration Argon2id CLI | `node/daemon.py` — `meshbay-node calibrate-argon2` | Basse | - -**Questions ouvertes restantes :** -- Les groupes d'un même node partagent-ils la même clé Ed25519 de node ? (probable oui) -- UI multi-groupe localhost:18000 : onglets par groupe ou liste unifiée ? +| 7.0 | JWT group claims + node authz check | `hub/auth.py` + `node/transport/quic_server.py` | **Blocker** | +| 7.1 | QUIC 0-RTT session resumption | `transport/quic_server.py` + `quic_client.py` | High | +| 7.2 | Signaling `client_incoming`/`punch_ready` + jti denylist push | `hub/api/revocation.py` + `node/hub_client.py` | High | +| 7.3 | Multi-group daemon (1-port multiplexing) | `node/daemon.py` — N IndexGroups, 1 QuicChunkServer | High | +| 7.4 | HLS streaming via QUIC | `node/transport/hls.py` — segments as QUIC streams | Medium | +| 7.5 | Chat: Sender Keys protocol + storage + MNP wire | `common/senderkeys.py` + `node/chat/store.py` | Medium | +| 7.6 | Chat: local web UI + WS push to members | `node/ui/app.py` WebSocket for notifications | Medium | +| 7.7 | Argon2id calibration CLI | `node/daemon.py` — `meshbay-node calibrate-argon2` | Low | + +**Remaining open questions:** +- Do groups on the same node share the node's Ed25519 key? (likely yes) +- Multi-group UI at localhost:18000: tabs per group or unified list? --- -## Phase 8 — Hub v2 : admin, federation, sécurité production +## Phase 8 — Hub v2: admin, federation, production security -**Objectif :** hub prêt pour opération publique — rôles admin, MHP réseau, -CSAM intégré, monitoring. +**Objective:** hub ready for public operation — admin roles, MHP network, +CSAM integrated, monitoring. -| # | Composant | Fichier(s) | Priorité | +| # | Component | File(s) | Priority | |---|---|---|---| -| 8.1 | Rôles admin (hub_admin flag sur User) | `hub/db/models.py` + `hub/api/admin.py` | Haute | -| 8.2 | MHP inter-hub réseau (pas juste en mémoire) | `hub/api/federation.py` + Alembic migration | Haute | -| 8.3 | federated_groups DB persistance | `hub/db/models.py` FederatedGroup already defined | Haute | -| 8.4 | CSAM DB réelle (import NCMEC/IWF) | `hub/csam.py` — import CLI + API update | Haute | -| 8.5 | Signaling endpoint WS (pour punch coordination) | `hub/api/signaling.py` | Haute | -| 8.6 | Métriques / healthcheck | `hub/api/health.py` | Moyenne | -| 8.7 | Cleanup IP logs (purge > 1 an) | `hub/tasks/cleanup.py` — APScheduler | Moyenne | -| 8.8 | Alembic migration Argon2id params | Bump migration + `hub/auth.py` | Basse | - -**Questions à clarifier :** -- Qui peut être hub_admin ? Premier user inscrit ? Config toml ? -- MHP : authentification inter-hubs via JWT ou mutual TLS ? -- Signaling : hub WebSocket pour coordonner punch → connect en < 2s +| 8.1 | Admin roles (hub_admin flag on User) + authz on revocation | `hub/db/models.py` + `hub/api/admin.py` + `hub/api/revocation.py` | **High — S1** | +| 8.2 | Email encryption at rest | `hub/db/models.py` — AES-256-GCM with config-derived key | **High — S2** | +| 8.3 | Refresh token rotation (one-time use) | `hub/api/users.py` — rotate on each use, detect reuse | **High — S5** | +| 8.4 | MHP inter-hub network (not just in-memory) | `hub/api/federation.py` + Alembic migration | High | +| 8.5 | federated_groups DB persistence | `hub/db/models.py` FederatedGroup already defined | High | +| 8.6 | CSAM real DB (import NCMEC/IWF) | `hub/csam.py` — import CLI + API update | High | +| 8.7 | Rate limiting on all authenticated endpoints | `hub/api/middleware.py` — extend slowapi | Medium — M3 | +| 8.8 | Metrics / healthcheck | `hub/api/health.py` | Medium | +| 8.9 | Cleanup IP logs (purge > 1 year) | `hub/tasks/cleanup.py` — APScheduler | Medium | +| 8.10 | Alembic migration Argon2id params | Bump migration + `hub/auth.py` | Low | + +Items 8.1-8.3 are from the first security review (S1, S2, S5). + +**Questions to clarify:** +- Who can be hub_admin? First registered user? Config toml? +- MHP: inter-hub authentication via JWT or mutual TLS? --- @@ -123,7 +151,7 @@ des bindings JNI. La couche UI peut être Jetpack Compose. | 10.1 | Web client : décryptage privé (AES-GCM + SubtleCrypto) | `static/crypto.js` MeshBayCrypto | Haute | | 10.2 | Web client : groupe-type "browser" (AES-GCM GEK) | Hub : `cipher` field sur Group | Haute | | 10.3 | Player HLS dans browser (hls.js + déchiffrement) | `static/app.js` + hls.js | Haute | -| 10.4 | Chat browser (Double Ratchet JS via WASM ou port) | `static/ratchet.js` | Moyenne | +| 10.4 | Chat browser (Sender Keys JS — AES-GCM via SubtleCrypto) | `static/senderkeys.js` | Moyenne | | 10.5 | PWA / Service Worker | offline + cache | Basse | **Question clé :** pour le streaming privé en browser, deux approches : @@ -173,8 +201,9 @@ Phase 10 (Web v2) ← streaming privé browser Phase 12 (Packaging) ← distribution ``` -**Prochaine décision structurante :** -La Phase 7 nécessite de clarifier 3 points avant de coder : -1. Architecture multi-groupe sur un node (ports partagés ou dédiés ?) -2. Mécanisme de signaling punch/connect (nouveau endpoint WS sur le hub ?) -3. Le chat est-il un module (Phase 7.5) ou une feature core du protocole ? +**Next structural decisions (all resolved — see first-review.md):** +1. Multi-group on a single QUIC port ✅ (decided Phase 7) +2. Signaling punch/connect via existing hub WS ✅ (decided Phase 7) +3. Chat is a core feature, not a module ✅ (decided draft v3) +4. Chat encryption: Sender Keys ✅ (decided in security review) +5. JWT group claims required ✅ (decided in security review) -- cgit v1.2.3