diff options
| -rw-r--r-- | devel-phases-next.md | 57 | ||||
| -rw-r--r-- | docs/meshbay-draft-v3.md | 54 |
2 files changed, 87 insertions, 24 deletions
diff --git a/devel-phases-next.md b/devel-phases-next.md index 96526a1..ff161d6 100644 --- a/devel-phases-next.md +++ b/devel-phases-next.md @@ -10,25 +10,56 @@ **Objectif :** un node utilisable quotidiennement — multi-groupe, streaming fluide, chat intégré, reconnexion rapide. -**Débloqueurs techniques :** -- 0-RTT QUIC (aioquic le supporte) → handshake < 50ms en reconnexion -- Coordination punch/connect via hub WebSocket (réduire le gap) -- HLS via QUIC (pas seulement via HTTP) +### Décisions architecturales (arrêtées) + +**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. + +**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 : +``` +Client → Hub (HTTPS) : "je vais connecter node X, je viens de IP:PORT" +Hub → Node (WS) : message "client_incoming: {peer_ip, peer_port}" +Node → NAT (UDP) : punch_nat(peer_ip, peer_port) immédiat +Node → Hub (WS) : "punch_ready" +Hub → Client (HTTPS) : "connecte-toi maintenant" +Client → Node (QUIC) : < 2s après le probe → trou frais → < 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) + +### Milestones | # | Composant | Fichier(s) | Priorité | |---|---|---|---| | 7.1 | QUIC 0-RTT session resumption | `transport/quic_server.py` + `quic_client.py` | Haute | -| 7.2 | Signaling punch/connect via hub WS | `hub/api/signaling.py` + `node/hub_client.py` | Haute | -| 7.3 | Daemon multi-groupe | `node/daemon.py` — N DirectoryIndexers + N servers | Haute | -| 7.4 | HLS streaming via QUIC | `node/transport/hls.py` — segments via QUIC stream | Moyenne | -| 7.5 | Chat wire protocol (MNP) | `common/protocol.py` MNP.CHAT_* + `node/chat/` | Moyenne | -| 7.6 | Chat UI (node web UI) | `node/ui/app.py` — fil de messages temps réel | Moyenne | +| 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 à clarifier avant 7.3 (multi-groupe) :** -- Un groupe = un port dédié, ou multiplexage sur un port unique ? -- Les groupes d'un même node partagent-ils la même clé Ed25519 de node ? -- Interface UI : comment présenter plusieurs groupes dans localhost:18000 ? +**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 ? --- diff --git a/docs/meshbay-draft-v3.md b/docs/meshbay-draft-v3.md index 6983191..e11200c 100644 --- a/docs/meshbay-draft-v3.md +++ b/docs/meshbay-draft-v3.md @@ -222,14 +222,20 @@ Complete table of validated and planned hub REST API endpoints. Endpoints marked A local program running on the host user's machine. The node is the actual host of all content. **Responsibilities:** -- Watch and index shared directories (Mesh Group Index) -- Serve files and video streams to group members +- Watch and index shared directories (Mesh Group Index) — one directory per group +- Serve files, video streams, and group chat to members - Manage all cryptographic keys locally (encrypted keystore) -- Handle P2P connections and NAT traversal -- Run the MNP protocol +- Handle P2P connections and NAT traversal (STUN + QUIC hole punching) +- Run the MNP protocol (QUIC v2, TCP+TLS v1) - Host the Python extension module sandbox - Serve the local web UI (localhost:18000) -- Host the group chat (core feature) + +**Multi-group architecture (decided Phase 7):** +A node exposes **one QUIC port** for all groups it hosts. Groups are not isolated +by port — the MNP handshake identifies the target group via the `group_id` claim +in the client JWT. The server routes each connection to the appropriate +DirectoryIndexer and GEK after JWT verification. +Rationale: one NAT hole to maintain, one port to forward manually if needed. **Platform:** Linux primary, cross-platform from the start (Windows/macOS). Python ensures portability. @@ -582,13 +588,23 @@ Node → Client: session established - `18001/tcp` — MNP P2P listener (exposed externally, TLS required) - Configurable via `~/.config/meshbay/node.toml` -### 6.6 Chat Encryption +### 6.6 Chat Encryption and Model + +Group chat is a **core feature** (not an extension module). + +**Model (decided):** between a forum and Signal. +- **Persistent:** messages stored on the node (not ephemeral like Signal by default) +- **Structured:** optional threads/topics for longer discussions, flat stream for quick messages +- **Scope:** per group (not per user pair) +- **Attachments:** files and images, shared like regular group files +- **Push/pull:** connected members get real-time push (WebSocket); offline members pull history on reconnect +- **Retention:** managed by the group admin (no automatic expiry) -Group chat is a **core feature** (not an extension module). Uses the **Double Ratchet algorithm** (as in Signal): +**Encryption:** Double Ratchet algorithm (implemented in `meshbay_common.ratchet`): - Forward secrecy and break-in recovery per message -- Each message independently encrypted -- Attachment files: encrypted with the current Double Ratchet message key, hash included in message -- Python implementation: [TBD — evaluate existing libraries] +- Each message independently encrypted, out-of-order delivery handled +- Attachment files: encrypted with GEK-derived key (same as file chunks), hash in message +- Group scope: all members share the same ratchet state seeded from the group GEK --- @@ -613,7 +629,23 @@ Group chat is a **core feature** (not an extension module). Uses the **Double Ra Without step 4 (Mesh Relay), approximately 15% of connections between symmetric-NAT peers will fail. This is documented behavior until Mesh Relay is implemented. -Signaling for hole punching: coordinated via hub WebSocket endpoint, <1 KB per attempt, no persistent state. +**Signaling punch/connect (Phase 7.2 — reduces handshake from 12.7s to < 200ms):** +Currently the node punches blindly at startup; the client may connect 10-20s later +on an aging NAT entry, causing retransmissions. The coordinated flow uses the +existing hub→node WebSocket (revocation channel): +``` +Client → Hub : POST /v1/nodes/{id}/incoming {peer_ip, peer_port} +Hub → Node (WS) : {type: "client_incoming", peer_ip, peer_port} +Node : punch_nat(peer_ip, peer_port) immediately +Node → Hub (WS) : {type: "punch_ready"} +Hub → Client: 200 OK "connect now" +Client → QUIC: first packet < 2s after probe → fresh NAT entry +``` +demo-v2 finding: SFR residential is **Port-Restricted Cone NAT**. +The probe must come from the QUIC server's own socket (`punch_nat()` via +`_transport.sendto()`). The QUIC client must connect from the same port +as the probe's destination (`local_port=QUIC_PORT`). Handshake time +with proper signaling: < 200ms (vs 12.7s without). ### 7.2 MNP — Mesh Node Protocol |