# MeshBay — Next Implementation Phases > 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 **Objective:** a node usable for daily operations — multi-group, smooth streaming, integrated chat, fast reconnection. ### Prerequisites (from first security review, 2026-08-10) 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)** Currently the node punches blindly at startup → 12.7s handshake (NAT hole ages before the client arrives). Solution: ``` 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) immediately Node → Hub (WS) : "punch_ready" Hub → Client (HTTPS) : "connect now" Client → Node (QUIC) : < 2s after probe → fresh NAT entry → < 200ms ``` 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 | # | Component | File(s) | Priority | |---|---|---|---| | 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, production security **Objective:** hub ready for public operation — admin roles, MHP network, CSAM integrated, monitoring. | # | Component | File(s) | Priority | |---|---|---|---| | 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? --- ## Phase 9 — Android client MVP **Objectif :** app Android permettant de créer un compte, rejoindre un groupe, télécharger des fichiers depuis un node. **Stack technique à décider :** - **Kotlin natif** : plus de contrôle, accès direct aux APIs Android (WebRTC, QUIC via fork) - **Flutter** : cross-platform (iOS futur), Dart, mais bindings aioquic inexistants - **React Native** : JS, même problème de bindings natifs QUIC **Recommandation :** Kotlin natif. La partie critique (QUIC/UDP + crypto) est en C/Rust via des bindings JNI. La couche UI peut être Jetpack Compose. | # | Composant | Tech | Priorité | |---|---|---|---| | 9.1 | Hub client (auth, groups, GEK) | Kotlin + Retrofit | Haute | | 9.2 | Crypto (Ed25519, X25519, ChaCha20) | Bouncy Castle JVM | Haute | | 9.3 | QUIC client | quiche (Cloudflare, Rust JNI) ou QUIC4J | Haute | | 9.4 | NAT traversal (STUN + punch) | Kotlin native UDP | Haute | | 9.5 | File browser + download | Kotlin + streaming IO | Haute | | 9.6 | Chat UI | Jetpack Compose | Moyenne | | 9.7 | Node UI pairing (QR code) | Android camera + hub API | Moyenne | **Préalable à clarifier :** quels bindings QUIC existent sur Android ? `quiche` de Cloudflare (en Rust, JNI) est le plus mature. --- ## Phase 10 — Web client v2 : groupes privés + streaming **Objectif :** navigateur peut décoder le contenu privé (AES-GCM) et streamer des vidéos. | # | Composant | Fichier(s) | Priorité | |---|---|---|---| | 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 (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 : - **AES-GCM GEK** (actuel) : browser-native mais nécessite un groupe dédié - **ChaCha20 via WASM** : même GEK que les clients natifs, plus complexe --- ## Phase 11 — Résilience réseau : TURN relay, 0-RTT, CGNAT **Objectif :** fonctionner même derrière les NAT les plus restrictifs (mobile 4G/5G CGNAT). | # | Composant | Notes | Priorité | |---|---|---|---| | 11.1 | Mesh Relay TURN server | Node Python serveur UDP relay chiffré | Haute | | 11.2 | Relay registration MHP | Hub : `/v1/relays/` + annonce aux nodes | Haute | | 11.3 | Node : fallback automatique → relay | Après échec STUN dans discover_nat() | Haute | | 11.4 | Punch coordination signaling | Hub WS → node punch → client connect < 2s | Haute | | 11.5 | QUIC 0-RTT (aioquic session tickets) | Node stocke ticket → reconnexion < 50ms | Moyenne | | 11.6 | Test CGNAT mobile 4G | Spike dédié : node mobile → node fixe | Moyenne | | 11.7 | Connection pool (1 QUIC conn = N requêtes) | Node : réutilisation de stream par user | Moyenne | --- ## Phase 12 — RPM/DEB packaging production + CI **Objectif :** packages installables, CI qui tourne les tests, releases signées. | # | Composant | Notes | |---|---|---| | 12.1 | RPM build pipeline (Fedora, RHEL) | rpmbuild + spec files déjà écrits | | 12.2 | DEB build pipeline (Ubuntu, Debian) | dpkg-deb + control déjà écrits | | 12.3 | GitHub Actions CI | pytest + ruff sur PR | | 12.4 | Release signing | GPG key pour les packages | | 12.5 | Repo apt/dnf auto-hébergé | meshbay.org/packages/ | --- ## Ordre recommandé ``` Phase 7 (Node v2) ← débloque l'usage réel au quotidien Phase 8 (Hub v2) ← stabilisation, admin, CSAM Phase 11 (Relay+0-RTT)← résout le handshake 12.7s et CGNAT mobile Phase 9 (Android) ← client mobile, long chantier Phase 10 (Web v2) ← streaming privé browser Phase 12 (Packaging) ← distribution ``` **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)