diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-10 22:12:59 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-10 22:12:59 +0200 |
| commit | 60c4570e72e36c2a9720593c8baec74ee2ab52d6 (patch) | |
| tree | e833e222a6a95e64e5b24a0813882636044198c2 /devel-phases-next.md | |
| parent | 1a53eb4cc404ec94658fde0ae04cfe2ccf1810dc (diff) | |
| download | meshbay-60c4570e72e36c2a9720593c8baec74ee2ab52d6.tar.gz | |
feat: Phase 9.1–9.5 — WebRTC DataChannel transport for browser P2P
Browser clients can now connect P2P to nodes behind residential NAT via
WebRTC DataChannel with ICE/STUN. Validated on SFR Port-Restricted Cone
NAT + 4G CGNAT across three scenarios (WiFi LAN, 4G IPv6, 4G IPv4 STUN).
No TURN relay needed. Hub serves only as signaling relay (<1 KB).
New files:
- webrtc_server.py: aiortc-based WebRTC transport (node side)
- signaling.py: SDP/ICE relay endpoint (hub side)
- transport.js: browser WebRTC client with msgpack framing
- webrtc-test.html: spike test page for browser→NAT→node validation
- test_webrtc_transport.py: 4 tests (handshake, file transfer, auth, guard)
- meshbay-draft-v4.md: architecture spec updated for web client
Modified:
- hub_client.py: WebRTC offer handling via hub WebSocket
- revocation.py: node_id from WS auth + webrtc_answer routing
- pyproject.toml: aiortc>=1.9 dependency
123 tests passing (117 existing + 6 new).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'devel-phases-next.md')
| -rw-r--r-- | devel-phases-next.md | 419 |
1 files changed, 270 insertions, 149 deletions
diff --git a/devel-phases-next.md b/devel-phases-next.md index f4793df..09d07fa 100644 --- a/devel-phases-next.md +++ b/devel-phases-next.md @@ -1,209 +1,330 @@ # MeshBay — Next Implementation Phases -> Base: Phases 1-6 complete. demo-v2 NAT QUIC validated. -> Architecture reference: docs/meshbay-draft-v3.md +> Base: Phases 1–8 complete. 117 tests. demo-v3 validated against meshbay.org. +> Architecture reference: docs/meshbay-draft-v4.md > First security review: first-review.md (2026-08-10) --- -## Phase 7 — Node v2 : production, streaming, chat +## Phase 7 — Node v2 : production, streaming, chat ✅ DONE -**Objective:** a node usable for daily operations — multi-group, smooth streaming, -integrated chat, fast reconnection. +Commit: fc56585 — 26 files, +2155/−159 lines, 109 tests. -### Prerequisites (from first security review, 2026-08-10) +| # | Component | Status | +|---|---|---| +| 7.0 | JWT group claims + node authz check | ✅ | +| 7.1 | QUIC 0-RTT session resumption | ✅ | +| 7.2 | Signaling `client_incoming`/`punch_ready` + jti denylist push | ✅ | +| 7.3 | Multi-group daemon (1-port multiplexing) | ✅ | +| 7.4 | HLS streaming via QUIC | ✅ | +| 7.5 | Chat: Sender Keys protocol + storage + MNP wire | ✅ | +| 7.6 | Chat: local web UI + WS push to members | ✅ | + +--- + +## Phase 8 — Hub v2: admin, federation, security ✅ DONE + +Commit: 46918ec — 20 files, +508/−90 lines, 117 tests. +Deployed to meshbay.org. Existing emails encrypted. DB schema migrated. + +| # | Component | Status | +|---|---|---| +| 8.1 | Admin roles — config-based `require_admin` | ✅ S1 resolved | +| 8.2 | Email encrypted at rest — AES-256-GCM, HKDF | ✅ S2 resolved | +| 8.3 | Refresh token rotation — family-based reuse detection | ✅ S5 resolved | +| 8.4 | Federation DB persistence (HubPeer model) | ✅ | +| 8.5 | Federation token verification async (DB-backed) | ✅ | +| 8.6 | CSAM hash check in swarm registration | ✅ | +| 8.7 | Rate limiting on auth endpoints (5/10/20 per min) | ✅ | +| 8.8 | Healthcheck endpoint (GET /v1/health) | ✅ | +| 8.9 | IP log cleanup background task (365-day retention) | ✅ | +| 8.10 | Argon2id bumped to 256 MB (pw_version=2, rehash on login) | ✅ | + +--- + +## Phase 9 — Web client: WebRTC transport + core SPA + +**Objective:** a web browser can connect P2P to a node behind residential NAT, +browse files, download, stream video, and chat — with zero data through the hub. + +**Architecture decisions (settled 2026-08-10):** + +### Transport: WebRTC DataChannel for browsers -Before writing Phase 7 production code, two critical design gaps must be -addressed — see `first-review.md` for full analysis: +Native clients (desktop, Android) use QUIC with `punch_nat()` — already validated +in demo-v2 on SFR residential (Port-Restricted Cone NAT). -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. +Browsers cannot use QUIC for NAT traversal because WebTransport does not allow +the browser to choose its UDP source port. Port-Restricted Cone NAT requires the +client to connect from the exact port the node probed — impossible for browsers. -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. +**Solution:** WebRTC DataChannel with ICE/STUN. The browser's built-in WebRTC +stack handles NAT traversal automatically. The node uses `aiortc` (same author as +`aioquic`, already referenced in draft-v3 as [future]). -### Architectural decisions (settled) +ICE is strictly superior to our custom `punch_nat()` for this use case: +- Both sides send STUN binding requests simultaneously → mutual hole-punching +- No need for the client to pre-announce its port +- Handles both sides behind NAT +- Battle-tested by billions of users (Google Meet, Discord, etc.) -**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**. +The MNP protocol (handshake, file_request, file_chunk, chat_message, etc.) runs +identically over WebRTC DataChannel as over QUIC streams. Same E2E encryption. + +**Node dual transport:** +- QUIC (port 19000) — native clients, already in place +- WebRTC DataChannel — browsers, using `aiortc` + +### Signaling: hub WebSocket relay + +The hub relays WebRTC signaling (SDP offer/answer, ICE candidates) between +browser and node. This is the same role described in draft-v3 section 4.1.3: +"NAT traversal coordination [...] stateless [...] <1 KB per message." -**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 +Browser → Hub (HTTPS) : POST /v1/nodes/{id}/webrtc/offer {sdp, ice_candidates} +Hub → Node (WS) : {type: "webrtc_offer", sdp, ice_candidates, peer_id} +Node → Hub (WS) : {type: "webrtc_answer", sdp, ice_candidates, peer_id} +Hub → Browser (SSE) : {sdp, ice_candidates} ``` -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. +After signaling, the DataChannel is P2P. Hub is no longer involved. -**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) +### UI: Preact SPA -### Milestones +- **Framework:** Preact (~3 KB gzipped) + preact-router +- **Build:** esbuild (single binary, no node_modules bloat) for minification +- **Theming:** CSS `prefers-color-scheme` + localStorage toggle (dark/light) +- **i18n:** JSON translation files loaded client-side, English default +- **Responsive:** sidebar collapses to hamburger on mobile viewports +- **Crypto:** existing `crypto.js` (SubtleCrypto AES-GCM) for E2E decryption -| # | 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 | +### Hub role (reminder — fundamental constraint) -**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? +The hub is a registrar and signaling facilitator. It stores ONLY: +- User accounts (login, encrypted email, public keys, keypair bundle) +- Group metadata (name, admin, members, GEK bundles — no file indexes) +- Node registrations (endpoint hints, public keys) ---- +All data (files, streams, chat messages, directory indexes) lives on mesh nodes. +Clients (web or native) transfer data E2E with nodes. The hub never touches +content. This is non-negotiable. -## Phase 8 — Hub v2: admin, federation, production security +### Chat/forum storage -**Objective:** hub ready for public operation — admin roles, MHP network, -CSAM integrated, monitoring. +Chat messages are stored on the node(s) hosting the group, not on the hub. +The browser retrieves chat history from the node via DataChannel, same as files. +If no node in the group is online, the group (including chat) is unavailable. +This is inherent to the P2P model and acceptable. -| # | Component | File(s) | Priority | +### File search + +Content is not indexed on the hub. Search works client-side: +- Node provides a Mesh Group Index (file metadata: names, paths, sizes, hashes) +- For private groups, the index is GEK-encrypted — hub stores it opaque, client decrypts +- Browser caches decrypted indexes in IndexedDB (~50–100 MB quota, extensible) +- Search runs locally on cached indexes — instant, no network call, no hub involvement + +### Milestones + +| # | Component | Files | 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 | +| 9.1 | **Spike: WebRTC DataChannel on node** | `aiortc` integration, 4 tests (handshake, file transfer, auth, guard) | ✅ | +| 9.2 | WebRTC signaling endpoints on hub | `hub/api/signaling.py` — relay SDP/ICE, 2 tests | ✅ | +| 9.3 | WebRTC→MNP transport adapter on node | `node/transport/webrtc_server.py` + hub_client WebRTC handler | ✅ | +| 9.4 | `transport.js` — browser WebRTC client | `static/transport.js` — connect, handshake, fetch, msgpack | ✅ | +| 9.5 | **Spike: E2E browser→NAT→node file transfer** | Mobile 4G → SFR NAT → node, IPv4 STUN + IPv6 validated | ✅ | +| 9.6 | Preact SPA shell (login, routing, theme) | `static/app.js`, `static/css/style.css` | High | +| 9.7 | Group list + file explorer UI | `static/components/GroupList.js`, `FileExplorer.js` | High | +| 9.8 | File download via DataChannel | `static/components/Download.js` — chunk reassembly | High | +| 9.9 | Video streaming via DataChannel | HLS segments → MediaSource API | Medium | +| 9.10 | Chat/forum UI via DataChannel | `static/components/ChatView.js` — Sender Keys | Medium | +| 9.11 | i18n framework + English strings | `static/i18n/en.json` | Medium | +| 9.12 | Settings UI (profile, theme, notifications) | `static/components/Settings.js` | Medium | +| 9.13 | Tests: unit + integration | WebRTC transport, MNP over DataChannel | High | + +**Critical path validated (2026-08-10):** 9.1 → 9.5 all pass. WebRTC DataChannel +works browser → node through SFR residential NAT, confirmed with three scenarios: + +| Test | ICE path | Result | +|---|---|---| +| WiFi LAN (same network) | IPv6 direct | OK, ~100ms | +| Mobile 4G SFR + IPv6 | IPv6 inter-network | OK, ~600ms | +| Mobile 4G SFR + IPv4 only | STUN hole-punch IPv4 | OK, ~650ms | -Items 8.1-8.3 are from the first security review (S1, S2, S5). +Node behind SFR Port-Restricted Cone NAT + mobile behind SFR CGNAT 4G. +No TURN relay needed. ICE/STUN handles both NAT types automatically. -**Questions to clarify:** -- Who can be hub_admin? First registered user? Config toml? -- MHP: inter-hub authentication via JWT or mutual TLS? +**Dependencies added:** +- `aiortc>=1.9` in `meshbay-node/pyproject.toml` ✅ +- `esbuild` as a dev tool (single binary, not npm) — needed for 9.6+ +- `preact` + `preact-router` (ESM imports, no npm needed — CDN or vendored) --- -## Phase 9 — Android client MVP +## Phase 10 — meshbay.org site + admin/moderation UI -**Objectif :** app Android permettant de créer un compte, rejoindre un groupe, -télécharger des fichiers depuis un node. +**Objective:** meshbay.org becomes both a production hub and the project's public +website, with admin/moderation interfaces and user-facing features. -**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 +### Site architecture -**Recommandation :** Kotlin natif. La partie critique (QUIC/UDP + crypto) est en C/Rust via -des bindings JNI. La couche UI peut être Jetpack Compose. +Two layers, cleanly separated: +- **Generic hub** (API + web app) — reusable by any hub operator +- **Site overlay** — meshbay.org-specific pages (landing, /downloads, /about) -| # | 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 | +The site overlay is served by Caddy (static files) with priority over the hub. +The hub serves the SPA for authenticated users at `/app/`. -**Préalable à clarifier :** quels bindings QUIC existent sur Android ? -`quiche` de Cloudflare (en Rust, JNI) est le plus mature. +``` +site/ # meshbay.org-specific (not in generic hub package) +├── index.html # Landing page — project promotion +├── downloads.html # Package repos (Ubuntu, Fedora, Android APK) +├── about.html # Project info, GitHub link, contact +└── assets/ # Landing page CSS/images +``` ---- +### User roles -## Phase 10 — Web client v2 : groupes privés + streaming +| Role | Capabilities | +|---|---| +| `user` | Standard user — browse, download, chat, manage own profile | +| `moderator` | Review reports, suspend content/groups/users | +| `admin` | All moderator rights + hub management, user management, logs | -**Objectif :** navigateur peut décoder le contenu privé (AES-GCM) et streamer des vidéos. +Role stored on User model. `require_moderator` dependency (checks role ≥ moderator). +`require_admin` already exists (Phase 8.1 — config-based, extended to DB role). -| # | 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 | +### Milestones -**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 +| # | Component | Priority | +|---|---|---| +| 10.1 | Landing page + /downloads + /about | High | +| 10.2 | Moderator role + `require_moderator` dependency | High | +| 10.3 | Moderation UI (report list, suspend content/group/user) | High | +| 10.4 | Admin UI (hub management, user list, logs viewer) | High | +| 10.5 | Notification system (invitations, new content, maintenance) | Medium | +| 10.6 | User settings (profile, per-group options, privacy, mute) | Medium | +| 10.7 | Public group search (name + keyword in description) | Medium | +| 10.8 | Front page (notifications feed, prioritized: contacts → private → public) | Medium | +| 10.9 | Package repositories (APT for Ubuntu, DNF for Fedora) | Medium | +| 10.10 | Auto-update check endpoint for clients | Low | + +### Hub mirror (design only — implementation deferred) + +A mirror hub is a complete replica of the primary hub (same user DB, same groups, +same GEK bundles, same storage). Purpose: load distribution via DNS round-robin. + +**Design constraints:** +- Shared Ed25519 private key (transferred once at setup, securely) +- PostgreSQL logical replication for active-active read/write on both mirrors +- Both mirrors can issue JWTs (same signing key) +- DNS round-robin (2+ A records on meshbay.org) +- If one mirror goes down, the other continues serving + +**Not implemented now.** The design must not prevent future implementation: +- Hub config and private key paths must be externalizable +- No hub-specific state that can't be replicated +- JWT verification must not depend on hub-local state --- -## Phase 11 — Résilience réseau : TURN relay, 0-RTT, CGNAT +## Phase 11 — Android client MVP + +**Objective:** Android app for account creation, group browsing, file download, +chat. No node functionality on mobile (client-only). -**Objectif :** fonctionner même derrière les NAT les plus restrictifs (mobile 4G/5G CGNAT). +**Stack:** Kotlin native + Jetpack Compose. QUIC via `quiche` (Cloudflare, Rust +JNI binding). Crypto via Bouncy Castle JVM. Same NAT traversal as desktop native +clients (`punch_nat` + QUIC). -| # | Composant | Notes | Priorité | +| # | Component | Tech | Priority | |---|---|---|---| -| 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 | +| 11.1 | Hub client (auth, groups, GEK) | Kotlin + Retrofit | High | +| 11.2 | Crypto (Ed25519, X25519, ChaCha20) | Bouncy Castle JVM | High | +| 11.3 | QUIC client | quiche (Rust JNI) | High | +| 11.4 | NAT traversal (STUN + punch) | Kotlin native UDP | High | +| 11.5 | File browser + download | Kotlin + streaming IO | High | +| 11.6 | Chat UI | Jetpack Compose | Medium | +| 11.7 | Contact list integration | Android Contacts API (permission-gated) | Medium | +| 11.8 | Account creation from app | Registration flow + keypair bundle | High | + +**Cross-device compatibility:** the user may switch between web and Android. +The `keypair_bundle` (encrypted, stored on hub) enables this — same credentials, +same keys on both platforms. Notification state and read markers should sync +via hub (small encrypted blob per user, minimal storage). + +**Upload from mobile:** posting photos/videos to a group. The mobile uploads to +the group's node(s), not to the hub. The node stores it. MNP protocol extended +with an `upload` message type for client→node push. + +**Out of scope:** node functionality on mobile, Mac/iPhone support. --- -## Phase 12 — RPM/DEB packaging production + CI +## Phase 12 — Network resilience (optional, low priority) -**Objectif :** packages installables, CI qui tourne les tests, releases signées. +**Objective:** handle edge cases — symmetric NAT (CGNAT mobile), TURN relay, +0-RTT reconnection. Not needed for typical residential users. -| # | Composant | Notes | +| # | Component | Priority | |---|---|---| -| 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/ | +| 12.1 | Mesh Relay TURN server | Low | +| 12.2 | Relay registration via MHP | Low | +| 12.3 | Node fallback to relay after ICE failure | Low | +| 12.4 | QUIC 0-RTT (session tickets) | Medium | +| 12.5 | Connection pool (1 QUIC conn = N requests) | Medium | +| 12.6 | Test CGNAT mobile 4G | Low | + +**Note:** enterprise users behind restrictive firewalls can configure port +forwarding themselves. This phase targets the ~15% of residential connections +where even ICE/STUN fails (symmetric NAT behind CGNAT). Not a priority — +the user explicitly deprioritized this. + +--- + +## Phase 13 — RPM/DEB packaging + CI + +| # | Component | +|---|---| +| 13.1 | RPM build pipeline (Fedora, RHEL) | +| 13.2 | DEB build pipeline (Ubuntu, Debian) | +| 13.3 | GitHub Actions CI (pytest + ruff on PR) | +| 13.4 | Release signing (GPG key) | +| 13.5 | Repo apt/dnf on meshbay.org/packages/ | +| 13.6 | Android APK distribution on meshbay.org/downloads/ | --- -## Ordre recommandé +## Recommended order ``` -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 +Phase 9 (Web client) ← core product: browser P2P to nodes +Phase 10 (Site + admin UI) ← public-facing site, moderation, admin +Phase 11 (Android) ← mobile client, long effort +Phase 13 (Packaging) ← distribution +Phase 12 (Resilience) ← optional, edge cases only ``` -**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) +Phase 9 is the critical path. Milestone 9.5 (spike: browser → NAT → node file +transfer) is the single most important validation in the project. If it works, +the web client is viable. If not, the architecture needs fundamental rethinking. + +--- + +## Structural decisions (all resolved) + +1. Multi-group on a single QUIC port ✅ (Phase 7) +2. Signaling punch/connect via hub WS ✅ (Phase 7) +3. Chat is a core feature, not a module ✅ (draft v3) +4. Chat encryption: Sender Keys ✅ (security review) +5. JWT group claims required ✅ (security review) +6. Admin model: config-based ✅ (Phase 8) +7. Refresh token rotation: family-based ✅ (Phase 8) +8. Email encrypted at rest: AES-256-GCM ✅ (Phase 8) +9. Argon2id params: 256 MB, pw_version for migration ✅ (Phase 8) +10. **Browser transport: WebRTC DataChannel + ICE/STUN** ✅ (decided 2026-08-10) +11. **Hub role: registrar + signaling ONLY, never in data path** ✅ (reinforced 2026-08-10) +12. **Chat stored on nodes, not hub** ✅ (decided 2026-08-10) +13. **Web UI: Preact SPA, dark/light, responsive, i18n** ✅ (decided 2026-08-10) +14. **Site overlay: meshbay.org-specific pages separate from generic hub** ✅ (decided 2026-08-10) |