From 4694a3098b53b54d2e66c6726e9d19d0193640dc Mon Sep 17 00:00:00 2001 From: Christian Nennemann Date: Wed, 4 Mar 2026 02:10:20 +0100 Subject: [PATCH] docs: comprehensive update for sprints 1-9 Update README, ROADMAP, and mdBook to reflect all sprint deliverables: rich messaging, file transfer, disappearing messages, Go/TypeScript SDKs, C FFI, mesh networking (identity, store-and-forward, broadcast), and security hardening. Add 6 new mdBook guides (REPL reference, Go SDK, TypeScript SDK + browser demo, rich messaging, file transfer, mesh networking). Check off 16 completed ROADMAP items across phases 3-9. --- README.md | 108 +++++++++--- ROADMAP.md | 145 +++++++-------- docs/src/SUMMARY.md | 8 +- docs/src/getting-started/demo-walkthrough.md | 7 +- docs/src/getting-started/file-transfer.md | 114 ++++++++++++ docs/src/getting-started/go-sdk.md | 152 ++++++++++++++++ docs/src/getting-started/mesh-networking.md | 175 ++++++++++++++++++ docs/src/getting-started/repl-reference.md | 124 +++++++++++++ docs/src/getting-started/rich-messaging.md | 94 ++++++++++ docs/src/getting-started/typescript-sdk.md | 176 +++++++++++++++++++ docs/src/getting-started/wasm.md | 22 ++- docs/src/introduction.md | 45 ++++- docs/src/wire-format/node-service-schema.md | 48 +++-- 13 files changed, 1084 insertions(+), 134 deletions(-) create mode 100644 docs/src/getting-started/file-transfer.md create mode 100644 docs/src/getting-started/go-sdk.md create mode 100644 docs/src/getting-started/mesh-networking.md create mode 100644 docs/src/getting-started/repl-reference.md create mode 100644 docs/src/getting-started/rich-messaging.md create mode 100644 docs/src/getting-started/typescript-sdk.md diff --git a/README.md b/README.md index 17add3a..250a752 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # QPQ — quicproquo -[![CI](https://github.com/nickvidal/quicproquo/actions/workflows/ci.yml/badge.svg)](https://github.com/nickvidal/quicproquo/actions/workflows/ci.yml) +[![CI](https://github.com/xorwell/quicproquo/actions/workflows/ci.yml/badge.svg)](https://github.com/xorwell/quicproquo/actions/workflows/ci.yml) > End-to-end encrypted messaging over **QUIC + TLS 1.3 + MLS** (RFC 9420), written in Rust. @@ -43,12 +43,16 @@ agreement across any number of participants. Messages are framed with ### Core -- **Interactive REPL** — multi-conversation chat with auto-register, auto-login, slash commands, background polling, and message history +- **Interactive REPL** — multi-conversation chat with auto-register, auto-login, 40+ slash commands, background polling, and message history - **1:1 DMs** — dedicated channels with server-enforced membership authorization - **Multi-party groups** — N-member MLS groups with Commit fan-out and epoch sync - **OPAQUE authentication** — password-authenticated key exchange (password never leaves the client) - **Encrypted local storage** — SQLCipher database + encrypted session tokens (Argon2id + ChaCha20-Poly1305) - **Persistent state** — server and client survive restarts; SQLite/SQLCipher or file-backed storage +- **Rich messaging** — reactions, read receipts, typing indicators, message editing, message deletion +- **File transfer** — chunked upload/download with SHA-256 content addressing, MIME detection, 50 MB limit +- **Disappearing messages** — per-conversation TTL with server-side GC (`/disappear 30m`, `1h`, `1d`, `7d`) +- **Account deletion** — transactional purge of all user data, sessions, and channel memberships (GDPR-ready) - **Self-DM notepad** — send messages to yourself (local-only, no server round-trip) - **Certificate pinning** — pass the server cert as `--ca-cert` to trust only that server - **Federation** — server-to-server message relay via Cap'n Proto RPC over QUIC with mTLS @@ -58,7 +62,14 @@ agreement across any number of participants. Messages are framed with - **Dynamic plugin system** — load `.so`/`.dylib` plugins at runtime via `--plugin-dir` - **Safety numbers** — `/verify ` for out-of-band key verification (60-digit numeric code) - **Transcript export** — encrypted, tamper-evident message archives with hash-chain integrity verification -- **20 CLI subcommands** — `register-user`, `login`, `create-group`, `invite`, `join`, `send`, `recv`, `chat`, `repl`, `export`, `export-verify`, and more +- **MLS key rotation** — `/update-key` rotates MLS leaf node material with epoch advancement + +### Client SDKs + +- **Go SDK** (`sdks/go/`) — native QUIC transport via `quic-go`, Cap'n Proto RPC, full API: connect, OPAQUE auth, send/receive, disappearing messages, account deletion +- **TypeScript SDK** (`sdks/typescript/`) — `@quicproquo/client` with WASM crypto (175 KB), WebSocket transport, offline crypto mode, browser demo +- **Python FFI** (`examples/python/`) — `ctypes` wrapper over the C FFI library with CLI +- **C FFI** (`crates/quicproquo-ffi/`) — `libquicproquo_ffi.so` with 7 extern functions: connect, login, send, receive, disconnect, last_error, free_string ### REPL slash commands @@ -72,21 +83,51 @@ agreement across any number of participants. Messages are framed with | `/leave` | Leave the current group | | `/switch @user` or `/switch #group` | Switch active conversation | | `/list` or `/ls` | List all conversations | -| `/members` | Show group members | +| `/members` | Show group members with resolved usernames | +| `/group-info` (or `/gi`) | Show group type, members, MLS epoch | +| `/rename ` | Rename the current conversation | | `/history [count]` (or `/hist`) | Show message history (default 20) | +| `/react [index]` | React to a message with an emoji | +| `/typing` | Send a typing indicator | +| `/typing-notify on\|off` | Toggle typing indicator display | +| `/edit ` | Edit one of your messages | +| `/delete ` | Delete one of your messages | +| `/send-file ` (or `/sf`) | Upload and send a file (chunked, SHA-256 verified) | +| `/download ` (or `/dl`) | Download a received file | +| `/disappear ` | Set message TTL (`30m`, `1h`, `1d`, `7d`) | | `/verify ` | Compare safety numbers with a peer | | `/update-key` (or `/rotate-key`) | Rotate your MLS key material | -| `/mesh peers` | Scan for nearby qpq nodes via mDNS | -| `/mesh server ` | Note a discovered server address | +| `/delete-account` | Permanently delete your account (with confirmation) | | `/whoami` | Show identity and group status | | `/help` | Command reference | | `/quit` | Exit | +**Mesh commands** (requires `--features mesh`): + +| Command | Description | +|---|---| +| `/mesh peers` | Scan for nearby qpq nodes via mDNS | +| `/mesh server ` | Note a discovered server address | +| `/mesh send ` | Direct P2P message via iroh | +| `/mesh broadcast ` | Publish to a broadcast channel | +| `/mesh subscribe ` | Join a broadcast channel | +| `/mesh route` | Show routing table | +| `/mesh identity` | Show mesh identity info | +| `/mesh store` | Show store-and-forward stats | + +### Mesh networking (feature-gated: `--features mesh`) + +- **P2P transport** (`quicproquo-p2p`) — iroh-based direct peer-to-peer messaging with NAT traversal +- **Self-sovereign identity** — Ed25519 keypair-based mesh identity, independent of server registration +- **Store-and-forward** — TTL-based message buffering with hop counting and deduplication +- **Broadcast channels** — ChaCha20-Poly1305 symmetric topic-based pub/sub (no MLS overhead) +- **mDNS discovery** — servers announce `_quicproquo._udp.local.`; clients auto-discover nearby nodes +- **Federation routing** — server-to-server message relay with mTLS + ### Experimental / proof-of-concept - **Tauri 2 GUI** (`quicproquo-gui`) — foundational desktop app shell; not feature-complete - **Mobile FFI** (`quicproquo-mobile`) — C API for QUIC connection migration (wifi to cellular) -- **P2P transport** (`quicproquo-p2p`) — iroh-based direct peer-to-peer messaging with NAT traversal (feature-gated behind `--features mesh`) - **Bot framework** (`quicproquo-bot`) — programmable bot client --- @@ -161,17 +202,18 @@ See the [full demo walkthrough](docs/src/getting-started/demo-walkthrough.md) fo | Crate | Purpose | |---|---| -| `quicproquo-core` | MLS group operations, hybrid KEM, OPAQUE auth, crypto primitives | +| `quicproquo-core` | MLS group operations, hybrid KEM, OPAQUE auth, crypto primitives, WASM-compatible modules | | `quicproquo-proto` | Cap'n Proto schemas and generated RPC code | -| `quicproquo-server` | QUIC server, NodeService RPC, storage backends, federation, plugins | -| `quicproquo-client` | CLI + REPL, session management, conversation store | -| `quicproquo-plugin-api` | C-compatible plugin hook API (`HookVTable`) | +| `quicproquo-server` | QUIC server, NodeService RPC (24 methods), storage backends, federation, plugins, blob storage | +| `quicproquo-client` | CLI + REPL (40+ commands), session management, conversation store, file transfer | +| `quicproquo-ffi` | C FFI bindings (`libquicproquo_ffi.so`) for cross-language integration | +| `quicproquo-plugin-api` | C-compatible plugin hook API (`HookVTable`, 6 hooks) | | `quicproquo-kt` | Key transparency / Merkle-log identity bindings | | `quicproquo-bot` | Programmable bot client framework | | `quicproquo-gen` | Code generation utilities | | `quicproquo-gui` | Tauri 2 desktop app (experimental, requires GTK) | | `quicproquo-mobile` | C FFI for mobile connection migration (experimental) | -| `quicproquo-p2p` | iroh-based P2P transport (feature-gated, `--features mesh`) | +| `quicproquo-p2p` | iroh-based P2P transport, mesh identity, store-and-forward, broadcast channels | --- @@ -181,7 +223,7 @@ GitHub Actions runs on every push and PR: - `cargo fmt --check` — formatting - `cargo build --workspace` — full build -- `cargo test --workspace` — 103+ tests (core, server, client, E2E, doctests) +- `cargo test --workspace` — 130+ tests (core, server, client, E2E, P2P, doctests) - `cargo clippy --workspace` — lint - `cargo deny check` — license and advisory audit - `cargo audit` — vulnerability scan @@ -214,23 +256,27 @@ See [ROADMAP.md](ROADMAP.md) for the full phased plan. Summary: |-------|-------|--------| | 1 | Production hardening (unwrap removal, secure defaults, Docker) | In progress | | 2 | Test and CI maturity | Partially done | -| 3 | Client SDKs (Go, Python, WASM, FFI, WebTransport) | Planned | -| 4 | Trust and security (audit, key transparency, PQ MLS) | Planned | -| 5 | Features and UX (multi-device, offline queue, file transfer) | Planned | +| 3 | Client SDKs (Go, TypeScript/WASM, Python FFI, C FFI) | **Go, TS, FFI, WASM done** | +| 4 | Trust and security (audit, key transparency, PQ MLS) | DS auth + enumeration mitigation done | +| 5 | Features and UX (rich messaging, file transfer, disappearing) | **Edit/delete, files, TTL done** | | 6 | Scale and operations (horizontal scaling, observability) | Planned | -| 7 | Platform expansion (mobile, web, federation, sealed sender) | Planned | -| 8 | Freifunk / community mesh networking | F0-F2 done | -| 9 | Developer experience and community growth | Planned | +| 7 | Platform expansion (mobile, web, federation, sealed sender) | **Sealed sender done** | +| 8 | Freifunk / community mesh networking | **F0-F6 done** | +| 9 | Developer experience and community growth | Safety numbers + plugins done | -### Recently completed +### Recently completed (Sprints 1-9) -- **Federation routing** — server-to-server message relay with mTLS -- **mDNS discovery** — servers advertise on local network, clients discover peers -- **P2P transport** — iroh-based direct messaging re-included in workspace (`--features mesh`) -- **CI pipeline** — fmt, build, test, clippy, deny, audit, coverage, Docker build -- **Plugin system** — dynamic `.so`/`.dylib` loading with C-compatible hook API -- **Safety numbers** — Signal-style 60-digit verification codes -- **Transcript export** — encrypted, hash-chained message archives +- **Rich messaging** — reactions, read receipts, typing indicators, edit/delete messages +- **File transfer** — chunked upload/download with SHA-256 content addressing and progress bars +- **Disappearing messages** — per-conversation TTL with server-side garbage collection +- **Account deletion** — transactional purge of all user data (GDPR-ready) +- **Go SDK** — native QUIC + Cap'n Proto client with full API coverage +- **TypeScript SDK** — WASM crypto (175 KB) + WebSocket transport + browser demo +- **C FFI + Python bindings** — cross-language integration via `libquicproquo_ffi` +- **Mesh networking** — self-sovereign identity, store-and-forward, broadcast channels, extended REPL +- **Security hardening** — DS sender binding, username enumeration mitigation, MLS key rotation +- **CI pipeline** — fmt, build, test, clippy, deny, audit, tarpaulin coverage, Docker build +- **Plugin system** — dynamic `.so`/`.dylib` loading with 6 C-compatible hook points --- @@ -259,12 +305,18 @@ cargo install mdbook # once mdbook serve docs # http://localhost:3000 ``` +- **[Getting Started](docs/src/getting-started/prerequisites.md)** — build, run, demo walkthrough +- **[REPL Command Reference](docs/src/getting-started/repl-reference.md)** — complete list of 40+ commands +- **[Go SDK Guide](docs/src/getting-started/go-sdk.md)** — native QUIC + Cap'n Proto client +- **[TypeScript SDK & Browser Demo](docs/src/getting-started/typescript-sdk.md)** — WASM crypto + WebSocket transport +- **[Rich Messaging](docs/src/getting-started/rich-messaging.md)** — reactions, typing, edit/delete, receipts +- **[File Transfer](docs/src/getting-started/file-transfer.md)** — chunked upload/download with SHA-256 +- **[Mesh Networking](docs/src/getting-started/mesh-networking.md)** — P2P, broadcast, store-and-forward - **[Architecture Overview](docs/src/architecture/overview.md)** — two-service model, dual-key design, crate layout - **[Protocol Deep Dives](docs/src/protocol-layers/overview.md)** — QUIC/TLS 1.3, Cap'n Proto, MLS, Hybrid KEM - **[Cryptographic Properties](docs/src/cryptography/overview.md)** — forward secrecy, post-compromise security, PQ readiness, threat model - **[Design Rationale](docs/src/design-rationale/overview.md)** — why MLS over Signal/Matrix, ADRs for key decisions - **[Wire Format Reference](docs/src/wire-format/overview.md)** — annotated Cap'n Proto schemas -- **[Getting Started](docs/src/getting-started/prerequisites.md)** — build, run, demo walkthrough - **[Roadmap](docs/src/roadmap/milestones.md)** — milestones, production readiness, future research - **[Future Improvements](docs/FUTURE-IMPROVEMENTS.md)** — prioritised list of security, ops, and feature improvements diff --git a/ROADMAP.md b/ROADMAP.md index cfa5e91..d1408cd 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -121,14 +121,11 @@ WASM/FFI for the crypto layer. ### Implementation -- [ ] **3.1 Go SDK (`quicproquo-go`)** - - Generate Go types: `capnp compile -ogo schemas/node.capnp` - - QUIC transport: `quic-go` with TLS 1.3 + ALPN `"capnp"` - - Cap'n Proto RPC framing over QUIC bidirectional stream - - Auth context: bearer token + session management - - Retry with exponential backoff (mirror Rust client pattern) - - Publish: `go get git.xorwell.de/c/quicproquo-go` - - Example: CLI client matching Rust feature set +- [x] **3.1 Go SDK (`quicproquo-go`)** + - Generated Go types from `node.capnp` (6487-line codegen, all 24 RPC methods) + - QUIC transport via `quic-go` with TLS 1.3 + ALPN `"capnp"` + - High-level `qpq` package: Connect, Health, ResolveUser, CreateChannel, Send/SendWithTTL, Receive/ReceiveWait, DeleteAccount, OPAQUE auth + - Example CLI in `sdks/go/cmd/example/` - [ ] **3.2 Python SDK (`quicproquo-py`)** - QUIC transport: `aioquic` with custom Cap'n Proto stream handler @@ -139,24 +136,16 @@ WASM/FFI for the crypto layer. - Publish: PyPI `quicproquo` - Example: async bot client -- [ ] **3.3 C FFI layer (`quicproquo-ffi`)** - - New crate in workspace: `crates/quicproquo-ffi` - - `cbindgen` to generate `quicproquo.h` C header - - Crypto functions: `qpc_identity_new()`, `qpc_group_create()`, - `qpc_encrypt()`, `qpc_decrypt()`, `qpc_key_package_generate()` - - Transport functions: `qpc_connect()`, `qpc_enqueue()`, `qpc_fetch()`, - `qpc_fetch_wait()` (bundles QUIC + Cap'n Proto internally) - - Memory: caller-allocated buffers with length, no ownership transfer - - Builds as `libquicproquo.so` / `.dylib` / `.dll` - - Swift and Kotlin wrapper examples using the C header +- [x] **3.3 C FFI layer (`quicproquo-ffi`)** + - `crates/quicproquo-ffi` with 7 extern "C" functions: connect, login, send, receive, disconnect, last_error, free_string + - Builds as `libquicproquo_ffi.so` / `.dylib` / `.dll` + - Python ctypes wrapper in `examples/python/qpq_client.py` -- [ ] **3.4 WASM compilation of `quicproquo-core`** - - `wasm-pack build` target for browser + Node.js - - Crypto-only: `GroupMember`, `IdentityKeypair`, `AppMessage`, - `hybrid_encrypt/decrypt`, `generate_key_package` - - Transport NOT included (browsers use WebTransport, see Phase 3.5) - - Publish to npm: `@quicproquo/core` - - TypeScript type definitions auto-generated via `wasm-bindgen` +- [x] **3.4 WASM compilation of `quicproquo-core`** + - `wasm-pack build` target producing 175 KB WASM bundle (LTO + opt-level=s) + - 13 `wasm_bindgen` functions: Ed25519 identity, hybrid KEM, safety numbers, sealed sender, padding + - Browser-ready with `crypto.getRandomValues()` RNG + - Published as `sdks/typescript/wasm-crypto/` - [ ] **3.5 WebTransport server endpoint** - Add HTTP/3 + WebTransport listener to server (same QUIC stack via quinn) @@ -167,13 +156,11 @@ WASM/FFI for the crypto layer. - Configurable port: `--webtransport-listen 0.0.0.0:7443` - Feature-flagged: `--features webtransport` -- [ ] **3.6 TypeScript/JavaScript SDK (`@quicproquo/client`)** - - WebTransport for QUIC connectivity (no HTTP fallback) - - WASM module (Phase 3.4) for MLS crypto - - Cap'n Proto serialization via WASM bridge - - Handles: auth flow, key upload, message send/receive, group management - - Publish to npm: `@quicproquo/client` - - Example: browser chat UI +- [x] **3.6 TypeScript/JavaScript SDK (`@quicproquo/client`)** + - `QpqClient` class: connect, offline, health, resolveUser, createChannel, send/sendWithTTL, receive, deleteAccount + - WASM crypto wrapper: generateIdentity, sign/verify, hybridEncrypt/Decrypt, computeSafetyNumber, sealedSend, pad + - WebSocket transport with request/response correlation and reconnection + - Browser demo: interactive crypto playground + chat UI (`sdks/typescript/demo/index.html`) - [ ] **3.7 SDK documentation and schema publishing** - Publish `.capnp` schemas as the canonical API contract @@ -199,11 +186,10 @@ Address the security gaps required for real-world deployment. - Users can verify peer keys haven't been substituted (MITM detection) - Revocation mechanism for compromised keys -- [ ] **4.3 Client authentication on Delivery Service** - - Currently server trusts claimed identity key on enqueue - - Bind enqueue operations to the authenticated session's identity key - - Prevent: client A fetching/sending as client B's identity - - Backward compat: sealed_sender mode for anonymous enqueue +- [x] **4.3 Client authentication on Delivery Service** + - DS sender identity binding with explicit audit logging + - `sender_prefix` tracking in enqueue/batch_enqueue RPCs + - Sender identity derived from authenticated session - [ ] **4.4 M7 — Post-quantum MLS integration** - Integrate hybrid KEM (X25519 + ML-KEM-768) into the OpenMLS crypto provider @@ -211,9 +197,9 @@ Address the security gaps required for real-world deployment. - Full test suite with PQ ciphersuite - Ref: existing `hybrid_kem.rs` and `hybrid_crypto.rs` -- [ ] **4.5 Username enumeration mitigation** - - Constant-time or uniform response for unknown users during OPAQUE login - - Prevent timing side-channels that reveal user existence +- [x] **4.5 Username enumeration mitigation** + - 5 ms timing floor on `resolveUser` responses + - Rate limiting to prevent bulk enumeration attacks --- @@ -238,15 +224,17 @@ Make it a product people want to use. - Explicit proposal handling (queue proposals, batch commit) - Group metadata (name, description, avatar hash) -- [ ] **5.4 Message editing and deletion** - - New `AppMessage` variants: `Edit { target_seq, new_content }`, `Delete { target_seq }` - - Client-side tombstones, server doesn't know about edits +- [x] **5.4 Message editing and deletion** + - `Edit` (0x06) and `Delete` (0x07) message types in `AppMessage` + - `/edit ` and `/delete ` REPL commands (own messages only) + - Database update/removal on incoming edit/delete -- [ ] **5.5 File and media transfer** - - Upload encrypted blob → get content hash - - Share hash + symmetric key inside MLS message - - Download by hash, decrypt client-side - - Size limits, content-type validation +- [x] **5.5 File and media transfer** + - `uploadBlob` / `downloadBlob` RPCs with 256 KB chunked streaming + - SHA-256 content-addressable storage with hash verification + - `FileRef` (0x08) message type with blob_id, filename, file_size, mime_type + - `/send-file ` and `/download ` REPL commands with progress bars + - 50 MB max file size, automatic MIME detection via `mime_guess` - [ ] **5.6 Abuse prevention and moderation** - Block user (client-side, suppress display) @@ -327,10 +315,10 @@ Long-term vision for wide adoption. - MLS group spanning multiple servers - Trust model for federated deployments -- [ ] **7.4 Sealed Sender** +- [x] **7.4 Sealed Sender** - Sender identity inside MLS ciphertext only (server can't see who sent) - - Requires: sender certificate + encrypted sender proof - - Ref: Signal's Sealed Sender design + - `sealed_sender` module in quicproquo-core with seal/unseal API + - WASM-accessible via `wasm_bindgen` for browser use - [ ] **7.5 Additional language SDKs** - Java/Kotlin: JNI bindings to C FFI (Phase 3.3) + native QUIC (netty-quic) @@ -387,28 +375,29 @@ functions without any central infrastructure or internet uplink. - REPL commands: `/mesh peers` (scan + list), `/mesh server ` (note address) - Nodes announce: `ver=1`, `server=`, `domain=` TXT records -- [ ] **F3 — Self-sovereign mesh identity** - - Keypair = identity; OPAQUE password auth becomes optional (opt-in for managed deployments) - - `--mesh` startup mode: no AS required, nodes accept any verifiable keypair - - Bootstrap trust via out-of-band key fingerprint exchange (QR code or short code) +- [x] **F3 — Self-sovereign mesh identity** + - Ed25519 keypair-based identity independent of AS registration + - JSON-persisted seed + known peers directory + - Sign/verify operations for mesh authenticity (`crates/quicproquo-p2p/src/identity.rs`) -- [ ] **F4 — Store-and-forward with TTL** - - Add `ttl_secs: u32` to `Envelope` in `node.capnp` - - Relay nodes hold messages for offline peers up to TTL, then discard - - Gossip-style propagation: each hop decrements a hop counter - - Enables asynchronous messaging across intermittently connected mesh segments +- [x] **F4 — Store-and-forward with TTL** + - `MeshEnvelope` with TTL-based expiry, hop_count tracking, max_hops routing limit + - SHA-256 deduplication ID prevents relay loops + - Ed25519 signature verification on envelopes + - `MeshStore` in-memory queue with per-recipient capacity limits and TTL-based GC -- [ ] **F5 — Lightweight broadcast channels** - - No MLS overhead; symmetric group key distributed out-of-band - - Gossip delivery: node broadcasts to all peers, peers re-broadcast once - - Loop prevention via bloom filter on seen message IDs - - Suitable for community bulletin boards, emergency broadcasts on mesh +- [x] **F5 — Lightweight broadcast channels** + - Symmetric ChaCha20-Poly1305 encrypted channels (no MLS overhead) + - Topic-based pub/sub via `BroadcastChannel` and `BroadcastManager` + - Subscribe/unsubscribe, create, publish API on `P2pNode` -- [ ] **F6 — Extended `/mesh` REPL commands** - - `/mesh dm ` — direct message to peer by key fingerprint (P2P path) - - `/mesh broadcast ` — publish to a symmetric broadcast channel - - `/mesh auto` — auto-select server with lowest RTT from discovered peers - - Auto-reconnect: if current server unreachable, fall back to next discovered peer +- [x] **F6 — Extended `/mesh` REPL commands** + - `/mesh send ` — direct P2P message via iroh + - `/mesh broadcast ` — publish to broadcast channel + - `/mesh subscribe ` — join broadcast channel + - `/mesh route` — show routing table + - `/mesh identity` — show mesh identity info + - `/mesh store` — show store-and-forward statistics - [ ] **F7 — OpenWrt cross-compilation guide** - Musl static builds: `x86_64-unknown-linux-musl`, `armv7-unknown-linux-musleabihf`, `mips-unknown-linux-musl` @@ -436,10 +425,10 @@ and lower the barrier to entry for non-crypto developers. - CI publishes HTML benchmark reports as GitHub Actions artifacts - Citable numbers — no other project benchmarks MLS + PQ-KEM in Rust -- [ ] **9.2 Safety Numbers (key verification)** - - Derive a 60-digit numeric code from two identity keys (Signal-style) - - REPL `/verify ` command for out-of-band key verification - - Pure client-side — no server or wire format changes needed +- [x] **9.2 Safety Numbers (key verification)** + - 60-digit numeric code derived from two identity keys (Signal-style) + - `/verify ` REPL command for out-of-band verification + - Available in WASM via `compute_safety_number` binding - [ ] **9.3 Full-Screen TUI (Ratatui + Crossterm)** - `qpq tui` launches a full-screen terminal UI: message pane, input bar, @@ -464,11 +453,11 @@ and lower the barrier to entry for non-crypto developers. - Any client can independently audit the full identity history - Lightweight subset of RFC 9162 adapted for identity keys -- [ ] **9.7 Dynamic Server Plugin System** - - Server loads `.so`/`.dylib` plugins at runtime from config `[plugins]` section +- [x] **9.7 Dynamic Server Plugin System** + - Server loads `.so`/`.dylib` plugins at runtime via `--plugin-dir` - C-compatible `HookVTable` via `extern "C"` — plugins in any language - - Ships with Rust reference plugin + Python ctypes example - - Extends existing `ServerHooks` trait with dynamic dispatch + - 6 hook points: on_message_enqueue, on_batch_enqueue, on_auth, on_channel_created, on_fetch, on_user_registered + - Example plugins: logging plugin, rate limit plugin (512 KiB payload enforcement) - [ ] **9.8 PQ Noise Transport Layer** - Hybrid `Noise_XX + ML-KEM-768` handshake for post-quantum transport security diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index e0a66ca..ba21db5 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -17,13 +17,19 @@ - [Building from Source](getting-started/building.md) - [Running the Server](getting-started/running-the-server.md) - [Running the Client](getting-started/running-the-client.md) +- [REPL Command Reference](getting-started/repl-reference.md) +- [Rich Messaging](getting-started/rich-messaging.md) +- [File Transfer](getting-started/file-transfer.md) - [TLS in quicproquo](getting-started/tls.md) - [Certificate Lifecycle and CA-Signed TLS](getting-started/certificate-lifecycle.md) - [Docker Deployment](getting-started/docker.md) -- [Bot SDK](getting-started/bot-sdk.md) +- [Go SDK](getting-started/go-sdk.md) +- [TypeScript SDK and Browser Demo](getting-started/typescript-sdk.md) - [C FFI Bindings](getting-started/ffi.md) - [WASM Integration](getting-started/wasm.md) +- [Bot SDK](getting-started/bot-sdk.md) - [Code Generators (qpq-gen)](getting-started/generators.md) +- [Mesh Networking](getting-started/mesh-networking.md) - [Demo Walkthrough: Alice and Bob](getting-started/demo-walkthrough.md) --- diff --git a/docs/src/getting-started/demo-walkthrough.md b/docs/src/getting-started/demo-walkthrough.md index 754ca7c..75444e1 100644 --- a/docs/src/getting-started/demo-walkthrough.md +++ b/docs/src/getting-started/demo-walkthrough.md @@ -324,7 +324,12 @@ Ensure the client has access to the server's TLS certificate. By default, both s ## Next steps -- [Running the Client](running-the-client.md) -- full CLI reference +- [REPL Command Reference](repl-reference.md) -- complete list of 40+ slash commands +- [Rich Messaging](rich-messaging.md) -- reactions, typing indicators, edit/delete +- [File Transfer](file-transfer.md) -- chunked upload/download with SHA-256 verification +- [Go SDK](go-sdk.md) -- build Go applications against the qpq server +- [TypeScript SDK & Browser Demo](typescript-sdk.md) -- WASM crypto in the browser +- [Mesh Networking](mesh-networking.md) -- P2P, broadcast channels, store-and-forward - [MLS (RFC 9420)](../protocol-layers/mls.md) -- how the MLS group operations work - [GroupMember Lifecycle](../internals/group-member-lifecycle.md) -- internal state machine details - [Delivery Service Internals](../internals/delivery-service.md) -- how the DS queues and delivers messages diff --git a/docs/src/getting-started/file-transfer.md b/docs/src/getting-started/file-transfer.md new file mode 100644 index 0000000..92b645c --- /dev/null +++ b/docs/src/getting-started/file-transfer.md @@ -0,0 +1,114 @@ +# File Transfer + +quicproquo supports encrypted file transfer with chunked upload/download, +SHA-256 content addressing, and automatic MIME type detection. Files up to +50 MB are supported. + +## Sending a file + +``` +/send-file /path/to/document.pdf +``` + +Or use the short alias: + +``` +/sf /path/to/document.pdf +``` + +This will: + +1. Read the file and compute its SHA-256 hash +2. Upload the file in **256 KB chunks** via the `uploadBlob` RPC +3. Verify the hash on the server side +4. Send a `FileRef` message to the active conversation containing the + blob ID (SHA-256 hash), filename, file size, and MIME type + +A progress bar is displayed during upload. + +## Downloading a file + +``` +/download 7 +``` + +Or: + +``` +/dl 7 +``` + +Where `7` is the history index of the FileRef message. This will: + +1. Fetch the blob in chunks via the `downloadBlob` RPC +2. Verify the SHA-256 hash matches the FileRef's blob ID +3. Save the file to the current directory using the original filename + +If a file with the same name already exists, a suffix is appended +(`-1`, `-2`, etc.) to avoid overwriting. + +## How it works + +### Content-addressable storage + +Files are stored on the server by their SHA-256 hash (`blob_id`). This +means identical files are automatically deduplicated — uploading the same +file twice reuses the existing blob. + +### Chunked transfer + +Files are split into 256 KB chunks for upload and download. This: + +- Keeps memory usage bounded for large files +- Allows progress reporting +- Enables future resumable transfers + +### FileRef message type + +The `FileRef` (type code `0x08`) is sent as a regular MLS-encrypted message: + +``` +[blob_id: 32 bytes (SHA-256)] +[filename_len: 2 bytes BE][filename: UTF-8] +[file_size: 8 bytes BE] +[mime_len: 2 bytes BE][mime_type: UTF-8] +``` + +The server stores a JSON `.meta` sidecar alongside each blob with metadata. + +### MIME type detection + +MIME types are detected automatically using the `mime_guess` crate based on +file extension. For example: + +- `report.pdf` → `application/pdf` +- `photo.jpg` → `image/jpeg` +- `data.csv` → `text/csv` + +### Limits + +- **Maximum file size:** 50 MB +- **Chunk size:** 256 KB +- **Hash algorithm:** SHA-256 + +### RPC methods + +| Method | Ordinal | Description | +|---|---|---| +| `uploadBlob` | `@21` | Chunked file upload with SHA-256 content addressing | +| `downloadBlob` | `@22` | Chunked file download with hash verification | + +### Error codes + +| Code | Meaning | +|---|---| +| E024 | Blob upload failed | +| E025 | Blob download failed | +| E026 | Blob hash mismatch | +| E027 | Blob operation error | + +## Implementation + +- **Server:** `crates/quicproquo-server/src/node_service/blob_ops.rs` +- **Client REPL:** `/send-file` and `/download` in `crates/quicproquo-client/src/client/repl.rs` +- **Message type:** `FileRef` variant in `crates/quicproquo-core/src/app_message.rs` diff --git a/docs/src/getting-started/go-sdk.md b/docs/src/getting-started/go-sdk.md new file mode 100644 index 0000000..6e9191b --- /dev/null +++ b/docs/src/getting-started/go-sdk.md @@ -0,0 +1,152 @@ +# Go SDK + +The Go SDK (`sdks/go/`) provides a native QUIC + Cap'n Proto client for +quicproquo, giving Go applications full access to the messaging API without +any HTTP translation layer. + +## Prerequisites + +- Go 1.21+ +- A running qpq server + +## Installation + +```go +import "quicproquo.dev/sdk/go/qpq" +``` + +## Quick start + +```go +package main + +import ( + "context" + "fmt" + "log" + + "quicproquo.dev/sdk/go/qpq" +) + +func main() { + ctx := context.Background() + + // Connect to the server + client, err := qpq.Connect(ctx, qpq.Options{ + Addr: "127.0.0.1:7000", + InsecureSkipVerify: true, // development only + }) + if err != nil { + log.Fatal(err) + } + + // Health check + status, err := client.Health(ctx) + if err != nil { + log.Fatal(err) + } + fmt.Println("Server status:", status) + + // OPAQUE authentication + if err := client.LoginStart(ctx, "alice", "secret"); err != nil { + log.Fatal(err) + } + if err := client.LoginFinish(ctx); err != nil { + log.Fatal(err) + } + + // Resolve a peer + peerKey, err := client.ResolveUser(ctx, "bob") + if err != nil { + log.Fatal(err) + } + + // Create a DM channel + channelID, err := client.CreateChannel(ctx, peerKey) + if err != nil { + log.Fatal(err) + } + fmt.Println("Channel:", channelID) + + // Send a message + if err := client.Send(ctx, peerKey, []byte("Hello from Go!")); err != nil { + log.Fatal(err) + } + + // Send a disappearing message (1 hour TTL) + if err := client.SendWithTTL(ctx, peerKey, []byte("This vanishes"), 3600); err != nil { + log.Fatal(err) + } + + // Receive messages + msgs, err := client.Receive(ctx, peerKey) + if err != nil { + log.Fatal(err) + } + for _, m := range msgs { + fmt.Println("Received:", string(m)) + } +} +``` + +## API reference + +| Method | Description | +|---|---| +| `Connect(ctx, opts)` | Establish QUIC connection to server | +| `Health(ctx)` | Server readiness probe | +| `RegisterStart/Finish(ctx, user, pass)` | OPAQUE registration | +| `LoginStart/Finish(ctx, user, pass)` | OPAQUE login | +| `ResolveUser(ctx, username)` | Look up a user's identity key | +| `CreateChannel(ctx, peerKey)` | Create a 1:1 DM channel | +| `Send(ctx, recipientKey, payload)` | Send a message | +| `SendWithTTL(ctx, recipientKey, payload, ttlSecs)` | Send a disappearing message | +| `Receive(ctx, recipientKey)` | Fetch queued messages | +| `ReceiveWait(ctx, recipientKey, timeoutMs)` | Long-poll for messages (default 5 s timeout) | +| `DeleteAccount(ctx)` | Delete the authenticated account | + +## Transport details + +- **Protocol:** QUIC with TLS 1.3 via `quic-go` +- **ALPN:** `"capnp"` (same as the Rust client) +- **Serialisation:** Cap'n Proto RPC over a single bidirectional stream +- **Idle timeout:** 300 seconds +- **Auth:** Session token stored in client, sent with every RPC via the `Auth` struct + +## Connection options + +```go +type Options struct { + Addr string // "host:port" + CACertPath string // path to CA cert (DER format) + InsecureSkipVerify bool // skip TLS verification (dev only!) + Token string // pre-existing auth token (optional) +} +``` + +## Project structure + +``` +sdks/go/ +├── proto/ # Generated Cap'n Proto types from node.capnp +├── transport/ # QUIC transport layer (quic-go + TLS 1.3) +├── qpq/ # High-level client API (QpqClient) +├── cmd/example/ # Example CLI program +├── go.mod +└── README.md +``` + +## Running the example + +```bash +cd sdks/go/cmd/example +go run main.go +``` + +## Regenerating proto types + +If you modify `schemas/node.capnp`, regenerate the Go types: + +```bash +capnp compile -ogo sdks/go/proto/node.capnp +``` diff --git a/docs/src/getting-started/mesh-networking.md b/docs/src/getting-started/mesh-networking.md new file mode 100644 index 0000000..35fd98f --- /dev/null +++ b/docs/src/getting-started/mesh-networking.md @@ -0,0 +1,175 @@ +# Mesh Networking + +quicproquo includes a mesh networking layer for decentralised, peer-to-peer +messaging without central infrastructure. It is designed for community +networks (Freifunk, BATMAN-adv, Babel routing) and offline-capable +environments. + +Mesh features are **feature-gated** — build the client with: + +```bash +cargo build -p quicproquo-client --features mesh +``` + +## Architecture + +``` +Client A ── mDNS discovery ──► nearby qpq node (LAN / mesh) + │ + Cap'n Proto federation + │ + remote qpq node (across mesh) + +Client B ── iroh P2P ──────► Client C (direct, NAT-traversed) +``` + +Three layers work together: + +1. **Server federation** — servers relay messages across mesh via Cap'n Proto + RPC over QUIC with mTLS +2. **mDNS discovery** — servers announce themselves, clients find nearby nodes +3. **iroh P2P** — direct peer-to-peer QUIC connections with NAT traversal + +## Self-sovereign mesh identity + +In mesh mode, identity is based on an Ed25519 keypair — independent of server +registration. No OPAQUE password auth or Authentication Service is required. + +``` +/mesh identity +``` + +Shows your mesh public key and known peers. The keypair and peer directory are +persisted in a local JSON file. + +## mDNS discovery + +Servers announce `_quicproquo._udp.local.` via mDNS on startup with TXT +records: + +``` +ver=1 +server= +domain= +``` + +Clients browse for nearby nodes: + +``` +/mesh peers +``` + +Note a discovered server for connection: + +``` +/mesh server 192.168.1.42:7000 +``` + +## Direct P2P messaging + +Send messages directly to a peer via iroh transport (QUIC with NAT +traversal): + +``` +/mesh send Hello from the mesh! +``` + +Messages are signed with your mesh identity key. The iroh relay server +is used as fallback when direct connections fail. + +## Store-and-forward + +Messages for offline peers are buffered with TTL-based expiry: + +- **MeshEnvelope** — signed message with TTL, hop count, and max hops +- **Deduplication** — SHA-256 message ID prevents relay loops +- **MeshStore** — in-memory queue with per-recipient capacity limits +- **Garbage collection** — expired messages are automatically purged + +Forward stored messages to a newly-discovered peer: + +``` +/mesh store # show queue statistics +``` + +The routing table shows known paths: + +``` +/mesh route +``` + +## Broadcast channels + +Lightweight pub/sub channels using symmetric ChaCha20-Poly1305 encryption +(no MLS overhead). Suitable for community bulletin boards, announcements, +and emergency broadcasts. + +``` +/mesh subscribe announcements # join a channel +/mesh broadcast announcements Hello! # publish to a channel +``` + +Channels are created automatically on first subscription. The symmetric key is +derived from the topic name (for open channels) or distributed out-of-band +(for private channels). + +## Federation + +Servers relay messages for recipients on remote nodes: + +- `handle_enqueue` and `handle_batch_enqueue` call + `federation::routing::resolve_destination()` +- Remote recipients are forwarded via `FederationClient::relay_enqueue()` +- mTLS mutual authentication between federation peers + +### Configuration + +```bash +# Environment variables +QPQ_FEDERATION_LISTEN=0.0.0.0:7001 +QPQ_LOCAL_DOMAIN=node1.mesh.local +QPQ_FEDERATION_CERT=/path/to/cert.der +QPQ_FEDERATION_KEY=/path/to/key.der +QPQ_FEDERATION_CA=/path/to/ca.der +``` + +Or in `qpq-server.toml`: + +```toml +federation_enabled = true +federation_domain = "node1.mesh.local" +federation_listen = "0.0.0.0:7001" +``` + +### ALPN tokens + +| Protocol | ALPN | +|---|---| +| Client ↔ Server | `b"capnp"` | +| P2P transport | `b"quicproquo/p2p/1"` | +| Federation | `b"quicproquo/federation/1"` | + +## REPL command summary + +| Command | Description | +|---|---| +| `/mesh peers` | Scan for nearby nodes via mDNS | +| `/mesh server ` | Note a discovered server | +| `/mesh send ` | Direct P2P message | +| `/mesh broadcast ` | Publish to broadcast channel | +| `/mesh subscribe ` | Join a broadcast channel | +| `/mesh route` | Show routing table | +| `/mesh identity` | Show mesh identity info | +| `/mesh store` | Show store-and-forward stats | + +## Implementation + +- **P2P node:** `crates/quicproquo-p2p/src/lib.rs` — `P2pNode` with iroh + transport +- **Mesh identity:** `crates/quicproquo-p2p/src/identity.rs` +- **Store-and-forward:** `crates/quicproquo-p2p/src/store.rs` + + `envelope.rs` +- **Broadcast:** `crates/quicproquo-p2p/src/broadcast.rs` +- **mDNS discovery:** `crates/quicproquo-client/src/client/mesh_discovery.rs` +- **Federation routing:** `crates/quicproquo-server/src/node_service/delivery.rs` +- **REPL commands:** mesh handlers in `crates/quicproquo-client/src/client/repl.rs` diff --git a/docs/src/getting-started/repl-reference.md b/docs/src/getting-started/repl-reference.md new file mode 100644 index 0000000..5fd1263 --- /dev/null +++ b/docs/src/getting-started/repl-reference.md @@ -0,0 +1,124 @@ +# REPL Command Reference + +The qpq interactive REPL provides 40+ slash commands for messaging, group +management, file transfer, privacy controls, and mesh networking. Launch it +with: + +```bash +cargo run --bin qpq -- repl --username alice --password mypass +``` + +Type any text without a leading `/` to send a message in the active +conversation. + +--- + +## Account and identity + +| Command | Alias | Description | +|---|---|---| +| `/whoami` | | Show your identity key, active conversation, and group status | +| `/verify ` | | Compare 60-digit safety numbers with a peer for out-of-band key verification | +| `/update-key` | `/rotate-key` | Rotate your MLS leaf node key material (proposes self-update, auto-commits, fans out to all members) | +| `/delete-account` | | Permanently delete your account with confirmation prompt. Purges all user data, keys, deliveries, and channel memberships from the server | + +--- + +## Conversations + +| Command | Alias | Description | +|---|---|---| +| `/dm ` | | Start or switch to a 1:1 DM with a peer. Creates the channel on first use | +| `/create-group ` | `/cg` | Create a new MLS group | +| `/invite ` | | Add a member to the current group (fetches their KeyPackage, sends Welcome) | +| `/remove ` | | Remove a member from the current group | +| `/join` | | Accept a pending group invitation (processes Welcome message) | +| `/leave` | | Leave the current group | +| `/switch @user` or `/switch #group` | | Switch the active conversation | +| `/list` | `/ls` | List all conversations | +| `/members` | | Show group members with resolved usernames | +| `/group-info` | `/gi` | Show group type, member list, and current MLS epoch | +| `/rename ` | | Rename the current conversation | +| `/history [count]` | `/hist` | Show message history (default: 20 messages) | + +--- + +## Messaging + +| Command | Alias | Description | +|---|---|---| +| *(plain text)* | | Send a message in the active conversation | +| `/react [index]` | | React to a message with an emoji. Omit index to react to the latest message | +| `/typing` | | Send a typing indicator to the active conversation | +| `/typing-notify on\|off` | | Toggle display of other users' typing indicators | +| `/edit ` | | Edit one of your own messages (by history index) | +| `/delete ` | | Delete one of your own messages (by history index) | + +--- + +## File transfer + +| Command | Alias | Description | +|---|---|---| +| `/send-file ` | `/sf` | Upload a file in 256 KB chunks (SHA-256 verified), send a FileRef to the conversation. Max 50 MB. MIME type auto-detected | +| `/download ` | `/dl` | Download a received file by message index. Verifies SHA-256 hash on completion. Saves to current directory with collision avoidance | + +--- + +## Privacy and disappearing messages + +| Command | Alias | Description | +|---|---|---| +| `/disappear ` | | Set message TTL for the active conversation. Duration format: `30m`, `1h`, `1d`, `7d`. Messages are deleted by the server after expiry | +| `/disappear off` | | Disable disappearing messages for the conversation | + +--- + +## Mesh networking + +These commands require the client to be built with mesh support: + +```bash +cargo build -p quicproquo-client --features mesh +``` + +| Command | Description | +|---|---| +| `/mesh peers` | Scan for nearby qpq nodes via mDNS (`_quicproquo._udp.local.`) | +| `/mesh server ` | Note a discovered server address for connection | +| `/mesh send ` | Send a direct P2P message via iroh transport | +| `/mesh broadcast ` | Publish a message to a broadcast channel | +| `/mesh subscribe ` | Join a topic-based broadcast channel | +| `/mesh route` | Display the current routing table | +| `/mesh identity` | Show your mesh identity (Ed25519 public key, known peers) | +| `/mesh store` | Show store-and-forward queue statistics | + +--- + +## System + +| Command | Alias | Description | +|---|---|---| +| `/help` | `/h` | Show command reference | +| `/quit` | `/q`, `/exit` | Exit the REPL | + +--- + +## Message types on the wire + +Every application message uses a `[version: 1][type: 1][payload...]` binary +format inside the MLS ciphertext: + +| Type | Code | Wire format | +|---|---|---| +| Chat | `0x01` | `[msg_id: 16][body_len: 2 BE][body]` | +| Reply | `0x02` | `[ref_msg_id: 16][body_len: 2 BE][body]` | +| Reaction | `0x03` | `[ref_msg_id: 16][emoji_len: 1][emoji]` | +| ReadReceipt | `0x04` | `[msg_id: 16]` | +| Typing | `0x05` | `[active: 1]` (0 = stopped, 1 = typing) | +| Edit | `0x06` | `[ref_msg_id: 16][body_len: 2 BE][body]` | +| Delete | `0x07` | `[ref_msg_id: 16]` | +| FileRef | `0x08` | `[blob_id: 32][filename_len: 2 BE][filename][file_size: 8 BE][mime_len: 2 BE][mime_type]` | + +Read receipts are sent automatically when a Chat or Reply message is received. +Typing indicators time out after 10 seconds of inactivity. diff --git a/docs/src/getting-started/rich-messaging.md b/docs/src/getting-started/rich-messaging.md new file mode 100644 index 0000000..9b6e939 --- /dev/null +++ b/docs/src/getting-started/rich-messaging.md @@ -0,0 +1,94 @@ +# Rich Messaging + +quicproquo supports rich messaging features beyond basic text: reactions, read +receipts, typing indicators, message editing, and message deletion. All +message types are end-to-end encrypted inside MLS ciphertext — the server +only sees opaque bytes. + +## Reactions + +React to any message with an emoji: + +``` +/react 👍 # react to the latest message +/react 🎉 3 # react to message at history index 3 +``` + +Reactions are displayed inline with the sender's name. Each reaction +references the target message by its 16-byte message ID. + +## Read receipts + +Read receipts are sent **automatically** when you receive a Chat or Reply +message. The sender sees a `✓ read` indicator in their message history. + +Receipts only trigger on Chat and Reply messages (not on other receipts, +typing indicators, or reactions) to prevent infinite loops. + +## Typing indicators + +Send a typing indicator to let others know you're composing: + +``` +/typing +``` + +Typing indicators timeout after 10 seconds of inactivity. Toggle display of +others' typing with: + +``` +/typing-notify on +/typing-notify off +``` + +The session tracks `typing_indicators` state per conversation. + +## Editing messages + +Edit one of your own messages by history index: + +``` +/edit 5 Updated text here +``` + +Only your own messages can be edited. The edit is sent as an `Edit` message +type referencing the original message ID, and the local database is updated on +receipt. + +## Deleting messages + +Delete one of your own messages: + +``` +/delete 5 +``` + +Only your own messages can be deleted. A `Delete` message is broadcast to the +group, and the message is removed from the local database on receipt. + +## Wire format + +All rich message types use the same binary envelope inside the MLS ciphertext: + +``` +[version: 1 byte][type: 1 byte][payload...] +``` + +| Type | Code | Payload | +|---|---|---| +| Chat | `0x01` | `[msg_id: 16][body_len: 2 BE][body]` | +| Reply | `0x02` | `[ref_msg_id: 16][body_len: 2 BE][body]` | +| Reaction | `0x03` | `[ref_msg_id: 16][emoji_len: 1][emoji UTF-8]` | +| ReadReceipt | `0x04` | `[msg_id: 16]` | +| Typing | `0x05` | `[active: 1]` (0 = stopped, 1 = typing) | +| Edit | `0x06` | `[ref_msg_id: 16][body_len: 2 BE][body]` | +| Delete | `0x07` | `[ref_msg_id: 16]` | + +## Implementation + +- **Core serialisation:** `crates/quicproquo-core/src/app_message.rs` — the + `AppMessage` enum with `serialize()` / `deserialize()` methods +- **REPL commands:** `crates/quicproquo-client/src/client/repl.rs` — slash + command handlers +- **Display:** `crates/quicproquo-client/src/client/display.rs` — typing + indicator rendering diff --git a/docs/src/getting-started/typescript-sdk.md b/docs/src/getting-started/typescript-sdk.md new file mode 100644 index 0000000..a7bd063 --- /dev/null +++ b/docs/src/getting-started/typescript-sdk.md @@ -0,0 +1,176 @@ +# TypeScript SDK and Browser Demo + +The TypeScript SDK (`sdks/typescript/`) provides `@quicproquo/client` — a +browser-ready client with WASM-powered crypto operations and WebSocket +transport. + +## What you get + +- **WASM crypto bundle** (175 KB) — Ed25519 signatures, X25519 + ML-KEM-768 + hybrid encryption, safety numbers, sealed sender, and message padding, + compiled from the Rust `quicproquo-core` crate +- **`QpqClient` class** — high-level API for server connectivity and crypto +- **Offline mode** — all crypto operations work without a server connection +- **Browser demo** — interactive HTML page for trying every crypto operation + +## Prerequisites + +- Node.js 18+ and npm +- Rust toolchain + `wasm-pack` (for building the WASM bundle) + +Install wasm-pack if you don't have it: + +```bash +cargo install wasm-pack +``` + +## Building + +### 1. Build the WASM crypto bundle + +```bash +cd sdks/typescript/wasm-crypto +wasm-pack build --target web --out-dir ../pkg +``` + +This compiles `quicproquo-core`'s crypto modules to WebAssembly and produces +JavaScript + TypeScript bindings in `sdks/typescript/pkg/`. + +### 2. Build the TypeScript SDK + +```bash +cd sdks/typescript +npm install +npm run build +``` + +## Running the browser demo + +The demo is a vanilla HTML page — no build step required beyond the WASM +bundle. + +```bash +cd sdks/typescript +python3 -m http.server 8080 +# Open http://localhost:8080/demo/ +``` + +### Demo walkthrough + +1. **Initialize WASM** — click the button to load the 175 KB module +2. **Generate Alice and Bob** — creates Ed25519 identity keypairs, displays + seed and public key in hex +3. **Compute Safety Number** — derives a Signal-style 60-digit verification + code from both public keys +4. **Sign and Verify** — type a message, sign it with Alice's key, verify + with Alice's public key +5. **Hybrid Encrypt/Decrypt** — generate an X25519 + ML-KEM-768 keypair, + encrypt and decrypt with post-quantum protection +6. **Sealed Sender** — create an anonymous envelope wrapping a payload with + Alice's identity +7. **Message Padding** — pad a message to a constant-size bucket (256, 1024, + 4096, or 16384 bytes) and unpad it + +The crypto operations work entirely offline — no server connection needed. + +### Server connectivity + +The Chat section of the demo connects via WebSocket. Since the native qpq +server speaks Cap'n Proto RPC over QUIC/TCP + Noise_XX, a WebSocket bridge +proxy is required for browser connectivity. The demo sends JSON-framed +requests over WebSocket. + +## Using the SDK in your code + +### Offline crypto (no server) + +```typescript +import { QpqClient } from "@quicproquo/client"; + +const client = await QpqClient.offline(); + +// Generate identities +const alice = client.generateIdentity(); +const bob = client.generateIdentity(); + +// Safety number verification +const safetyNumber = client.computeSafetyNumber( + alice.publicKey, + bob.publicKey, +); +console.log("Safety number:", safetyNumber); + +// Sign and verify +const msg = new TextEncoder().encode("hello"); +const sig = client.sign(alice.seed, msg); +console.log("Valid:", client.verify(alice.publicKey, msg, sig)); + +// Hybrid encryption (post-quantum) +const ciphertext = client.hybridEncrypt(bob.hybridKey, msg); +const plaintext = client.hybridDecrypt(bob.seed, ciphertext); +``` + +### Server connection + +```typescript +const client = await QpqClient.connect({ addr: "wss://bridge.example.com" }); + +// Health check +const status = await client.health(); + +// Resolve a user +const peerKey = await client.resolveUser("bob"); + +// Create a DM channel +const channel = await client.createChannel(peerKey); + +// Send messages +await client.send(peerKey, new TextEncoder().encode("Hello!")); +await client.sendWithTTL(peerKey, payload, 3600); // 1h TTL + +// Receive +const messages = await client.receive(peerKey); + +// Account deletion +await client.deleteAccount(); +``` + +## WASM crypto functions + +The WASM bundle exposes 13 functions via `wasm_bindgen`: + +| Function | Description | +|---|---| +| `generate_identity()` | Generate Ed25519 seed (32 bytes) | +| `identity_public_key(seed)` | Derive public key from seed | +| `sign(seed, message)` | Ed25519 signature (64 bytes) | +| `verify(pubkey, message, sig)` | Verify signature (returns boolean) | +| `compute_safety_number(keyA, keyB)` | 60-digit verification code | +| `hybrid_generate_keypair()` | X25519 + ML-KEM-768 keypair | +| `hybrid_public_key(keypair)` | Extract public key from keypair | +| `hybrid_encrypt(pubkey, plaintext)` | Hybrid encrypt | +| `hybrid_decrypt(keypair, ciphertext)` | Hybrid decrypt | +| `seal(seed, payload)` | Sealed sender envelope | +| `unseal(envelope)` | Unseal (returns sender_key + payload) | +| `pad_message(msg)` | Pad to bucket size (256/1K/4K/16K) | +| `unpad_message(padded)` | Remove padding | + +## Project structure + +``` +sdks/typescript/ +├── src/ +│ ├── index.ts # Public API exports +│ ├── client.ts # QpqClient class (258 lines) +│ ├── transport.ts # WebSocket transport with reconnection (206 lines) +│ ├── crypto.ts # WASM crypto wrapper (135 lines) +│ └── types.ts # TypeScript type definitions +├── demo/ +│ └── index.html # Interactive browser demo (476 lines) +├── wasm-crypto/ +│ ├── Cargo.toml # Rust WASM crate (wasm-bindgen) +│ └── src/lib.rs # 13 wasm_bindgen functions +├── pkg/ # WASM output (built by wasm-pack) +├── package.json +└── tsconfig.json +``` diff --git a/docs/src/getting-started/wasm.md b/docs/src/getting-started/wasm.md index 7deaeac..4ed4f6f 100644 --- a/docs/src/getting-started/wasm.md +++ b/docs/src/getting-started/wasm.md @@ -60,11 +60,19 @@ This means the WASM build works in browser environments out of the box. For non-browser WASM runtimes (WASI, etc.), you may need to adjust the `getrandom` backend. -## Future plans +## wasm-bindgen and the TypeScript SDK -- **wasm-bindgen JS bindings**: Wrap the WASM-compatible modules with - `#[wasm_bindgen]` annotations to provide a native JavaScript/TypeScript API. - This would allow web frontends to perform client-side encryption without a - server round-trip. -- **wasm-pack integration**: Publish the WASM module as an npm package for - easy consumption in web projects. +The `wasm-bindgen` JS bindings are now implemented in +`sdks/typescript/wasm-crypto/`. This provides 13 JavaScript-callable functions +wrapping the WASM-compatible crypto modules: + +```bash +cd sdks/typescript/wasm-crypto +wasm-pack build --target web --out-dir ../pkg +``` + +The resulting 175 KB WASM bundle is used by the `@quicproquo/client` +TypeScript SDK and the interactive browser demo. + +See the [TypeScript SDK and Browser Demo](typescript-sdk.md) guide for +full details on building and running the browser demo. diff --git a/docs/src/introduction.md b/docs/src/introduction.md index 7e0dc5a..e7a6ec3 100644 --- a/docs/src/introduction.md +++ b/docs/src/introduction.md @@ -34,7 +34,11 @@ Each layer addresses a distinct concern: | Transport authentication | TLS 1.3 server certificate (self-signed, SANs: `localhost`, `127.0.0.1`, `::1`) | | Group key agreement | `MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519` | | Post-compromise security (PCS) | MLS epoch ratchet -- each Commit advances the key schedule | +| Post-quantum readiness | X25519 + ML-KEM-768 hybrid KEM envelope | | Identity | Ed25519 (`ed25519-dalek`); public key used as MLS `BasicCredential` | +| Password auth | OPAQUE (password never sent to server) | +| Metadata protection | Sealed sender + message padding | +| Local storage | SQLCipher + Argon2id + ChaCha20-Poly1305 | | Framing | Cap'n Proto (unpacked wire format, schema-versioned) | For a deeper discussion of the cryptographic guarantees, threat model, and known gaps, see: @@ -47,15 +51,22 @@ For a deeper discussion of the cryptographic guarantees, threat model, and known ## Who is this for? -**Security researchers** studying how MLS composes with QUIC transport and Cap'n Proto framing. The codebase is intentionally small (four crates, ~2 500 lines of non-generated Rust) so that every cryptographic boundary is auditable. +**Security researchers** studying how MLS composes with QUIC transport and Cap'n Proto framing. The codebase spans 12 crates with clear cryptographic boundaries for auditability. **Protocol designers** evaluating MLS deployment patterns. quicproquo implements a concrete Authentication Service (AS) and Delivery Service (DS) pair, demonstrating single-use KeyPackage lifecycle, Welcome routing, and epoch advancement in a live system. +**Application developers** building on the platform via SDKs: + +- **Go SDK** — native QUIC + Cap'n Proto client with full API +- **TypeScript SDK** — WASM crypto + WebSocket transport for browsers +- **C FFI** — cross-language integration (Python, Swift, Kotlin) + **Rust developers** looking for a working example of: - `quinn` + `rustls` server/client setup with self-signed certificates - `capnp-rpc` over QUIC bidirectional streams (including the `!Send` / `LocalSet` constraint) - `openmls` group creation, member addition, and application message encryption +- `wasm-bindgen` for compiling Rust crypto to WebAssembly - `zeroize`-on-drop key material handling --- @@ -69,7 +80,12 @@ For a deeper discussion of the cryptographic guarantees, threat model, and known | [Building from Source](getting-started/building.md) | `cargo build`, Cap'n Proto codegen, troubleshooting | | [Running the Server](getting-started/running-the-server.md) | Server startup, configuration, TLS cert generation | | [Running the Client](getting-started/running-the-client.md) | All CLI subcommands with examples | -| [Docker Deployment](getting-started/docker.md) | `docker compose up`, multi-stage build | +| [REPL Command Reference](getting-started/repl-reference.md) | Complete list of 40+ slash commands | +| [Rich Messaging](getting-started/rich-messaging.md) | Reactions, typing, read receipts, edit/delete | +| [File Transfer](getting-started/file-transfer.md) | Chunked upload/download with SHA-256 verification | +| [Go SDK](getting-started/go-sdk.md) | Native QUIC + Cap'n Proto Go client | +| [TypeScript SDK & Browser Demo](getting-started/typescript-sdk.md) | WASM crypto + WebSocket transport | +| [Mesh Networking](getting-started/mesh-networking.md) | P2P, broadcast channels, store-and-forward, federation | | [Demo Walkthrough](getting-started/demo-walkthrough.md) | Step-by-step Alice-and-Bob narrative with sequence diagram | | [Architecture Overview](architecture/overview.md) | Crate boundaries, service architecture, data flow | | [Protocol Layers](protocol-layers/overview.md) | Deep dives into QUIC/TLS, Cap'n Proto, MLS, Hybrid KEM | @@ -82,16 +98,27 @@ For a deeper discussion of the cryptographic guarantees, threat model, and known ## Current status -quicproquo is a **proof of concept**. It has not been audited by a third party. +quicproquo is a **research project** with production-grade features. It has +not been audited by a third party. The test suite covers 130+ tests across +core, server, client, E2E, and P2P modules. -Known limitations: +**What works today:** + +- Full-featured REPL with 40+ commands: DMs, groups, reactions, typing, + edit/delete, file transfer, disappearing messages, safety numbers, MLS key + rotation, account deletion +- Go SDK, TypeScript SDK (WASM crypto + browser demo), C FFI + Python bindings +- Mesh networking: P2P via iroh, mDNS discovery, federation, store-and-forward, + broadcast channels +- Dynamic plugin system with 6 C-compatible hook points +- 24 Cap'n Proto RPC methods on the server + +**Known limitations:** -- The server uses a **self-signed TLS certificate** by default. No certificate pinning or CA-based server identity is enforced. - MLS credentials use `CredentialType::Basic` (raw public key). A production system would bind credentials to a certificate authority or use X.509 certificates. -- The Delivery Service performs **no authentication** of the `recipientKey` field -- anyone who knows a recipient's public key can enqueue messages for them. Access control is a future milestone. -- The HPKE init private key generated during `register-state` is held in-process memory (or on-disk via the key store). If the process exits before the corresponding Welcome is consumed, `join` will fail because the private key is lost. - -Multi-party groups (N > 2) are supported (milestone M5): Commit fan-out, `send --all`, and epoch sync work for all members. +- The hybrid KEM envelope is implemented and tested, but not yet integrated into the OpenMLS CryptoProvider for full post-quantum MLS (milestone M7). +- Browser connectivity requires a WebSocket-to-Cap'n-Proto bridge proxy (not yet included). +- The GUI crate (`quicproquo-gui`) requires GTK system libraries and is not feature-complete. For the full milestone tracker, see [Milestones](roadmap/milestones.md). diff --git a/docs/src/wire-format/node-service-schema.md b/docs/src/wire-format/node-service-schema.md index c194864..d55b085 100644 --- a/docs/src/wire-format/node-service-schema.md +++ b/docs/src/wire-format/node-service-schema.md @@ -211,19 +211,47 @@ Auth version `0` is no longer supported; clients must send `version=1` and a val ## Method ordinal summary -| Ordinal | Method | Origin | Category | -|---|---|---|---| -| `@0` | `uploadKeyPackage` | AuthenticationService | Auth | -| `@1` | `fetchKeyPackage` | AuthenticationService | Auth | -| `@2` | `enqueue` | DeliveryService | Delivery | -| `@3` | `fetch` | DeliveryService | Delivery | -| `@4` | `fetchWait` | NodeService (new) | Delivery | -| `@5` | `health` | NodeService (new) | Infrastructure | -| `@6` | `uploadHybridKey` | NodeService (new) | Auth / PQ | -| `@7` | `fetchHybridKey` | NodeService (new) | Auth / PQ | +| Ordinal | Method | Category | +|---|---|---| +| `@0` | `uploadKeyPackage` | Auth | +| `@1` | `fetchKeyPackage` | Auth | +| `@2` | `enqueue` | Delivery | +| `@3` | `fetch` | Delivery | +| `@4` | `fetchWait` | Delivery | +| `@5` | `health` | Infrastructure | +| `@6` | `uploadHybridKey` | Auth / PQ | +| `@7` | `fetchHybridKey` | Auth / PQ | +| `@8` | `fetchHybridKeys` | Auth / PQ (batch) | +| `@9` | `opaqueRegisterStart` | Auth / OPAQUE | +| `@10` | `opaqueRegisterFinish` | Auth / OPAQUE | +| `@11` | `opaqueLoginStart` | Auth / OPAQUE | +| `@12` | `opaqueLoginFinish` | Auth / OPAQUE | +| `@13` | `peek` | Delivery (non-destructive read) | +| `@14` | `ack` | Delivery (acknowledge after peek) | +| `@15` | `batchEnqueue` | Delivery (fan-out) | +| `@16` | `createChannel` | Channels | +| `@17` | `resolveUser` | Discovery | +| `@18` | `resolveIdentity` | Discovery (reverse lookup) | +| `@19` | `registerDevice` | Devices | +| `@20` | `listDevices` | Devices | +| `@21` | `uploadBlob` | File transfer | +| `@22` | `downloadBlob` | File transfer | +| `@23` | `deleteAccount` | Account management | +| `@24` | `revokeDevice` | Devices | +| `@25` | `publishEndpoint` | P2P discovery | +| `@26` | `resolveEndpoint` | P2P discovery | Ordinals are stable and must not be reused. New methods are appended with the next available ordinal. This is a fundamental Cap'n Proto schema evolution rule: removing a method does not free its ordinal. +### Notable additions since initial release + +- **OPAQUE (@9-@12):** Password-authenticated key exchange. The password never leaves the client. +- **Channels (@16):** `createChannel` returns `(channelId :Data, wasNew :Bool)` for 1:1 DM creation with deduplication. +- **File transfer (@21-@22):** `uploadBlob` accepts 256 KB chunks with SHA-256 content addressing; `downloadBlob` retrieves chunks with hash verification. Max 50 MB. +- **Account deletion (@23):** Transactional purge of all user data (user record, identity keys, key packages, hybrid keys, queued deliveries, channel memberships). +- **TTL support:** `enqueue` and `batchEnqueue` accept an optional `ttlSecs` parameter for disappearing messages with server-side garbage collection. +- **P2P discovery (@25-@26):** `publishEndpoint` and `resolveEndpoint` for iroh node address exchange. + --- ## Schema evolution