chore: rename quicproquo → quicprochat in docs, Docker, CI, and packaging
Rename all project references from quicproquo/qpq to quicprochat/qpc across documentation, Docker configuration, CI workflows, packaging scripts, operational configs, and build tooling. - Docker: crate paths, binary names, user/group, data dirs, env vars - CI: workflow crate references, binary names, artifact names - Docs: all markdown files under docs/, SDK READMEs, book.toml - Packaging: OpenWrt Makefile, init script, UCI config (file renames) - Scripts: justfile, dev-shell, screenshot, cross-compile, ai_team - Operations: Prometheus config, alert rules, Grafana dashboard - Config: .env.example (QPQ_* → QPC_*), CODEOWNERS paths - Top-level: README, CONTRIBUTING, ROADMAP, CLAUDE.md
This commit is contained in:
16
.env.example
16
.env.example
@@ -1,20 +1,20 @@
|
|||||||
# quicproquo Production Environment Variables
|
# quicprochat Production Environment Variables
|
||||||
# Copy this file to .env and fill in the values.
|
# Copy this file to .env and fill in the values.
|
||||||
|
|
||||||
# Server auth token (required, >= 16 characters)
|
# Server auth token (required, >= 16 characters)
|
||||||
QPQ_AUTH_TOKEN=
|
QPC_AUTH_TOKEN=
|
||||||
|
|
||||||
# SQLCipher database encryption key (required for store_backend=sql)
|
# SQLCipher database encryption key (required for store_backend=sql)
|
||||||
QPQ_DB_KEY=
|
QPC_DB_KEY=
|
||||||
|
|
||||||
# Ports (defaults shown)
|
# Ports (defaults shown)
|
||||||
QPQ_LISTEN_PORT=7000
|
QPC_LISTEN_PORT=7000
|
||||||
QPQ_WS_PORT=9000
|
QPC_WS_PORT=9000
|
||||||
|
|
||||||
# Optional features
|
# Optional features
|
||||||
QPQ_SEALED_SENDER=false
|
QPC_SEALED_SENDER=false
|
||||||
QPQ_REDACT_LOGS=true
|
QPC_REDACT_LOGS=true
|
||||||
QPQ_WS_LISTEN=
|
QPC_WS_LISTEN=
|
||||||
|
|
||||||
# Grafana admin password (required — must be strong, no default)
|
# Grafana admin password (required — must be strong, no default)
|
||||||
GRAFANA_ADMIN_PASSWORD=
|
GRAFANA_ADMIN_PASSWORD=
|
||||||
|
|||||||
20
.github/CODEOWNERS
vendored
20
.github/CODEOWNERS
vendored
@@ -1,4 +1,4 @@
|
|||||||
# Code owners for quicproquo. PRs require review from owners.
|
# Code owners for quicprochat. PRs require review from owners.
|
||||||
# See https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
# See https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
||||||
# Replace 'maintainers' with your GitHub user/team handle.
|
# Replace 'maintainers' with your GitHub user/team handle.
|
||||||
|
|
||||||
@@ -6,32 +6,32 @@
|
|||||||
* @maintainers
|
* @maintainers
|
||||||
|
|
||||||
# Security-critical: crypto primitives, MLS, hybrid KEM
|
# Security-critical: crypto primitives, MLS, hybrid KEM
|
||||||
/crates/quicproquo-core/ @maintainers
|
/crates/quicprochat-core/ @maintainers
|
||||||
|
|
||||||
# Wire format: protobuf definitions, Cap'n Proto schemas
|
# Wire format: protobuf definitions, Cap'n Proto schemas
|
||||||
/crates/quicproquo-proto/ @maintainers
|
/crates/quicprochat-proto/ @maintainers
|
||||||
/proto/ @maintainers
|
/proto/ @maintainers
|
||||||
|
|
||||||
# Auth and server-side domain logic
|
# Auth and server-side domain logic
|
||||||
/crates/quicproquo-server/ @maintainers
|
/crates/quicprochat-server/ @maintainers
|
||||||
|
|
||||||
# Client SDK: auth, conversation store, messaging pipeline
|
# Client SDK: auth, conversation store, messaging pipeline
|
||||||
/crates/quicproquo-sdk/ @maintainers
|
/crates/quicprochat-sdk/ @maintainers
|
||||||
|
|
||||||
# CLI/TUI client
|
# CLI/TUI client
|
||||||
/crates/quicproquo-client/ @maintainers
|
/crates/quicprochat-client/ @maintainers
|
||||||
|
|
||||||
# RPC framework: framing, middleware, QUIC transport
|
# RPC framework: framing, middleware, QUIC transport
|
||||||
/crates/quicproquo-rpc/ @maintainers
|
/crates/quicprochat-rpc/ @maintainers
|
||||||
|
|
||||||
# Key transparency
|
# Key transparency
|
||||||
/crates/quicproquo-kt/ @maintainers
|
/crates/quicprochat-kt/ @maintainers
|
||||||
|
|
||||||
# Plugin ABI (no_std C-ABI boundary)
|
# Plugin ABI (no_std C-ABI boundary)
|
||||||
/crates/quicproquo-plugin-api/ @maintainers
|
/crates/quicprochat-plugin-api/ @maintainers
|
||||||
|
|
||||||
# P2P transport
|
# P2P transport
|
||||||
/crates/quicproquo-p2p/ @maintainers
|
/crates/quicprochat-p2p/ @maintainers
|
||||||
|
|
||||||
# CI and infrastructure
|
# CI and infrastructure
|
||||||
/.github/ @maintainers
|
/.github/ @maintainers
|
||||||
|
|||||||
2
.github/workflows/bench.yml
vendored
2
.github/workflows/bench.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
|||||||
${{ runner.os }}-bench-
|
${{ runner.os }}-bench-
|
||||||
|
|
||||||
- name: Run benchmarks
|
- name: Run benchmarks
|
||||||
run: cargo bench --package quicproquo-core -- --output-format=bencher 2>&1 | tee bench-output.txt
|
run: cargo bench --package quicprochat-core -- --output-format=bencher 2>&1 | tee bench-output.txt
|
||||||
|
|
||||||
- name: Upload HTML reports
|
- name: Upload HTML reports
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -102,7 +102,7 @@ jobs:
|
|||||||
- name: Run coverage
|
- name: Run coverage
|
||||||
run: |
|
run: |
|
||||||
cargo tarpaulin --workspace \
|
cargo tarpaulin --workspace \
|
||||||
--exclude quicproquo-p2p \
|
--exclude quicprochat-p2p \
|
||||||
--out xml \
|
--out xml \
|
||||||
--output-dir coverage/ \
|
--output-dir coverage/ \
|
||||||
-- --test-threads 1
|
-- --test-threads 1
|
||||||
|
|||||||
8
.github/workflows/openwrt.yml
vendored
8
.github/workflows/openwrt.yml
vendored
@@ -43,11 +43,11 @@ jobs:
|
|||||||
CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '1'
|
CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '1'
|
||||||
CARGO_PROFILE_RELEASE_STRIP: symbols
|
CARGO_PROFILE_RELEASE_STRIP: symbols
|
||||||
run: |
|
run: |
|
||||||
cargo zigbuild --release --target ${{ matrix.target }} --bin qpq-server
|
cargo zigbuild --release --target ${{ matrix.target }} --bin qpc-server
|
||||||
|
|
||||||
- name: Check binary size
|
- name: Check binary size
|
||||||
run: |
|
run: |
|
||||||
BINARY="target/${{ matrix.target }}/release/qpq-server"
|
BINARY="target/${{ matrix.target }}/release/qpc-server"
|
||||||
SIZE=$(stat -c%s "$BINARY")
|
SIZE=$(stat -c%s "$BINARY")
|
||||||
SIZE_MB=$(echo "scale=2; $SIZE / 1048576" | bc)
|
SIZE_MB=$(echo "scale=2; $SIZE / 1048576" | bc)
|
||||||
echo "Binary size: ${SIZE_MB} MB"
|
echo "Binary size: ${SIZE_MB} MB"
|
||||||
@@ -60,6 +60,6 @@ jobs:
|
|||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: qpq-server-${{ matrix.target }}
|
name: qpc-server-${{ matrix.target }}
|
||||||
path: target/${{ matrix.target }}/release/qpq-server
|
path: target/${{ matrix.target }}/release/qpc-server
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Contributing to quicproquo
|
# Contributing to quicprochat
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@@ -37,4 +37,4 @@ Do not open public issues for security bugs. See [SECURITY.md](SECURITY.md) for
|
|||||||
|
|
||||||
## Licensing
|
## Licensing
|
||||||
|
|
||||||
The server crate (`quicproquo-server`) is licensed under **AGPL-3.0**. All other crates are dual-licensed under **Apache-2.0 / MIT**. By submitting a contribution, you agree to license your work under the applicable license(s).
|
The server crate (`quicprochat-server`) is licensed under **AGPL-3.0**. All other crates are dual-licensed under **Apache-2.0 / MIT**. By submitting a contribution, you agree to license your work under the applicable license(s).
|
||||||
|
|||||||
56
README.md
56
README.md
@@ -1,8 +1,8 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="assets/logo.png" alt="quicproquo" width="160">
|
<img src="assets/logo.png" alt="quicprochat" width="160">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1 align="center">quicproquo</h1>
|
<h1 align="center">quicprochat</h1>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<strong>End-to-end encrypted group messaging over QUIC, powered by MLS and post-quantum cryptography.</strong>
|
<strong>End-to-end encrypted group messaging over QUIC, powered by MLS and post-quantum cryptography.</strong>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
quicproquo is a production-grade messenger where the server **never sees plaintext**. All traffic flows over QUIC/TLS 1.3, group keys are negotiated with the [MLS protocol](https://www.rfc-editor.org/rfc/rfc9420) (RFC 9420), and a hybrid X25519 + ML-KEM-768 KEM provides post-quantum confidentiality. Written in Rust. 45,000 lines of code. 301 tests.
|
quicprochat is a production-grade messenger where the server **never sees plaintext**. All traffic flows over QUIC/TLS 1.3, group keys are negotiated with the [MLS protocol](https://www.rfc-editor.org/rfc/rfc9420) (RFC 9420), and a hybrid X25519 + ML-KEM-768 KEM provides post-quantum confidentiality. Written in Rust. 45,000 lines of code. 301 tests.
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────┐
|
||||||
@@ -53,17 +53,17 @@ cargo build --workspace
|
|||||||
cargo test --workspace
|
cargo test --workspace
|
||||||
|
|
||||||
# Start the server (auto-generates self-signed TLS cert)
|
# Start the server (auto-generates self-signed TLS cert)
|
||||||
cargo run --bin qpq-server -- --allow-insecure-auth
|
cargo run --bin qpc-server -- --allow-insecure-auth
|
||||||
|
|
||||||
# Interactive REPL (registers + logs in automatically)
|
# Interactive REPL (registers + logs in automatically)
|
||||||
cargo run --bin qpq -- repl --username alice --password secret
|
cargo run --bin qpc -- repl --username alice --password secret
|
||||||
```
|
```
|
||||||
|
|
||||||
**Two-terminal demo:**
|
**Two-terminal demo:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Terminal 1 # Terminal 2
|
# Terminal 1 # Terminal 2
|
||||||
qpq repl -u alice -p secretA qpq repl -u bob -p secretB
|
qpc repl -u alice -p secretA qpc repl -u bob -p secretB
|
||||||
|
|
||||||
# Alice: # Bob sees:
|
# Alice: # Bob sees:
|
||||||
/dm bob [alice] Hello, Bob!
|
/dm bob [alice] Hello, Bob!
|
||||||
@@ -73,19 +73,19 @@ Hello, Bob!
|
|||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
quicproquo/
|
quicprochat/
|
||||||
├── crates/
|
├── crates/
|
||||||
│ ├── quicproquo-core # MLS, hybrid KEM, PQ Noise, OPAQUE, recovery, padding
|
│ ├── quicprochat-core # MLS, hybrid KEM, PQ Noise, OPAQUE, recovery, padding
|
||||||
│ ├── quicproquo-proto # Protobuf (prost) + Cap'n Proto generated types
|
│ ├── quicprochat-proto # Protobuf (prost) + Cap'n Proto generated types
|
||||||
│ ├── quicproquo-rpc # QUIC RPC framework (framing, dispatch, middleware)
|
│ ├── quicprochat-rpc # QUIC RPC framework (framing, dispatch, middleware)
|
||||||
│ ├── quicproquo-sdk # Client SDK (QpqClient, conversation store, outbox)
|
│ ├── quicprochat-sdk # Client SDK (QpqClient, conversation store, outbox)
|
||||||
│ ├── quicproquo-server # QUIC server, 33 RPC methods, domain services, plugins
|
│ ├── quicprochat-server # QUIC server, 33 RPC methods, domain services, plugins
|
||||||
│ ├── quicproquo-client # CLI + REPL + TUI (Ratatui)
|
│ ├── quicprochat-client # CLI + REPL + TUI (Ratatui)
|
||||||
│ ├── quicproquo-kt # Key transparency (Merkle-log, revocation)
|
│ ├── quicprochat-kt # Key transparency (Merkle-log, revocation)
|
||||||
│ ├── quicproquo-p2p # iroh P2P, mesh identity, store-and-forward
|
│ ├── quicprochat-p2p # iroh P2P, mesh identity, store-and-forward
|
||||||
│ ├── quicproquo-ffi # C FFI (libquicproquo_ffi.so)
|
│ ├── quicprochat-ffi # C FFI (libquicprochat_ffi.so)
|
||||||
│ └── quicproquo-plugin-api # Dynamic plugin hooks (C ABI)
|
│ └── quicprochat-plugin-api # Dynamic plugin hooks (C ABI)
|
||||||
├── proto/qpq/v1/ # 15 .proto schema files
|
├── proto/qpc/v1/ # 15 .proto schema files
|
||||||
├── sdks/ # Go, Python, TypeScript, Swift, Kotlin, Java, Ruby
|
├── sdks/ # Go, Python, TypeScript, Swift, Kotlin, Java, Ruby
|
||||||
├── docs/ # mdBook docs, SDK guides, operational runbooks
|
├── docs/ # mdBook docs, SDK guides, operational runbooks
|
||||||
└── packaging/ # OpenWrt, Docker, cross-compilation
|
└── packaging/ # OpenWrt, Docker, cross-compilation
|
||||||
@@ -132,7 +132,7 @@ quicproquo/
|
|||||||
|
|
||||||
| Language | Location | Transport | Notes |
|
| Language | Location | Transport | Notes |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **Rust** | `crates/quicproquo-sdk` | QUIC (quinn) | Reference implementation |
|
| **Rust** | `crates/quicprochat-sdk` | QUIC (quinn) | Reference implementation |
|
||||||
| **Go** | `sdks/go/` | QUIC (quic-go) | Cap'n Proto RPC, full API |
|
| **Go** | `sdks/go/` | QUIC (quic-go) | Cap'n Proto RPC, full API |
|
||||||
| **Python** | `sdks/python/` | QUIC (aioquic) + FFI | Async client, PyPI-ready |
|
| **Python** | `sdks/python/` | QUIC (aioquic) + FFI | Async client, PyPI-ready |
|
||||||
| **TypeScript** | `sdks/typescript/` | WebSocket + WASM crypto | 175 KB WASM bundle, browser demo |
|
| **TypeScript** | `sdks/typescript/` | WebSocket + WASM crypto | 175 KB WASM bundle, browser demo |
|
||||||
@@ -165,8 +165,8 @@ quicproquo/
|
|||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker build -t quicproquo -f docker/Dockerfile .
|
docker build -t quicprochat -f docker/Dockerfile .
|
||||||
docker run -p 7000:7000 -v qpq-data:/data quicproquo
|
docker run -p 7000:7000 -v qpc-data:/data quicprochat
|
||||||
```
|
```
|
||||||
|
|
||||||
### Production (Docker Compose)
|
### Production (Docker Compose)
|
||||||
@@ -190,13 +190,13 @@ See [docs/openwrt.md](docs/openwrt.md) for `opkg` packaging and `procd` init scr
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Environment variables (see .env.example for full list)
|
# Environment variables (see .env.example for full list)
|
||||||
QPQ_LISTEN=0.0.0.0:7000
|
QPC_LISTEN=0.0.0.0:7000
|
||||||
QPQ_AUTH_TOKEN=your-strong-token
|
QPC_AUTH_TOKEN=your-strong-token
|
||||||
QPQ_DB_KEY=your-db-encryption-key
|
QPC_DB_KEY=your-db-encryption-key
|
||||||
QPQ_STORE_BACKEND=sql
|
QPC_STORE_BACKEND=sql
|
||||||
QPQ_METRICS_LISTEN=0.0.0.0:9090
|
QPC_METRICS_LISTEN=0.0.0.0:9090
|
||||||
QPQ_DRAIN_TIMEOUT=30
|
QPC_DRAIN_TIMEOUT=30
|
||||||
QPQ_RPC_TIMEOUT=30
|
QPC_RPC_TIMEOUT=30
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
64
ROADMAP.md
64
ROADMAP.md
@@ -1,4 +1,4 @@
|
|||||||
# Roadmap — quicproquo
|
# Roadmap — quicprochat
|
||||||
|
|
||||||
> From proof-of-concept to production-grade E2E encrypted messaging.
|
> From proof-of-concept to production-grade E2E encrypted messaging.
|
||||||
>
|
>
|
||||||
@@ -18,7 +18,7 @@ Eliminate all crash paths, enforce secure defaults, fix deployment blockers.
|
|||||||
- Audit: `grep -rn 'unwrap()\|expect(' crates/` outside `#[cfg(test)]`
|
- Audit: `grep -rn 'unwrap()\|expect(' crates/` outside `#[cfg(test)]`
|
||||||
|
|
||||||
- [x] **1.2 Enforce secure defaults in production mode**
|
- [x] **1.2 Enforce secure defaults in production mode**
|
||||||
- Reject startup if `QPQ_PRODUCTION=true` and `auth_token` is empty or `"devtoken"`
|
- Reject startup if `QPC_PRODUCTION=true` and `auth_token` is empty or `"devtoken"`
|
||||||
- Require non-empty `db_key` when using SQL backend in production
|
- Require non-empty `db_key` when using SQL backend in production
|
||||||
- Refuse to auto-generate TLS certs in production mode (require existing cert+key)
|
- Refuse to auto-generate TLS certs in production mode (require existing cert+key)
|
||||||
- Already partially implemented — verify and harden the validation in `config.rs`
|
- Already partially implemented — verify and harden the validation in `config.rs`
|
||||||
@@ -30,8 +30,8 @@ Eliminate all crash paths, enforce secure defaults, fix deployment blockers.
|
|||||||
- [x] **1.4 Fix Dockerfile**
|
- [x] **1.4 Fix Dockerfile**
|
||||||
- Sync workspace members (handle excluded `p2p` crate)
|
- Sync workspace members (handle excluded `p2p` crate)
|
||||||
- Create dedicated user/group instead of `nobody`
|
- Create dedicated user/group instead of `nobody`
|
||||||
- Set writable `QPQ_DATA_DIR` with correct permissions
|
- Set writable `QPC_DATA_DIR` with correct permissions
|
||||||
- Test: `docker build . && docker run --rm -it qpq-server --help`
|
- Test: `docker build . && docker run --rm -it qpc-server --help`
|
||||||
|
|
||||||
- [x] **1.5 TLS certificate lifecycle**
|
- [x] **1.5 TLS certificate lifecycle**
|
||||||
- Document CA-signed cert setup (Let's Encrypt / custom CA)
|
- Document CA-signed cert setup (Let's Encrypt / custom CA)
|
||||||
@@ -121,27 +121,27 @@ WASM/FFI for the crypto layer.
|
|||||||
|
|
||||||
### Implementation
|
### Implementation
|
||||||
|
|
||||||
- [x] **3.1 Go SDK (`quicproquo-go`)**
|
- [x] **3.1 Go SDK (`quicprochat-go`)**
|
||||||
- Generated Go types from `node.capnp` (6487-line codegen, all 24 RPC methods)
|
- Generated Go types from `node.capnp` (6487-line codegen, all 24 RPC methods)
|
||||||
- QUIC transport via `quic-go` with TLS 1.3 + ALPN `"capnp"`
|
- 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
|
- High-level `qpc` package: Connect, Health, ResolveUser, CreateChannel, Send/SendWithTTL, Receive/ReceiveWait, DeleteAccount, OPAQUE auth
|
||||||
- Example CLI in `sdks/go/cmd/example/`
|
- Example CLI in `sdks/go/cmd/example/`
|
||||||
|
|
||||||
- [x] **3.2 Python SDK (`quicproquo-py`)**
|
- [x] **3.2 Python SDK (`quicprochat-py`)**
|
||||||
- QUIC transport: `aioquic` with custom Cap'n Proto stream handler
|
- QUIC transport: `aioquic` with custom Cap'n Proto stream handler
|
||||||
- Cap'n Proto serialization: `pycapnp` for message types
|
- Cap'n Proto serialization: `pycapnp` for message types
|
||||||
- Manual RPC framing: length-prefixed request/response over QUIC stream
|
- Manual RPC framing: length-prefixed request/response over QUIC stream
|
||||||
- Async/await API matching the Rust client patterns
|
- Async/await API matching the Rust client patterns
|
||||||
- Crypto: PyO3 bindings to `quicproquo-core` for MLS operations
|
- Crypto: PyO3 bindings to `quicprochat-core` for MLS operations
|
||||||
- Publish: PyPI `quicproquo`
|
- Publish: PyPI `quicprochat`
|
||||||
- Example: async bot client
|
- Example: async bot client
|
||||||
|
|
||||||
- [x] **3.3 C FFI layer (`quicproquo-ffi`)**
|
- [x] **3.3 C FFI layer (`quicprochat-ffi`)**
|
||||||
- `crates/quicproquo-ffi` with 7 extern "C" functions: connect, login, send, receive, disconnect, last_error, free_string
|
- `crates/quicprochat-ffi` with 7 extern "C" functions: connect, login, send, receive, disconnect, last_error, free_string
|
||||||
- Builds as `libquicproquo_ffi.so` / `.dylib` / `.dll`
|
- Builds as `libquicprochat_ffi.so` / `.dylib` / `.dll`
|
||||||
- Python ctypes wrapper in `examples/python/qpq_client.py`
|
- Python ctypes wrapper in `examples/python/qpc_client.py`
|
||||||
|
|
||||||
- [x] **3.4 WASM compilation of `quicproquo-core`**
|
- [x] **3.4 WASM compilation of `quicprochat-core`**
|
||||||
- `wasm-pack build` target producing 175 KB WASM bundle (LTO + opt-level=s)
|
- `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
|
- 13 `wasm_bindgen` functions: Ed25519 identity, hybrid KEM, safety numbers, sealed sender, padding
|
||||||
- Browser-ready with `crypto.getRandomValues()` RNG
|
- Browser-ready with `crypto.getRandomValues()` RNG
|
||||||
@@ -156,7 +156,7 @@ WASM/FFI for the crypto layer.
|
|||||||
- Configurable port: `--webtransport-listen 0.0.0.0:7443`
|
- Configurable port: `--webtransport-listen 0.0.0.0:7443`
|
||||||
- Feature-flagged: `--features webtransport`
|
- Feature-flagged: `--features webtransport`
|
||||||
|
|
||||||
- [x] **3.6 TypeScript/JavaScript SDK (`@quicproquo/client`)**
|
- [x] **3.6 TypeScript/JavaScript SDK (`@quicprochat/client`)**
|
||||||
- `QpqClient` class: connect, offline, health, resolveUser, createChannel, send/sendWithTTL, receive, deleteAccount
|
- `QpqClient` class: connect, offline, health, resolveUser, createChannel, send/sendWithTTL, receive, deleteAccount
|
||||||
- WASM crypto wrapper: generateIdentity, sign/verify, hybridEncrypt/Decrypt, computeSafetyNumber, sealedSend, pad
|
- WASM crypto wrapper: generateIdentity, sign/verify, hybridEncrypt/Decrypt, computeSafetyNumber, sealedSend, pad
|
||||||
- WebSocket transport with request/response correlation and reconnection
|
- WebSocket transport with request/response correlation and reconnection
|
||||||
@@ -317,17 +317,17 @@ Long-term vision for wide adoption.
|
|||||||
|
|
||||||
- [x] **7.4 Sealed Sender**
|
- [x] **7.4 Sealed Sender**
|
||||||
- Sender identity inside MLS ciphertext only (server can't see who sent)
|
- Sender identity inside MLS ciphertext only (server can't see who sent)
|
||||||
- `sealed_sender` module in quicproquo-core with seal/unseal API
|
- `sealed_sender` module in quicprochat-core with seal/unseal API
|
||||||
- WASM-accessible via `wasm_bindgen` for browser use
|
- WASM-accessible via `wasm_bindgen` for browser use
|
||||||
|
|
||||||
- [x] **7.5 Additional language SDKs**
|
- [x] **7.5 Additional language SDKs**
|
||||||
- Java/Kotlin: JNI bindings to C FFI (Phase 3.3) + native QUIC (netty-quic)
|
- Java/Kotlin: JNI bindings to C FFI (Phase 3.3) + native QUIC (netty-quic)
|
||||||
- Swift: Swift wrapper over C FFI + Network.framework QUIC
|
- Swift: Swift wrapper over C FFI + Network.framework QUIC
|
||||||
- Ruby: FFI bindings via `quicproquo-ffi`
|
- Ruby: FFI bindings via `quicprochat-ffi`
|
||||||
- Evaluate demand-driven — only build SDKs people request
|
- Evaluate demand-driven — only build SDKs people request
|
||||||
|
|
||||||
- [x] **7.6 P2P / NAT traversal**
|
- [x] **7.6 P2P / NAT traversal**
|
||||||
- Direct peer-to-peer via iroh (foundation exists in `quicproquo-p2p`)
|
- Direct peer-to-peer via iroh (foundation exists in `quicprochat-p2p`)
|
||||||
- Server as fallback relay only
|
- Server as fallback relay only
|
||||||
- Reduces latency and single-point-of-failure
|
- Reduces latency and single-point-of-failure
|
||||||
- Ref: `FUTURE-IMPROVEMENTS.md § 6.1`
|
- Ref: `FUTURE-IMPROVEMENTS.md § 6.1`
|
||||||
@@ -342,35 +342,35 @@ Long-term vision for wide adoption.
|
|||||||
|
|
||||||
## Phase 8 — Freifunk / Community Mesh Networking
|
## Phase 8 — Freifunk / Community Mesh Networking
|
||||||
|
|
||||||
Make qpq a first-class citizen on decentralised, community-operated wireless
|
Make qpc a first-class citizen on decentralised, community-operated wireless
|
||||||
networks (Freifunk, BATMAN-adv/Babel routing, OpenWrt). Multiple qpq nodes form
|
networks (Freifunk, BATMAN-adv/Babel routing, OpenWrt). Multiple qpc nodes form
|
||||||
a federated mesh; clients auto-discover nearby nodes via mDNS; the network
|
a federated mesh; clients auto-discover nearby nodes via mDNS; the network
|
||||||
functions without any central infrastructure or internet uplink.
|
functions without any central infrastructure or internet uplink.
|
||||||
|
|
||||||
### Architecture
|
### Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
Client A ─── mDNS discovery ──► nearby qpq node (LAN / mesh)
|
Client A ─── mDNS discovery ──► nearby qpc node (LAN / mesh)
|
||||||
│
|
│
|
||||||
Cap'n Proto federation
|
Cap'n Proto federation
|
||||||
│
|
│
|
||||||
remote qpq node (across mesh)
|
remote qpc node (across mesh)
|
||||||
```
|
```
|
||||||
|
|
||||||
- [x] **F0 — Re-include `quicproquo-p2p` in workspace; fix ALPN strings**
|
- [x] **F0 — Re-include `quicprochat-p2p` in workspace; fix ALPN strings**
|
||||||
- Moved `crates/quicproquo-p2p` from `exclude` back into `[workspace] members`
|
- Moved `crates/quicprochat-p2p` from `exclude` back into `[workspace] members`
|
||||||
- Fixed ALPN `b"quicnprotochat/p2p/1"` → `b"quicproquo/p2p/1"` (breaking wire change)
|
- Fixed ALPN `b"quicnprotochat/p2p/1"` → `b"quicprochat/p2p/1"` (breaking wire change)
|
||||||
- Fixed federation ALPN `b"qnpc-fed"` → `b"quicproquo/federation/1"`
|
- Fixed federation ALPN `b"qnpc-fed"` → `b"quicprochat/federation/1"`
|
||||||
- Feature-gated behind `--features mesh` on client (keeps iroh out of default builds)
|
- Feature-gated behind `--features mesh` on client (keeps iroh out of default builds)
|
||||||
|
|
||||||
- [x] **F1 — Federation routing in message delivery**
|
- [x] **F1 — Federation routing in message delivery**
|
||||||
- `handle_enqueue` and `handle_batch_enqueue` call `federation::routing::resolve_destination()`
|
- `handle_enqueue` and `handle_batch_enqueue` call `federation::routing::resolve_destination()`
|
||||||
- Recipients with a remote home server are relayed via `FederationClient::relay_enqueue()`
|
- Recipients with a remote home server are relayed via `FederationClient::relay_enqueue()`
|
||||||
- mTLS mutual authentication between nodes (both present client certs, validated against shared CA)
|
- mTLS mutual authentication between nodes (both present client certs, validated against shared CA)
|
||||||
- Config: `QPQ_FEDERATION_LISTEN`, `QPQ_LOCAL_DOMAIN`, `QPQ_FEDERATION_CERT/KEY/CA`
|
- Config: `QPC_FEDERATION_LISTEN`, `QPC_LOCAL_DOMAIN`, `QPC_FEDERATION_CERT/KEY/CA`
|
||||||
|
|
||||||
- [x] **F2 — mDNS local peer discovery**
|
- [x] **F2 — mDNS local peer discovery**
|
||||||
- Server announces `_quicproquo._udp.local.` on startup via `mdns-sd`
|
- Server announces `_quicprochat._udp.local.` on startup via `mdns-sd`
|
||||||
- Client: `MeshDiscovery::start()` browses for nearby nodes (feature-gated)
|
- Client: `MeshDiscovery::start()` browses for nearby nodes (feature-gated)
|
||||||
- REPL commands: `/mesh peers` (scan + list), `/mesh server <host:port>` (note address)
|
- REPL commands: `/mesh peers` (scan + list), `/mesh server <host:port>` (note address)
|
||||||
- Nodes announce: `ver=1`, `server=<host:port>`, `domain=<local_domain>` TXT records
|
- Nodes announce: `ver=1`, `server=<host:port>`, `domain=<local_domain>` TXT records
|
||||||
@@ -378,7 +378,7 @@ functions without any central infrastructure or internet uplink.
|
|||||||
- [x] **F3 — Self-sovereign mesh identity**
|
- [x] **F3 — Self-sovereign mesh identity**
|
||||||
- Ed25519 keypair-based identity independent of AS registration
|
- Ed25519 keypair-based identity independent of AS registration
|
||||||
- JSON-persisted seed + known peers directory
|
- JSON-persisted seed + known peers directory
|
||||||
- Sign/verify operations for mesh authenticity (`crates/quicproquo-p2p/src/identity.rs`)
|
- Sign/verify operations for mesh authenticity (`crates/quicprochat-p2p/src/identity.rs`)
|
||||||
|
|
||||||
- [x] **F4 — Store-and-forward with TTL**
|
- [x] **F4 — Store-and-forward with TTL**
|
||||||
- `MeshEnvelope` with TTL-based expiry, hop_count tracking, max_hops routing limit
|
- `MeshEnvelope` with TTL-based expiry, hop_count tracking, max_hops routing limit
|
||||||
@@ -419,7 +419,7 @@ functions without any central infrastructure or internet uplink.
|
|||||||
Features designed to attract contributors, create demo/showcase potential,
|
Features designed to attract contributors, create demo/showcase potential,
|
||||||
and lower the barrier to entry for non-crypto developers.
|
and lower the barrier to entry for non-crypto developers.
|
||||||
|
|
||||||
- [x] **9.1 Criterion Benchmark Suite (`qpq-bench`)**
|
- [x] **9.1 Criterion Benchmark Suite (`qpc-bench`)**
|
||||||
- Criterion benchmarks for all crypto primitives: hybrid KEM encap/decap,
|
- Criterion benchmarks for all crypto primitives: hybrid KEM encap/decap,
|
||||||
MLS group-add at 10/100/1000 members, epoch rotation, Noise_XX handshake
|
MLS group-add at 10/100/1000 members, epoch rotation, Noise_XX handshake
|
||||||
- CI publishes HTML benchmark reports as GitHub Actions artifacts
|
- CI publishes HTML benchmark reports as GitHub Actions artifacts
|
||||||
@@ -431,7 +431,7 @@ and lower the barrier to entry for non-crypto developers.
|
|||||||
- Available in WASM via `compute_safety_number` binding
|
- Available in WASM via `compute_safety_number` binding
|
||||||
|
|
||||||
- [x] **9.3 Full-Screen TUI (Ratatui + Crossterm)**
|
- [x] **9.3 Full-Screen TUI (Ratatui + Crossterm)**
|
||||||
- `qpq tui` launches a full-screen terminal UI: message pane, input bar,
|
- `qpc tui` launches a full-screen terminal UI: message pane, input bar,
|
||||||
channel sidebar with unread counts, MLS epoch indicator
|
channel sidebar with unread counts, MLS epoch indicator
|
||||||
- Feature-gated `--features tui` to keep ratatui/crossterm out of default builds
|
- Feature-gated `--features tui` to keep ratatui/crossterm out of default builds
|
||||||
- Existing REPL and CLI subcommands are unaffected
|
- Existing REPL and CLI subcommands are unaffected
|
||||||
@@ -444,7 +444,7 @@ and lower the barrier to entry for non-crypto developers.
|
|||||||
- [x] **9.5 Verifiable Transcript Archive**
|
- [x] **9.5 Verifiable Transcript Archive**
|
||||||
- `GroupMember::export_transcript(path, password)` writes encrypted, tamper-evident
|
- `GroupMember::export_transcript(path, password)` writes encrypted, tamper-evident
|
||||||
message archive (CBOR records, Argon2id + ChaCha20-Poly1305, Merkle chain)
|
message archive (CBOR records, Argon2id + ChaCha20-Poly1305, Merkle chain)
|
||||||
- `qpq export verify` CLI command independently verifies chain integrity
|
- `qpc export verify` CLI command independently verifies chain integrity
|
||||||
- Useful for legal discovery, audit, or personal backup
|
- Useful for legal discovery, audit, or personal backup
|
||||||
|
|
||||||
- [x] **9.6 Key Transparency (Merkle-Log Identity Binding)**
|
- [x] **9.6 Key Transparency (Merkle-Log Identity Binding)**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Production Docker Compose for quicproquo
|
# Production Docker Compose for quicprochat
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# 1. Copy .env.example to .env and fill in secrets
|
# 1. Copy .env.example to .env and fill in secrets
|
||||||
@@ -11,45 +11,45 @@
|
|||||||
# - Database encryption key
|
# - Database encryption key
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
qpq:
|
qpc:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
qpq-data:
|
qpc-data:
|
||||||
prometheus-data:
|
prometheus-data:
|
||||||
grafana-data:
|
grafana-data:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# ── quicproquo server ────────────────────────────────────────────────────────
|
# ── quicprochat server ────────────────────────────────────────────────────────
|
||||||
server:
|
server:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: docker/Dockerfile
|
dockerfile: docker/Dockerfile
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "${QPQ_LISTEN_PORT:-7000}:7000/udp" # QUIC
|
- "${QPC_LISTEN_PORT:-7000}:7000/udp" # QUIC
|
||||||
- "${QPQ_WS_PORT:-9000}:9000" # WebSocket bridge (optional)
|
- "${QPC_WS_PORT:-9000}:9000" # WebSocket bridge (optional)
|
||||||
environment:
|
environment:
|
||||||
RUST_LOG: info
|
RUST_LOG: info
|
||||||
QPQ_PRODUCTION: "true"
|
QPC_PRODUCTION: "true"
|
||||||
QPQ_LISTEN: "0.0.0.0:7000"
|
QPC_LISTEN: "0.0.0.0:7000"
|
||||||
QPQ_DATA_DIR: /var/lib/quicproquo
|
QPC_DATA_DIR: /var/lib/quicprochat
|
||||||
QPQ_TLS_CERT: /var/lib/quicproquo/certs/server-cert.der
|
QPC_TLS_CERT: /var/lib/quicprochat/certs/server-cert.der
|
||||||
QPQ_TLS_KEY: /var/lib/quicproquo/certs/server-key.der
|
QPC_TLS_KEY: /var/lib/quicprochat/certs/server-key.der
|
||||||
QPQ_AUTH_TOKEN: "${QPQ_AUTH_TOKEN}"
|
QPC_AUTH_TOKEN: "${QPC_AUTH_TOKEN}"
|
||||||
QPQ_STORE_BACKEND: sql
|
QPC_STORE_BACKEND: sql
|
||||||
QPQ_DB_PATH: /var/lib/quicproquo/qpq.db
|
QPC_DB_PATH: /var/lib/quicprochat/qpc.db
|
||||||
QPQ_DB_KEY: "${QPQ_DB_KEY}"
|
QPC_DB_KEY: "${QPC_DB_KEY}"
|
||||||
QPQ_METRICS_LISTEN: "0.0.0.0:9090"
|
QPC_METRICS_LISTEN: "0.0.0.0:9090"
|
||||||
QPQ_METRICS_ENABLED: "true"
|
QPC_METRICS_ENABLED: "true"
|
||||||
QPQ_SEALED_SENDER: "${QPQ_SEALED_SENDER:-false}"
|
QPC_SEALED_SENDER: "${QPC_SEALED_SENDER:-false}"
|
||||||
QPQ_REDACT_LOGS: "${QPQ_REDACT_LOGS:-true}"
|
QPC_REDACT_LOGS: "${QPC_REDACT_LOGS:-true}"
|
||||||
QPQ_WS_LISTEN: "${QPQ_WS_LISTEN:-}"
|
QPC_WS_LISTEN: "${QPC_WS_LISTEN:-}"
|
||||||
volumes:
|
volumes:
|
||||||
- qpq-data:/var/lib/quicproquo
|
- qpc-data:/var/lib/quicprochat
|
||||||
- ./certs:/var/lib/quicproquo/certs:ro
|
- ./certs:/var/lib/quicprochat/certs:ro
|
||||||
networks:
|
networks:
|
||||||
- qpq
|
- qpc
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
@@ -63,7 +63,7 @@ services:
|
|||||||
soft: 65536
|
soft: 65536
|
||||||
hard: 65536
|
hard: 65536
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "test", "-f", "/var/lib/quicproquo/certs/server-cert.der"]
|
test: ["CMD", "test", "-f", "/var/lib/quicprochat/certs/server-cert.der"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
@@ -90,7 +90,7 @@ services:
|
|||||||
- '--storage.tsdb.retention.time=30d'
|
- '--storage.tsdb.retention.time=30d'
|
||||||
- '--web.enable-lifecycle'
|
- '--web.enable-lifecycle'
|
||||||
networks:
|
networks:
|
||||||
- qpq
|
- qpc
|
||||||
depends_on:
|
depends_on:
|
||||||
- server
|
- server
|
||||||
|
|
||||||
@@ -108,6 +108,6 @@ services:
|
|||||||
- ./docs/operations/dashboards:/var/lib/grafana/dashboards:ro
|
- ./docs/operations/dashboards:/var/lib/grafana/dashboards:ro
|
||||||
- ./docs/operations/grafana-provisioning:/etc/grafana/provisioning:ro
|
- ./docs/operations/grafana-provisioning:/etc/grafana/provisioning:ro
|
||||||
networks:
|
networks:
|
||||||
- qpq
|
- qpc
|
||||||
depends_on:
|
depends_on:
|
||||||
- prometheus
|
- prometheus
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ services:
|
|||||||
- "7000:7000"
|
- "7000:7000"
|
||||||
environment:
|
environment:
|
||||||
RUST_LOG: "info"
|
RUST_LOG: "info"
|
||||||
QPQ_LISTEN: "0.0.0.0:7000"
|
QPC_LISTEN: "0.0.0.0:7000"
|
||||||
# Healthcheck: attempt a TCP connection to port 7000.
|
# Healthcheck: attempt a TCP connection to port 7000.
|
||||||
# Uses bash /dev/tcp — available in debian:bookworm-slim without extra packages.
|
# Uses bash /dev/tcp — available in debian:bookworm-slim without extra packages.
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
|||||||
@@ -12,60 +12,60 @@ WORKDIR /build
|
|||||||
|
|
||||||
# Copy manifests first so dependency layers are cached independently of source.
|
# Copy manifests first so dependency layers are cached independently of source.
|
||||||
COPY Cargo.toml Cargo.lock ./
|
COPY Cargo.toml Cargo.lock ./
|
||||||
COPY crates/quicproquo-core/Cargo.toml crates/quicproquo-core/Cargo.toml
|
COPY crates/quicprochat-core/Cargo.toml crates/quicprochat-core/Cargo.toml
|
||||||
COPY crates/quicproquo-proto/Cargo.toml crates/quicproquo-proto/Cargo.toml
|
COPY crates/quicprochat-proto/Cargo.toml crates/quicprochat-proto/Cargo.toml
|
||||||
COPY crates/quicproquo-server/Cargo.toml crates/quicproquo-server/Cargo.toml
|
COPY crates/quicprochat-server/Cargo.toml crates/quicprochat-server/Cargo.toml
|
||||||
COPY crates/quicproquo-client/Cargo.toml crates/quicproquo-client/Cargo.toml
|
COPY crates/quicprochat-client/Cargo.toml crates/quicprochat-client/Cargo.toml
|
||||||
COPY crates/quicproquo-p2p/Cargo.toml crates/quicproquo-p2p/Cargo.toml
|
COPY crates/quicprochat-p2p/Cargo.toml crates/quicprochat-p2p/Cargo.toml
|
||||||
COPY crates/quicproquo-kt/Cargo.toml crates/quicproquo-kt/Cargo.toml
|
COPY crates/quicprochat-kt/Cargo.toml crates/quicprochat-kt/Cargo.toml
|
||||||
COPY crates/quicproquo-plugin-api/Cargo.toml crates/quicproquo-plugin-api/Cargo.toml
|
COPY crates/quicprochat-plugin-api/Cargo.toml crates/quicprochat-plugin-api/Cargo.toml
|
||||||
COPY crates/quicproquo-rpc/Cargo.toml crates/quicproquo-rpc/Cargo.toml
|
COPY crates/quicprochat-rpc/Cargo.toml crates/quicprochat-rpc/Cargo.toml
|
||||||
COPY crates/quicproquo-sdk/Cargo.toml crates/quicproquo-sdk/Cargo.toml
|
COPY crates/quicprochat-sdk/Cargo.toml crates/quicprochat-sdk/Cargo.toml
|
||||||
|
|
||||||
# Create dummy source files so `cargo build` can resolve the dependency graph
|
# Create dummy source files so `cargo build` can resolve the dependency graph
|
||||||
# and cache the compiled dependencies before copying real source.
|
# and cache the compiled dependencies before copying real source.
|
||||||
RUN mkdir -p \
|
RUN mkdir -p \
|
||||||
crates/quicproquo-core/src \
|
crates/quicprochat-core/src \
|
||||||
crates/quicproquo-proto/src \
|
crates/quicprochat-proto/src \
|
||||||
crates/quicproquo-server/src \
|
crates/quicprochat-server/src \
|
||||||
crates/quicproquo-client/src \
|
crates/quicprochat-client/src \
|
||||||
crates/quicproquo-p2p/src \
|
crates/quicprochat-p2p/src \
|
||||||
crates/quicproquo-kt/src \
|
crates/quicprochat-kt/src \
|
||||||
crates/quicproquo-plugin-api/src \
|
crates/quicprochat-plugin-api/src \
|
||||||
crates/quicproquo-rpc/src \
|
crates/quicprochat-rpc/src \
|
||||||
crates/quicproquo-sdk/src \
|
crates/quicprochat-sdk/src \
|
||||||
&& echo 'fn main() {}' > crates/quicproquo-server/src/main.rs \
|
&& echo 'fn main() {}' > crates/quicprochat-server/src/main.rs \
|
||||||
&& echo 'fn main() {}' > crates/quicproquo-client/src/main.rs \
|
&& echo 'fn main() {}' > crates/quicprochat-client/src/main.rs \
|
||||||
&& touch crates/quicproquo-core/src/lib.rs \
|
&& touch crates/quicprochat-core/src/lib.rs \
|
||||||
&& touch crates/quicproquo-proto/src/lib.rs \
|
&& touch crates/quicprochat-proto/src/lib.rs \
|
||||||
&& touch crates/quicproquo-p2p/src/lib.rs \
|
&& touch crates/quicprochat-p2p/src/lib.rs \
|
||||||
&& touch crates/quicproquo-kt/src/lib.rs \
|
&& touch crates/quicprochat-kt/src/lib.rs \
|
||||||
&& touch crates/quicproquo-plugin-api/src/lib.rs \
|
&& touch crates/quicprochat-plugin-api/src/lib.rs \
|
||||||
&& touch crates/quicproquo-rpc/src/lib.rs \
|
&& touch crates/quicprochat-rpc/src/lib.rs \
|
||||||
&& touch crates/quicproquo-sdk/src/lib.rs
|
&& touch crates/quicprochat-sdk/src/lib.rs
|
||||||
|
|
||||||
# Schemas must exist before the proto crate's build.rs runs.
|
# Schemas must exist before the proto crate's build.rs runs.
|
||||||
COPY schemas/ schemas/
|
COPY schemas/ schemas/
|
||||||
|
|
||||||
# Build dependencies only (source stubs mean this layer is cache-friendly).
|
# Build dependencies only (source stubs mean this layer is cache-friendly).
|
||||||
RUN cargo build --release --bin qpq-server 2>/dev/null || true
|
RUN cargo build --release --bin qpc-server 2>/dev/null || true
|
||||||
|
|
||||||
# Copy real source and build for real.
|
# Copy real source and build for real.
|
||||||
COPY crates/ crates/
|
COPY crates/ crates/
|
||||||
|
|
||||||
# Touch source to force re-compilation after copying real crates.
|
# Touch source to force re-compilation after copying real crates.
|
||||||
RUN touch \
|
RUN touch \
|
||||||
crates/quicproquo-core/src/lib.rs \
|
crates/quicprochat-core/src/lib.rs \
|
||||||
crates/quicproquo-proto/src/lib.rs \
|
crates/quicprochat-proto/src/lib.rs \
|
||||||
crates/quicproquo-p2p/src/lib.rs \
|
crates/quicprochat-p2p/src/lib.rs \
|
||||||
crates/quicproquo-kt/src/lib.rs \
|
crates/quicprochat-kt/src/lib.rs \
|
||||||
crates/quicproquo-plugin-api/src/lib.rs \
|
crates/quicprochat-plugin-api/src/lib.rs \
|
||||||
crates/quicproquo-rpc/src/lib.rs \
|
crates/quicprochat-rpc/src/lib.rs \
|
||||||
crates/quicproquo-sdk/src/lib.rs \
|
crates/quicprochat-sdk/src/lib.rs \
|
||||||
crates/quicproquo-server/src/main.rs \
|
crates/quicprochat-server/src/main.rs \
|
||||||
crates/quicproquo-client/src/main.rs
|
crates/quicprochat-client/src/main.rs
|
||||||
|
|
||||||
RUN cargo build --release --bin qpq-server
|
RUN cargo build --release --bin qpc-server
|
||||||
|
|
||||||
# ── Stage 2: Runtime ──────────────────────────────────────────────────────────
|
# ── Stage 2: Runtime ──────────────────────────────────────────────────────────
|
||||||
#
|
#
|
||||||
@@ -78,31 +78,31 @@ RUN apt-get update \
|
|||||||
&& apt-get install -y --no-install-recommends ca-certificates \
|
&& apt-get install -y --no-install-recommends ca-certificates \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY --from=builder /build/target/release/qpq-server /usr/local/bin/qpq-server
|
COPY --from=builder /build/target/release/qpc-server /usr/local/bin/qpc-server
|
||||||
|
|
||||||
# Create a dedicated non-root user with a writable data directory.
|
# Create a dedicated non-root user with a writable data directory.
|
||||||
RUN groupadd --system qpq \
|
RUN groupadd --system qpc \
|
||||||
&& useradd --system --gid qpq --no-create-home --shell /usr/sbin/nologin qpq \
|
&& useradd --system --gid qpc --no-create-home --shell /usr/sbin/nologin qpc \
|
||||||
&& mkdir -p /var/lib/quicproquo \
|
&& mkdir -p /var/lib/quicprochat \
|
||||||
&& chown qpq:qpq /var/lib/quicproquo
|
&& chown qpc:qpc /var/lib/quicprochat
|
||||||
|
|
||||||
EXPOSE 7000
|
EXPOSE 7000
|
||||||
|
|
||||||
# Persistent data volume: TLS certs, SQLCipher DB, delivery queues, KT log.
|
# Persistent data volume: TLS certs, SQLCipher DB, delivery queues, KT log.
|
||||||
# Mount a named volume or host path here for data persistence across restarts:
|
# Mount a named volume or host path here for data persistence across restarts:
|
||||||
# docker run -v qpq-data:/var/lib/quicproquo ...
|
# docker run -v qpc-data:/var/lib/quicprochat ...
|
||||||
VOLUME ["/var/lib/quicproquo"]
|
VOLUME ["/var/lib/quicprochat"]
|
||||||
|
|
||||||
ENV RUST_LOG=info \
|
ENV RUST_LOG=info \
|
||||||
QPQ_LISTEN=0.0.0.0:7000 \
|
QPC_LISTEN=0.0.0.0:7000 \
|
||||||
QPQ_DATA_DIR=/var/lib/quicproquo \
|
QPC_DATA_DIR=/var/lib/quicprochat \
|
||||||
QPQ_TLS_CERT=/var/lib/quicproquo/server-cert.der \
|
QPC_TLS_CERT=/var/lib/quicprochat/server-cert.der \
|
||||||
QPQ_TLS_KEY=/var/lib/quicproquo/server-key.der \
|
QPC_TLS_KEY=/var/lib/quicprochat/server-key.der \
|
||||||
QPQ_PRODUCTION=true
|
QPC_PRODUCTION=true
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
|
||||||
CMD test -f /var/lib/quicproquo/server-cert.der || exit 1
|
CMD test -f /var/lib/quicprochat/server-cert.der || exit 1
|
||||||
|
|
||||||
USER qpq
|
USER qpc
|
||||||
|
|
||||||
CMD ["qpq-server"]
|
CMD ["qpc-server"]
|
||||||
|
|||||||
@@ -12,45 +12,45 @@ WORKDIR /build
|
|||||||
|
|
||||||
# Copy manifests first so dependency layers are cached independently of source.
|
# Copy manifests first so dependency layers are cached independently of source.
|
||||||
COPY Cargo.toml Cargo.lock ./
|
COPY Cargo.toml Cargo.lock ./
|
||||||
COPY crates/quicproquo-core/Cargo.toml crates/quicproquo-core/Cargo.toml
|
COPY crates/quicprochat-core/Cargo.toml crates/quicprochat-core/Cargo.toml
|
||||||
COPY crates/quicproquo-proto/Cargo.toml crates/quicproquo-proto/Cargo.toml
|
COPY crates/quicprochat-proto/Cargo.toml crates/quicprochat-proto/Cargo.toml
|
||||||
COPY crates/quicproquo-server/Cargo.toml crates/quicproquo-server/Cargo.toml
|
COPY crates/quicprochat-server/Cargo.toml crates/quicprochat-server/Cargo.toml
|
||||||
COPY crates/quicproquo-client/Cargo.toml crates/quicproquo-client/Cargo.toml
|
COPY crates/quicprochat-client/Cargo.toml crates/quicprochat-client/Cargo.toml
|
||||||
COPY crates/quicproquo-p2p/Cargo.toml crates/quicproquo-p2p/Cargo.toml
|
COPY crates/quicprochat-p2p/Cargo.toml crates/quicprochat-p2p/Cargo.toml
|
||||||
|
|
||||||
# Create dummy source files so `cargo build` can resolve the dependency graph
|
# Create dummy source files so `cargo build` can resolve the dependency graph
|
||||||
# and cache the compiled dependencies before copying real source.
|
# and cache the compiled dependencies before copying real source.
|
||||||
RUN mkdir -p \
|
RUN mkdir -p \
|
||||||
crates/quicproquo-core/src \
|
crates/quicprochat-core/src \
|
||||||
crates/quicproquo-proto/src \
|
crates/quicprochat-proto/src \
|
||||||
crates/quicproquo-server/src \
|
crates/quicprochat-server/src \
|
||||||
crates/quicproquo-client/src \
|
crates/quicprochat-client/src \
|
||||||
crates/quicproquo-p2p/src \
|
crates/quicprochat-p2p/src \
|
||||||
&& echo 'fn main() {}' > crates/quicproquo-server/src/main.rs \
|
&& echo 'fn main() {}' > crates/quicprochat-server/src/main.rs \
|
||||||
&& echo 'fn main() {}' > crates/quicproquo-client/src/main.rs \
|
&& echo 'fn main() {}' > crates/quicprochat-client/src/main.rs \
|
||||||
&& touch crates/quicproquo-core/src/lib.rs \
|
&& touch crates/quicprochat-core/src/lib.rs \
|
||||||
&& touch crates/quicproquo-proto/src/lib.rs \
|
&& touch crates/quicprochat-proto/src/lib.rs \
|
||||||
&& touch crates/quicproquo-p2p/src/lib.rs
|
&& touch crates/quicprochat-p2p/src/lib.rs
|
||||||
|
|
||||||
# Schemas must exist before the proto crate's build.rs runs.
|
# Schemas must exist before the proto crate's build.rs runs.
|
||||||
COPY schemas/ schemas/
|
COPY schemas/ schemas/
|
||||||
|
|
||||||
# Build dependencies only (source stubs mean this layer is cache-friendly).
|
# Build dependencies only (source stubs mean this layer is cache-friendly).
|
||||||
# The GUI crate is not included, so workspace resolution may fail — || true handles it.
|
# The GUI crate is not included, so workspace resolution may fail — || true handles it.
|
||||||
RUN cargo build --release --bin qpq-server --bin qpq 2>/dev/null || true
|
RUN cargo build --release --bin qpc-server --bin qpc 2>/dev/null || true
|
||||||
|
|
||||||
# Copy real source and build for real.
|
# Copy real source and build for real.
|
||||||
COPY crates/ crates/
|
COPY crates/ crates/
|
||||||
|
|
||||||
# Touch source to force re-compilation after copying real crates.
|
# Touch source to force re-compilation after copying real crates.
|
||||||
RUN touch \
|
RUN touch \
|
||||||
crates/quicproquo-core/src/lib.rs \
|
crates/quicprochat-core/src/lib.rs \
|
||||||
crates/quicproquo-proto/src/lib.rs \
|
crates/quicprochat-proto/src/lib.rs \
|
||||||
crates/quicproquo-p2p/src/lib.rs \
|
crates/quicprochat-p2p/src/lib.rs \
|
||||||
crates/quicproquo-server/src/main.rs \
|
crates/quicprochat-server/src/main.rs \
|
||||||
crates/quicproquo-client/src/main.rs
|
crates/quicprochat-client/src/main.rs
|
||||||
|
|
||||||
RUN cargo build --release --bin qpq-server --bin qpq
|
RUN cargo build --release --bin qpc-server --bin qpc
|
||||||
|
|
||||||
# ── Stage 2: Runtime ──────────────────────────────────────────────────────────
|
# ── Stage 2: Runtime ──────────────────────────────────────────────────────────
|
||||||
#
|
#
|
||||||
@@ -61,8 +61,8 @@ RUN apt-get update \
|
|||||||
&& apt-get install -y --no-install-recommends ca-certificates \
|
&& apt-get install -y --no-install-recommends ca-certificates \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY --from=builder /build/target/release/qpq-server /usr/local/bin/qpq-server
|
COPY --from=builder /build/target/release/qpc-server /usr/local/bin/qpc-server
|
||||||
COPY --from=builder /build/target/release/qpq /usr/local/bin/qpq
|
COPY --from=builder /build/target/release/qpc /usr/local/bin/qpc
|
||||||
|
|
||||||
RUN mkdir -p /chat
|
RUN mkdir -p /chat
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ services:
|
|||||||
context: ..
|
context: ..
|
||||||
dockerfile: docker/Dockerfile.chat-test
|
dockerfile: docker/Dockerfile.chat-test
|
||||||
command: >-
|
command: >-
|
||||||
qpq-server
|
qpc-server
|
||||||
--listen 0.0.0.0:7000
|
--listen 0.0.0.0:7000
|
||||||
--data-dir /data
|
--data-dir /data
|
||||||
--tls-cert /data/server-cert.der
|
--tls-cert /data/server-cert.der
|
||||||
@@ -43,10 +43,10 @@ services:
|
|||||||
entrypoint: ["sleep", "infinity"]
|
entrypoint: ["sleep", "infinity"]
|
||||||
environment:
|
environment:
|
||||||
RUST_LOG: warn
|
RUST_LOG: warn
|
||||||
QPQ_ACCESS_TOKEN: devtoken
|
QPC_ACCESS_TOKEN: devtoken
|
||||||
QPQ_CA_CERT: /data/server-cert.der
|
QPC_CA_CERT: /data/server-cert.der
|
||||||
QPQ_SERVER_NAME: localhost
|
QPC_SERVER_NAME: localhost
|
||||||
QPQ_SERVER: "server:7000"
|
QPC_SERVER: "server:7000"
|
||||||
volumes:
|
volumes:
|
||||||
- server-data:/data:ro
|
- server-data:/data:ro
|
||||||
working_dir: /chat
|
working_dir: /chat
|
||||||
@@ -65,10 +65,10 @@ services:
|
|||||||
entrypoint: ["sleep", "infinity"]
|
entrypoint: ["sleep", "infinity"]
|
||||||
environment:
|
environment:
|
||||||
RUST_LOG: warn
|
RUST_LOG: warn
|
||||||
QPQ_ACCESS_TOKEN: devtoken
|
QPC_ACCESS_TOKEN: devtoken
|
||||||
QPQ_CA_CERT: /data/server-cert.der
|
QPC_CA_CERT: /data/server-cert.der
|
||||||
QPQ_SERVER_NAME: localhost
|
QPC_SERVER_NAME: localhost
|
||||||
QPQ_SERVER: "server:7000"
|
QPC_SERVER: "server:7000"
|
||||||
volumes:
|
volumes:
|
||||||
- server-data:/data:ro
|
- server-data:/data:ro
|
||||||
working_dir: /chat
|
working_dir: /chat
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Future Improvements
|
# Future Improvements
|
||||||
|
|
||||||
This document consolidates suggested improvements for quicproquo, drawn from the [roadmap](src/roadmap/milestones.md), [production readiness WBS](src/roadmap/production-readiness.md), [security audit](SECURITY-AUDIT.md), [production readiness audit](PRODUCTION-READINESS-AUDIT.md), and [future research](src/roadmap/future-research.md). Items are grouped by theme and ordered by impact and dependency.
|
This document consolidates suggested improvements for quicprochat, drawn from the [roadmap](src/roadmap/milestones.md), [production readiness WBS](src/roadmap/production-readiness.md), [security audit](SECURITY-AUDIT.md), [production readiness audit](PRODUCTION-READINESS-AUDIT.md), and [future research](src/roadmap/future-research.md). Items are grouped by theme and ordered by impact and dependency.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ This document consolidates suggested improvements for quicproquo, drawn from the
|
|||||||
### 4.5 Docker user and writable paths
|
### 4.5 Docker user and writable paths
|
||||||
|
|
||||||
- **Current:** Image runs as `nobody`; data dir may not be writable.
|
- **Current:** Image runs as `nobody`; data dir may not be writable.
|
||||||
- **Improve:** Create a dedicated user/group in the image and set `QPQ_DATA_DIR` (and cert paths) to a directory writable by that user; document in deployment docs.
|
- **Improve:** Create a dedicated user/group in the image and set `QPC_DATA_DIR` (and cert paths) to a directory writable by that user; document in deployment docs.
|
||||||
- **Ref:** [Production readiness audit § 15](PRODUCTION-READINESS-AUDIT.md).
|
- **Ref:** [Production readiness audit § 15](PRODUCTION-READINESS-AUDIT.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -133,7 +133,7 @@ This document consolidates suggested improvements for quicproquo, drawn from the
|
|||||||
### 6.1 P2P / NAT traversal (iroh, LibP2P)
|
### 6.1 P2P / NAT traversal (iroh, LibP2P)
|
||||||
|
|
||||||
- **Goal:** Direct peer-to-peer when possible; server as optional relay/rendezvous. Reduces single-point-of-failure and can improve latency.
|
- **Goal:** Direct peer-to-peer when possible; server as optional relay/rendezvous. Reduces single-point-of-failure and can improve latency.
|
||||||
- **Ref:** [Future research § LibP2P / iroh](src/roadmap/future-research.md). The `quicproquo-p2p` crate is a starting point.
|
- **Ref:** [Future research § LibP2P / iroh](src/roadmap/future-research.md). The `quicprochat-p2p` crate is a starting point.
|
||||||
|
|
||||||
### 6.2 WebTransport (browser client)
|
### 6.2 WebTransport (browser client)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Production Readiness Audit
|
# Production Readiness Audit
|
||||||
|
|
||||||
This document summarizes issues and fixes needed to get quicproquo production-ready, based on a codebase review. It aligns with the existing [Production Readiness WBS](src/roadmap/production-readiness.md) and [Coding Standards](src/contributing/coding-standards.md).
|
This document summarizes issues and fixes needed to get quicprochat production-ready, based on a codebase review. It aligns with the existing [Production Readiness WBS](src/roadmap/production-readiness.md) and [Coding Standards](src/contributing/coding-standards.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ This document summarizes issues and fixes needed to get quicproquo production-re
|
|||||||
|
|
||||||
- **README and example config** use `auth_token = "devtoken"` and `db_key = ""`.
|
- **README and example config** use `auth_token = "devtoken"` and `db_key = ""`.
|
||||||
- **Risk:** Deploying with default/example config allows weak or no auth and unencrypted DB.
|
- **Risk:** Deploying with default/example config allows weak or no auth and unencrypted DB.
|
||||||
- **Fix:** Require explicit `QPQ_AUTH_TOKEN` (or config) in production; reject empty or `"devtoken"` when a production mode/env is set. Document that `db_key` empty disables SQLCipher and is not acceptable for production.
|
- **Fix:** Require explicit `QPC_AUTH_TOKEN` (or config) in production; reject empty or `"devtoken"` when a production mode/env is set. Document that `db_key` empty disables SQLCipher and is not acceptable for production.
|
||||||
|
|
||||||
### 2. **Database encryption optional**
|
### 2. **Database encryption optional**
|
||||||
|
|
||||||
@@ -19,15 +19,15 @@ This document summarizes issues and fixes needed to get quicproquo production-re
|
|||||||
|
|
||||||
### 3. **Secrets and generated files not ignored**
|
### 3. **Secrets and generated files not ignored**
|
||||||
|
|
||||||
- **`.gitignore`** does not include `data/`, so `data/server-cert.der`, `data/server-key.der`, and `data/qpq.db` could be committed.
|
- **`.gitignore`** does not include `data/`, so `data/server-cert.der`, `data/server-key.der`, and `data/qpc.db` could be committed.
|
||||||
- **Fix:** Add `data/` (and any other dirs that hold certs, keys, or DBs) to `.gitignore`. Consider adding `*.der` and `*.db` if used only for local/dev.
|
- **Fix:** Add `data/` (and any other dirs that hold certs, keys, or DBs) to `.gitignore`. Consider adding `*.der` and `*.db` if used only for local/dev.
|
||||||
|
|
||||||
### 4. **Dockerfile out of sync with workspace**
|
### 4. **Dockerfile out of sync with workspace**
|
||||||
|
|
||||||
- **Workspace** has 5 members including `crates/quicproquo-p2p`.
|
- **Workspace** has 5 members including `crates/quicprochat-p2p`.
|
||||||
- **Dockerfile** only copies 4 crate manifests and creates stub dirs for those 4; it never copies `quicproquo-p2p`.
|
- **Dockerfile** only copies 4 crate manifests and creates stub dirs for those 4; it never copies `quicprochat-p2p`.
|
||||||
- **Result:** `cargo build --release --bin quicproquo-server` can fail (missing workspace member) or behave inconsistently.
|
- **Result:** `cargo build --release --bin quicprochat-server` can fail (missing workspace member) or behave inconsistently.
|
||||||
- **Fix:** Add `COPY crates/quicproquo-p2p/Cargo.toml` and a stub `crates/quicproquo-p2p/src` (or equivalent) in the dependency-cache layer so the workspace resolves. Ensure the final `COPY crates/ crates/` still brings in real p2p source.
|
- **Fix:** Add `COPY crates/quicprochat-p2p/Cargo.toml` and a stub `crates/quicprochat-p2p/src` (or equivalent) in the dependency-cache layer so the workspace resolves. Ensure the final `COPY crates/ crates/` still brings in real p2p source.
|
||||||
|
|
||||||
### 5. **E2E test failing (rustls CryptoProvider)**
|
### 5. **E2E test failing (rustls CryptoProvider)**
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ This document summarizes issues and fixes needed to get quicproquo production-re
|
|||||||
|
|
||||||
### 6. **Panic risk in client RPC path**
|
### 6. **Panic risk in client RPC path**
|
||||||
|
|
||||||
- **`quicproquo-client/src/lib.rs`:** `set_auth()` uses `.expect("init_auth must be called with a non-empty token before RPCs")`. If RPC is called without `init_auth`, the process panics.
|
- **`quicprochat-client/src/lib.rs`:** `set_auth()` uses `.expect("init_auth must be called with a non-empty token before RPCs")`. If RPC is called without `init_auth`, the process panics.
|
||||||
- **Fix:** Replace with a `Result` or an error return (e.g. a dedicated error type) so callers get a recoverable error instead of a panic. Document that `init_auth` must be called before RPCs.
|
- **Fix:** Replace with a `Result` or an error return (e.g. a dedicated error type) so callers get a recoverable error instead of a panic. Document that `init_auth` must be called before RPCs.
|
||||||
|
|
||||||
### 7. **Mutex `.unwrap()` in production paths**
|
### 7. **Mutex `.unwrap()` in production paths**
|
||||||
@@ -95,7 +95,7 @@ This document summarizes issues and fixes needed to get quicproquo production-re
|
|||||||
### 15. **Docker image runs as `nobody`**
|
### 15. **Docker image runs as `nobody`**
|
||||||
|
|
||||||
- **Dockerfile** uses `USER nobody`. Good for not running as root, but `nobody` may not have a writable home or data dir.
|
- **Dockerfile** uses `USER nobody`. Good for not running as root, but `nobody` may not have a writable home or data dir.
|
||||||
- **Fix:** Ensure `QPQ_DATA_DIR` (and cert paths) point to a directory writable by `nobody`, or create a dedicated user/group with a known UID and use that in the Dockerfile and docs.
|
- **Fix:** Ensure `QPC_DATA_DIR` (and cert paths) point to a directory writable by `nobody`, or create a dedicated user/group with a known UID and use that in the Dockerfile and docs.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Security Audit
|
# Security Audit
|
||||||
|
|
||||||
This document is a security audit of the quicproquo codebase as of the audit date. It aligns with the [Threat Model](src/cryptography/threat-model.md) and [Production Readiness Audit](PRODUCTION-READINESS-AUDIT.md). The project has **not** undergone a formal third-party audit; this is an internal review.
|
This document is a security audit of the quicprochat codebase as of the audit date. It aligns with the [Threat Model](src/cryptography/threat-model.md) and [Production Readiness Audit](PRODUCTION-READINESS-AUDIT.md). The project has **not** undergone a formal third-party audit; this is an internal review.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -24,19 +24,19 @@ This document is a security audit of the quicproquo codebase as of the audit dat
|
|||||||
|
|
||||||
### 1.1 Token comparison
|
### 1.1 Token comparison
|
||||||
|
|
||||||
- **Location:** `crates/quicproquo-server/src/auth.rs`
|
- **Location:** `crates/quicprochat-server/src/auth.rs`
|
||||||
- **Finding:** Bearer token and identity key comparisons use `subtle::ConstantTimeEq` (`ct_eq`). Length is checked before comparison where applicable.
|
- **Finding:** Bearer token and identity key comparisons use `subtle::ConstantTimeEq` (`ct_eq`). Length is checked before comparison where applicable.
|
||||||
- **Status:** ✅ No timing leakage from token or identity comparison.
|
- **Status:** ✅ No timing leakage from token or identity comparison.
|
||||||
|
|
||||||
### 1.2 Session token generation
|
### 1.2 Session token generation
|
||||||
|
|
||||||
- **Location:** `crates/quicproquo-server/src/node_service/auth_ops.rs` (login finish)
|
- **Location:** `crates/quicprochat-server/src/node_service/auth_ops.rs` (login finish)
|
||||||
- **Finding:** Session tokens are 32 bytes from `rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut token)`. Stored in `sessions` with TTL (24h). Expired sessions are removed on next use.
|
- **Finding:** Session tokens are 32 bytes from `rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut token)`. Stored in `sessions` with TTL (24h). Expired sessions are removed on next use.
|
||||||
- **Status:** ✅ Cryptographically strong, single-use style (opaque 32-byte token).
|
- **Status:** ✅ Cryptographically strong, single-use style (opaque 32-byte token).
|
||||||
|
|
||||||
### 1.3 OPAQUE (RFC 9497)
|
### 1.3 OPAQUE (RFC 9497)
|
||||||
|
|
||||||
- **Location:** `crates/quicproquo-core/src/opaque_auth.rs`, server `auth_ops.rs`
|
- **Location:** `crates/quicprochat-core/src/opaque_auth.rs`, server `auth_ops.rs`
|
||||||
- **Finding:** Shared `OpaqueSuite` (Ristretto255, Triple-DH, Argon2id). Server never sees password. Registration and login flows use `ServerRegistration`/`ServerLogin` correctly. Pending login state is stored server-side and removed on consume. Identity key is bound at login finish; mismatch returns E016 and is not logged with secrets.
|
- **Finding:** Shared `OpaqueSuite` (Ristretto255, Triple-DH, Argon2id). Server never sees password. Registration and login flows use `ServerRegistration`/`ServerLogin` correctly. Pending login state is stored server-side and removed on consume. Identity key is bound at login finish; mismatch returns E016 and is not logged with secrets.
|
||||||
- **DoS:** Pending-login check runs **before** expensive OPAQUE work (login start); repeated attempts for the same username within 60s are rejected early.
|
- **DoS:** Pending-login check runs **before** expensive OPAQUE work (login start); repeated attempts for the same username within 60s are rejected early.
|
||||||
- **Status:** ✅ Correct usage; DoS mitigation in place.
|
- **Status:** ✅ Correct usage; DoS mitigation in place.
|
||||||
@@ -52,19 +52,19 @@ This document is a security audit of the quicproquo codebase as of the audit dat
|
|||||||
|
|
||||||
### 2.1 MLS and identity
|
### 2.1 MLS and identity
|
||||||
|
|
||||||
- **Location:** `quicproquo-core` (group, identity, keypackage)
|
- **Location:** `quicprochat-core` (group, identity, keypackage)
|
||||||
- **Finding:** MLS ciphersuite `MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519` (RFC 9420). Ed25519 identity seed stored in `Zeroizing<[u8; 32]>`; zeroize-on-drop. KeyPackages validated for ciphersuite before server stores. Single-use KeyPackage semantics enforced (consume-on-fetch).
|
- **Finding:** MLS ciphersuite `MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519` (RFC 9420). Ed25519 identity seed stored in `Zeroizing<[u8; 32]>`; zeroize-on-drop. KeyPackages validated for ciphersuite before server stores. Single-use KeyPackage semantics enforced (consume-on-fetch).
|
||||||
- **Status:** ✅ Aligns with key lifecycle and zeroization goals.
|
- **Status:** ✅ Aligns with key lifecycle and zeroization goals.
|
||||||
|
|
||||||
### 2.2 Hybrid KEM (X25519 + ML-KEM-768)
|
### 2.2 Hybrid KEM (X25519 + ML-KEM-768)
|
||||||
|
|
||||||
- **Location:** `crates/quicproquo-core/src/hybrid_kem.rs`
|
- **Location:** `crates/quicprochat-core/src/hybrid_kem.rs`
|
||||||
- **Finding:** Hybrid keypair and shared secrets use `Zeroizing` where appropriate. HKDF domain separation (`quicproquo-hybrid-v1`). ChaCha20-Poly1305 for AEAD. Versioned envelope.
|
- **Finding:** Hybrid keypair and shared secrets use `Zeroizing` where appropriate. HKDF domain separation (`quicprochat-hybrid-v1`). ChaCha20-Poly1305 for AEAD. Versioned envelope.
|
||||||
- **Status:** ✅ PQ-ready envelope layer; secret handling is careful.
|
- **Status:** ✅ PQ-ready envelope layer; secret handling is careful.
|
||||||
|
|
||||||
### 2.3 Client state encryption (QPCE)
|
### 2.3 Client state encryption (QPCE)
|
||||||
|
|
||||||
- **Location:** `crates/quicproquo-client/src/client/state.rs`
|
- **Location:** `crates/quicprochat-client/src/client/state.rs`
|
||||||
- **Finding:** Optional password protection: Argon2id (default params) for key derivation, ChaCha20-Poly1305, random salt and nonce. Derived key held in `Zeroizing` during use. Unencrypted state is a documented option (e.g. dev).
|
- **Finding:** Optional password protection: Argon2id (default params) for key derivation, ChaCha20-Poly1305, random salt and nonce. Derived key held in `Zeroizing` during use. Unencrypted state is a documented option (e.g. dev).
|
||||||
- **Recommendation:** Document Argon2 params (memory, iterations) for auditability; consider explicit `Argon2::new()` with named params in a future revision.
|
- **Recommendation:** Document Argon2 params (memory, iterations) for auditability; consider explicit `Argon2::new()` with named params in a future revision.
|
||||||
- **Status:** ✅ Appropriate for optional at-rest protection.
|
- **Status:** ✅ Appropriate for optional at-rest protection.
|
||||||
@@ -75,13 +75,13 @@ This document is a security audit of the quicproquo codebase as of the audit dat
|
|||||||
|
|
||||||
### 3.1 Server TLS
|
### 3.1 Server TLS
|
||||||
|
|
||||||
- **Location:** `crates/quicproquo-server/src/tls.rs`
|
- **Location:** `crates/quicprochat-server/src/tls.rs`
|
||||||
- **Finding:** TLS 1.3 only. No client cert. ALPN `capnp`. When not in production, missing cert/key triggers self-signed generation; key file permissions set to `0o600` on Unix. Production mode requires existing cert/key (no auto-generation).
|
- **Finding:** TLS 1.3 only. No client cert. ALPN `capnp`. When not in production, missing cert/key triggers self-signed generation; key file permissions set to `0o600` on Unix. Production mode requires existing cert/key (no auto-generation).
|
||||||
- **Status:** ✅ Matches documented design; self-signed limitation is documented in threat model.
|
- **Status:** ✅ Matches documented design; self-signed limitation is documented in threat model.
|
||||||
|
|
||||||
### 3.2 Client TLS
|
### 3.2 Client TLS
|
||||||
|
|
||||||
- **Location:** `crates/quicproquo-client/src/client/rpc.rs`
|
- **Location:** `crates/quicprochat-client/src/client/rpc.rs`
|
||||||
- **Finding:** Client loads CA cert from file, builds `RootCertStore` with that single cert, uses it for server verification. Server name from CLI/env is used for connection (SNI and cert verification). No custom bypass.
|
- **Finding:** Client loads CA cert from file, builds `RootCertStore` with that single cert, uses it for server verification. Server name from CLI/env is used for connection (SNI and cert verification). No custom bypass.
|
||||||
- **Status:** ✅ Proper verification against provided CA; trust-on-first-use / self-signed caveat is documented.
|
- **Status:** ✅ Proper verification against provided CA; trust-on-first-use / self-signed caveat is documented.
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ This document is a security audit of the quicproquo codebase as of the audit dat
|
|||||||
|
|
||||||
### 4.2 Rate limiting
|
### 4.2 Rate limiting
|
||||||
|
|
||||||
- **Location:** `crates/quicproquo-server/src/auth.rs`, `delivery.rs`
|
- **Location:** `crates/quicprochat-server/src/auth.rs`, `delivery.rs`
|
||||||
- **Finding:** Per-token rate limit (e.g. 100 enqueues per 60s). Enqueue path checks before storage. Queue depth and payload size caps (1000 messages, 5 MB) enforced.
|
- **Finding:** Per-token rate limit (e.g. 100 enqueues per 60s). Enqueue path checks before storage. Queue depth and payload size caps (1000 messages, 5 MB) enforced.
|
||||||
- **Status:** ✅ Limits in place to curb abuse and DoS.
|
- **Status:** ✅ Limits in place to curb abuse and DoS.
|
||||||
|
|
||||||
@@ -156,11 +156,11 @@ These remain as documented, not new findings:
|
|||||||
### 8.1 High value
|
### 8.1 High value
|
||||||
|
|
||||||
- **Dependency audit:** Run `cargo install cargo-audit` then `cargo audit` locally (and in CI if available) to check for known-vulnerable dependencies. Fix or document any findings. See [Checking dependencies](#checking-dependencies) below.
|
- **Dependency audit:** Run `cargo install cargo-audit` then `cargo audit` locally (and in CI if available) to check for known-vulnerable dependencies. Fix or document any findings. See [Checking dependencies](#checking-dependencies) below.
|
||||||
- **Argon2 params:** Implemented: client state KDF now uses explicit Argon2id parameters (19 MiB memory, 2 iterations, 1 lane) in `quicproquo-client` so they are auditable.
|
- **Argon2 params:** Implemented: client state KDF now uses explicit Argon2id parameters (19 MiB memory, 2 iterations, 1 lane) in `quicprochat-client` so they are auditable.
|
||||||
|
|
||||||
### 8.2 Medium value
|
### 8.2 Medium value
|
||||||
|
|
||||||
- **Certificate pinning:** To pin the server, use the server's certificate as the client's `ca_cert` (e.g. copy `server-cert.der` from the server and pass it via `--ca-cert` or `QPQ_CA_CERT`). Do not use a general CA unless you intend to trust that CA for all servers. See [Certificate pinning](#certificate-pinning) below.
|
- **Certificate pinning:** To pin the server, use the server's certificate as the client's `ca_cert` (e.g. copy `server-cert.der` from the server and pass it via `--ca-cert` or `QPC_CA_CERT`). Do not use a general CA unless you intend to trust that CA for all servers. See [Certificate pinning](#certificate-pinning) below.
|
||||||
- **Health endpoint:** The `health` RPC is unauthenticated by design for liveness probes and load balancers; this is documented in code and in this audit.
|
- **Health endpoint:** The `health` RPC is unauthenticated by design for liveness probes and load balancers; this is documented in code and in this audit.
|
||||||
|
|
||||||
### 8.3 Lower priority
|
### 8.3 Lower priority
|
||||||
@@ -208,7 +208,7 @@ Fix or document any reported issues. Running `cargo audit` in CI (e.g. GitHub Ac
|
|||||||
|
|
||||||
## Certificate pinning
|
## Certificate pinning
|
||||||
|
|
||||||
The client trusts the server certificate(s) in the file given by `--ca-cert` (or `QPQ_CA_CERT`). To **pin** a specific server:
|
The client trusts the server certificate(s) in the file given by `--ca-cert` (or `QPC_CA_CERT`). To **pin** a specific server:
|
||||||
|
|
||||||
1. Obtain the server's certificate (e.g. copy `data/server-cert.der` from the server, or export from your deployment).
|
1. Obtain the server's certificate (e.g. copy `data/server-cert.der` from the server, or export from your deployment).
|
||||||
2. Use that file as the client's `ca_cert`. The client will only connect to a server that presents that exact certificate (or chain).
|
2. Use that file as the client's `ca_cert`. The client will only connect to a server that presents that exact certificate (or chain).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# OpenWrt Deployment Guide
|
# OpenWrt Deployment Guide
|
||||||
|
|
||||||
Run quicproquo on OpenWrt routers for mesh-capable, always-on encrypted messaging at the network edge.
|
Run quicprochat on OpenWrt routers for mesh-capable, always-on encrypted messaging at the network edge.
|
||||||
|
|
||||||
## Supported Targets
|
## Supported Targets
|
||||||
|
|
||||||
@@ -44,10 +44,10 @@ CARGO_PROFILE_RELEASE_OPT_LEVEL=s \
|
|||||||
CARGO_PROFILE_RELEASE_LTO=true \
|
CARGO_PROFILE_RELEASE_LTO=true \
|
||||||
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 \
|
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 \
|
||||||
CARGO_PROFILE_RELEASE_STRIP=symbols \
|
CARGO_PROFILE_RELEASE_STRIP=symbols \
|
||||||
cargo zigbuild --release --target x86_64-unknown-linux-musl --bin qpq-server
|
cargo zigbuild --release --target x86_64-unknown-linux-musl --bin qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
The binary lands at `target/<triple>/release/qpq-server`. Target size: under 5 MB.
|
The binary lands at `target/<triple>/release/qpc-server`. Target size: under 5 MB.
|
||||||
|
|
||||||
## OpenWrt Package Installation
|
## OpenWrt Package Installation
|
||||||
|
|
||||||
@@ -55,14 +55,14 @@ The binary lands at `target/<triple>/release/qpq-server`. Target size: under 5 M
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Copy binary to router
|
# Copy binary to router
|
||||||
scp target/aarch64-unknown-linux-musl/release/qpq-server root@router:/usr/bin/
|
scp target/aarch64-unknown-linux-musl/release/qpc-server root@router:/usr/bin/
|
||||||
|
|
||||||
# Copy init script and config
|
# Copy init script and config
|
||||||
scp packaging/openwrt/files/quicproquo.init root@router:/etc/init.d/quicproquo
|
scp packaging/openwrt/files/quicprochat.init root@router:/etc/init.d/quicprochat
|
||||||
scp packaging/openwrt/files/quicproquo.uci root@router:/etc/config/quicproquo
|
scp packaging/openwrt/files/quicprochat.uci root@router:/etc/config/quicprochat
|
||||||
|
|
||||||
# Enable and start
|
# Enable and start
|
||||||
ssh root@router 'chmod +x /etc/init.d/quicproquo && /etc/init.d/quicproquo enable && /etc/init.d/quicproquo start'
|
ssh root@router 'chmod +x /etc/init.d/quicprochat && /etc/init.d/quicprochat enable && /etc/init.d/quicprochat start'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Option 2: opkg package feed
|
### Option 2: opkg package feed
|
||||||
@@ -71,28 +71,28 @@ Add the feed to your OpenWrt build system:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# In your OpenWrt buildroot, add to feeds.conf:
|
# In your OpenWrt buildroot, add to feeds.conf:
|
||||||
echo "src-link quicproquo /path/to/quicproquo/packaging/openwrt" >> feeds.conf
|
echo "src-link quicprochat /path/to/quicprochat/packaging/openwrt" >> feeds.conf
|
||||||
|
|
||||||
# Update and install
|
# Update and install
|
||||||
./scripts/feeds update quicproquo
|
./scripts/feeds update quicprochat
|
||||||
./scripts/feeds install quicproquo
|
./scripts/feeds install quicprochat
|
||||||
|
|
||||||
# Select in menuconfig: Network -> quicproquo
|
# Select in menuconfig: Network -> quicprochat
|
||||||
make menuconfig
|
make menuconfig
|
||||||
make package/quicproquo/compile V=s
|
make package/quicprochat/compile V=s
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The server is configured via UCI at `/etc/config/quicproquo`:
|
The server is configured via UCI at `/etc/config/quicprochat`:
|
||||||
|
|
||||||
```
|
```
|
||||||
config server 'server'
|
config server 'server'
|
||||||
option listen '0.0.0.0:7000'
|
option listen '0.0.0.0:7000'
|
||||||
option data_dir '/var/lib/quicproquo'
|
option data_dir '/var/lib/quicprochat'
|
||||||
option log_level 'info'
|
option log_level 'info'
|
||||||
option tls_cert '/var/lib/quicproquo/server-cert.der'
|
option tls_cert '/var/lib/quicprochat/server-cert.der'
|
||||||
option tls_key '/var/lib/quicproquo/server-key.der'
|
option tls_key '/var/lib/quicprochat/server-key.der'
|
||||||
option production '1'
|
option production '1'
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ config server 'server'
|
|||||||
| Option | Default | Description |
|
| Option | Default | Description |
|
||||||
|--------------|------------------------------------------|----------------------------------|
|
|--------------|------------------------------------------|----------------------------------|
|
||||||
| `listen` | `0.0.0.0:7000` | QUIC listen address |
|
| `listen` | `0.0.0.0:7000` | QUIC listen address |
|
||||||
| `data_dir` | `/var/lib/quicproquo` | Persistent data directory |
|
| `data_dir` | `/var/lib/quicprochat` | Persistent data directory |
|
||||||
| `log_level` | `info` | RUST_LOG filter |
|
| `log_level` | `info` | RUST_LOG filter |
|
||||||
| `tls_cert` | `<data_dir>/server-cert.der` | TLS certificate path (DER) |
|
| `tls_cert` | `<data_dir>/server-cert.der` | TLS certificate path (DER) |
|
||||||
| `tls_key` | `<data_dir>/server-key.der` | TLS private key path (DER) |
|
| `tls_key` | `<data_dir>/server-key.der` | TLS private key path (DER) |
|
||||||
@@ -111,15 +111,15 @@ config server 'server'
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Start / stop / restart
|
# Start / stop / restart
|
||||||
/etc/init.d/quicproquo start
|
/etc/init.d/quicprochat start
|
||||||
/etc/init.d/quicproquo stop
|
/etc/init.d/quicprochat stop
|
||||||
/etc/init.d/quicproquo restart
|
/etc/init.d/quicprochat restart
|
||||||
|
|
||||||
# Enable at boot
|
# Enable at boot
|
||||||
/etc/init.d/quicproquo enable
|
/etc/init.d/quicprochat enable
|
||||||
|
|
||||||
# View logs
|
# View logs
|
||||||
logread -e quicproquo
|
logread -e quicprochat
|
||||||
```
|
```
|
||||||
|
|
||||||
## Binary Size Optimization
|
## Binary Size Optimization
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
# Backup and Restore Procedures
|
# Backup and Restore Procedures
|
||||||
|
|
||||||
This document covers backup and restore for all quicproquo server data stores.
|
This document covers backup and restore for all quicprochat server data stores.
|
||||||
|
|
||||||
## Data Inventory
|
## Data Inventory
|
||||||
|
|
||||||
| Data | Location | Backend | Contains |
|
| Data | Location | Backend | Contains |
|
||||||
|------|----------|---------|----------|
|
|------|----------|---------|----------|
|
||||||
| SQLCipher DB | `QPQ_DB_PATH` (default `data/qpq.db`) | `store_backend=sql` | Users, key packages, delivery queues, sessions, KT log, OPAQUE setup, blobs metadata, moderation |
|
| SQLCipher DB | `QPC_DB_PATH` (default `data/qpc.db`) | `store_backend=sql` | Users, key packages, delivery queues, sessions, KT log, OPAQUE setup, blobs metadata, moderation |
|
||||||
| File store | `QPQ_DATA_DIR` (default `data/`) | `store_backend=file` | Bincode-serialized key packages, delivery queues, server state |
|
| File store | `QPC_DATA_DIR` (default `data/`) | `store_backend=file` | Bincode-serialized key packages, delivery queues, server state |
|
||||||
| Blob storage | `QPQ_DATA_DIR/blobs/` | Filesystem | Uploaded file transfer blobs |
|
| Blob storage | `QPC_DATA_DIR/blobs/` | Filesystem | Uploaded file transfer blobs |
|
||||||
| TLS certificates | `QPQ_TLS_CERT`, `QPQ_TLS_KEY` | DER files | Server identity |
|
| TLS certificates | `QPC_TLS_CERT`, `QPC_TLS_KEY` | DER files | Server identity |
|
||||||
| OPAQUE ServerSetup | Inside DB or file store | Persisted | OPAQUE credential state (critical for auth) |
|
| OPAQUE ServerSetup | Inside DB or file store | Persisted | OPAQUE credential state (critical for auth) |
|
||||||
| Server signing key | Inside DB or file store | Persisted | Ed25519 key for delivery proofs |
|
| Server signing key | Inside DB or file store | Persisted | Ed25519 key for delivery proofs |
|
||||||
| KT Merkle log | Inside DB or file store | Persisted | Key transparency audit log |
|
| KT Merkle log | Inside DB or file store | Persisted | Key transparency audit log |
|
||||||
@@ -22,13 +22,13 @@ SQLCipher supports the `.backup` command while the server is running (WAL mode a
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Open the encrypted database with the same key
|
# 1. Open the encrypted database with the same key
|
||||||
sqlite3 data/qpq.db
|
sqlite3 data/qpc.db
|
||||||
|
|
||||||
# 2. At the sqlite3 prompt, set the encryption key
|
# 2. At the sqlite3 prompt, set the encryption key
|
||||||
PRAGMA key = 'your-db-key-here';
|
PRAGMA key = 'your-db-key-here';
|
||||||
|
|
||||||
# 3. Perform an online backup
|
# 3. Perform an online backup
|
||||||
.backup /backups/qpq-$(date +%Y%m%d-%H%M%S).db
|
.backup /backups/qpc-$(date +%Y%m%d-%H%M%S).db
|
||||||
|
|
||||||
.quit
|
.quit
|
||||||
```
|
```
|
||||||
@@ -39,11 +39,11 @@ PRAGMA key = 'your-db-key-here';
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
BACKUP_DIR="/backups/qpq"
|
BACKUP_DIR="/backups/qpc"
|
||||||
DB_PATH="${QPQ_DB_PATH:-data/qpq.db}"
|
DB_PATH="${QPC_DB_PATH:-data/qpc.db}"
|
||||||
DB_KEY="${QPQ_DB_KEY}"
|
DB_KEY="${QPC_DB_KEY}"
|
||||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||||
BACKUP_FILE="${BACKUP_DIR}/qpq-${TIMESTAMP}.db"
|
BACKUP_FILE="${BACKUP_DIR}/qpc-${TIMESTAMP}.db"
|
||||||
|
|
||||||
mkdir -p "$BACKUP_DIR"
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
@@ -58,40 +58,40 @@ sqlite3 "$BACKUP_FILE" "PRAGMA key = '${DB_KEY}'; PRAGMA integrity_check;" \
|
|||||||
|| { echo "ERROR: backup verification failed"; exit 1; }
|
|| { echo "ERROR: backup verification failed"; exit 1; }
|
||||||
|
|
||||||
# Retain last 7 daily backups
|
# Retain last 7 daily backups
|
||||||
find "$BACKUP_DIR" -name 'qpq-*.db' -mtime +7 -delete
|
find "$BACKUP_DIR" -name 'qpc-*.db' -mtime +7 -delete
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cold Backup (Offline)
|
### Cold Backup (Offline)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Stop the server
|
# 1. Stop the server
|
||||||
systemctl stop qpq-server # or docker compose stop server
|
systemctl stop qpc-server # or docker compose stop server
|
||||||
|
|
||||||
# 2. Copy the database file
|
# 2. Copy the database file
|
||||||
cp data/qpq.db /backups/qpq-$(date +%Y%m%d).db
|
cp data/qpc.db /backups/qpc-$(date +%Y%m%d).db
|
||||||
|
|
||||||
# 3. Copy the WAL and SHM files if they exist
|
# 3. Copy the WAL and SHM files if they exist
|
||||||
cp data/qpq.db-wal /backups/ 2>/dev/null || true
|
cp data/qpc.db-wal /backups/ 2>/dev/null || true
|
||||||
cp data/qpq.db-shm /backups/ 2>/dev/null || true
|
cp data/qpc.db-shm /backups/ 2>/dev/null || true
|
||||||
|
|
||||||
# 4. Restart the server
|
# 4. Restart the server
|
||||||
systemctl start qpq-server
|
systemctl start qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
## File Backend Backup
|
## File Backend Backup
|
||||||
|
|
||||||
When using `store_backend=file`, data is stored as bincode files under `QPQ_DATA_DIR`.
|
When using `store_backend=file`, data is stored as bincode files under `QPC_DATA_DIR`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Full directory backup
|
# Full directory backup
|
||||||
tar czf /backups/qpq-data-$(date +%Y%m%d-%H%M%S).tar.gz \
|
tar czf /backups/qpc-data-$(date +%Y%m%d-%H%M%S).tar.gz \
|
||||||
-C "$(dirname "${QPQ_DATA_DIR:-data}")" \
|
-C "$(dirname "${QPC_DATA_DIR:-data}")" \
|
||||||
"$(basename "${QPQ_DATA_DIR:-data}")"
|
"$(basename "${QPC_DATA_DIR:-data}")"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Blob Storage Backup
|
## Blob Storage Backup
|
||||||
|
|
||||||
Blobs are stored in `QPQ_DATA_DIR/blobs/`. These are immutable once written.
|
Blobs are stored in `QPC_DATA_DIR/blobs/`. These are immutable once written.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Incremental rsync (blobs are write-once, ideal for rsync)
|
# Incremental rsync (blobs are write-once, ideal for rsync)
|
||||||
@@ -117,38 +117,38 @@ cp data/federation-ca.der /backups/tls/federation-ca.der 2>/dev/null || true
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Stop the server
|
# 1. Stop the server
|
||||||
systemctl stop qpq-server
|
systemctl stop qpc-server
|
||||||
|
|
||||||
# 2. Move the current (corrupt/lost) database aside
|
# 2. Move the current (corrupt/lost) database aside
|
||||||
mv data/qpq.db data/qpq.db.broken 2>/dev/null || true
|
mv data/qpc.db data/qpc.db.broken 2>/dev/null || true
|
||||||
rm -f data/qpq.db-wal data/qpq.db-shm
|
rm -f data/qpc.db-wal data/qpc.db-shm
|
||||||
|
|
||||||
# 3. Copy the backup in place
|
# 3. Copy the backup in place
|
||||||
cp /backups/qpq-20260304.db data/qpq.db
|
cp /backups/qpc-20260304.db data/qpc.db
|
||||||
|
|
||||||
# 4. Verify integrity
|
# 4. Verify integrity
|
||||||
sqlite3 data/qpq.db "PRAGMA key = '${QPQ_DB_KEY}'; PRAGMA integrity_check;"
|
sqlite3 data/qpc.db "PRAGMA key = '${QPC_DB_KEY}'; PRAGMA integrity_check;"
|
||||||
|
|
||||||
# 5. Start the server (migrations will apply automatically if needed)
|
# 5. Start the server (migrations will apply automatically if needed)
|
||||||
systemctl start qpq-server
|
systemctl start qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
### Restore File Backend
|
### Restore File Backend
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Stop the server
|
# 1. Stop the server
|
||||||
systemctl stop qpq-server
|
systemctl stop qpc-server
|
||||||
|
|
||||||
# 2. Replace the data directory
|
# 2. Replace the data directory
|
||||||
mv data data.broken 2>/dev/null || true
|
mv data data.broken 2>/dev/null || true
|
||||||
tar xzf /backups/qpq-data-20260304.tar.gz -C .
|
tar xzf /backups/qpc-data-20260304.tar.gz -C .
|
||||||
|
|
||||||
# 3. Restore TLS certs if not included in the data backup
|
# 3. Restore TLS certs if not included in the data backup
|
||||||
cp /backups/tls/server-cert.der data/server-cert.der
|
cp /backups/tls/server-cert.der data/server-cert.der
|
||||||
cp /backups/tls/server-key.der data/server-key.der
|
cp /backups/tls/server-key.der data/server-key.der
|
||||||
|
|
||||||
# 4. Start the server
|
# 4. Start the server
|
||||||
systemctl start qpq-server
|
systemctl start qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
### Restore Blobs Only
|
### Restore Blobs Only
|
||||||
@@ -170,16 +170,16 @@ rsync -av /backups/blobs/ data/blobs/
|
|||||||
|
|
||||||
```cron
|
```cron
|
||||||
# SQLCipher hot backup every 6 hours
|
# SQLCipher hot backup every 6 hours
|
||||||
0 */6 * * * /opt/qpq/scripts/backup-db.sh >> /var/log/qpq-backup.log 2>&1
|
0 */6 * * * /opt/qpc/scripts/backup-db.sh >> /var/log/qpc-backup.log 2>&1
|
||||||
|
|
||||||
# Full data directory daily at 02:00
|
# Full data directory daily at 02:00
|
||||||
0 2 * * * tar czf /backups/qpq-data-$(date +\%Y\%m\%d).tar.gz -C /var/lib quicproquo
|
0 2 * * * tar czf /backups/qpc-data-$(date +\%Y\%m\%d).tar.gz -C /var/lib quicprochat
|
||||||
|
|
||||||
# Blob sync every hour
|
# Blob sync every hour
|
||||||
0 * * * * rsync -a /var/lib/quicproquo/blobs/ /backups/blobs/
|
0 * * * * rsync -a /var/lib/quicprochat/blobs/ /backups/blobs/
|
||||||
|
|
||||||
# Prune backups older than 30 days
|
# Prune backups older than 30 days
|
||||||
0 3 * * 0 find /backups -name 'qpq-*' -mtime +30 -delete
|
0 3 * * 0 find /backups -name 'qpc-*' -mtime +30 -delete
|
||||||
```
|
```
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
@@ -188,11 +188,11 @@ Always verify backups after creation:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# SQLCipher integrity check
|
# SQLCipher integrity check
|
||||||
sqlite3 /backups/qpq-latest.db \
|
sqlite3 /backups/qpc-latest.db \
|
||||||
"PRAGMA key = '${QPQ_DB_KEY}'; PRAGMA integrity_check; SELECT count(*) FROM users;"
|
"PRAGMA key = '${QPC_DB_KEY}'; PRAGMA integrity_check; SELECT count(*) FROM users;"
|
||||||
|
|
||||||
# File backend: check the archive is valid
|
# File backend: check the archive is valid
|
||||||
tar tzf /backups/qpq-data-latest.tar.gz > /dev/null
|
tar tzf /backups/qpc-data-latest.tar.gz > /dev/null
|
||||||
|
|
||||||
# TLS cert: check it parses and is not expired
|
# TLS cert: check it parses and is not expired
|
||||||
openssl x509 -inform DER -in /backups/tls/server-cert.der -noout -dates
|
openssl x509 -inform DER -in /backups/tls/server-cert.der -noout -dates
|
||||||
|
|||||||
@@ -42,10 +42,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": null,
|
"id": null,
|
||||||
"uid": "qpq-overview",
|
"uid": "qpc-overview",
|
||||||
"title": "quicproquo Server Overview",
|
"title": "quicprochat Server Overview",
|
||||||
"description": "Operational dashboard for quicproquo server instances",
|
"description": "Operational dashboard for quicprochat server instances",
|
||||||
"tags": ["quicproquo", "qpq"],
|
"tags": ["quicprochat", "qpc"],
|
||||||
"timezone": "browser",
|
"timezone": "browser",
|
||||||
"editable": true,
|
"editable": true,
|
||||||
"graphTooltip": 1,
|
"graphTooltip": 1,
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"gridPos": { "h": 4, "w": 4, "x": 0, "y": 0 },
|
"gridPos": { "h": 4, "w": 4, "x": 0, "y": 0 },
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"expr": "up{job=\"qpq-server\"}",
|
"expr": "up{job=\"qpc-server\"}",
|
||||||
"legendFormat": "{{instance}}"
|
"legendFormat": "{{instance}}"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -3,7 +3,7 @@ apiVersion: 1
|
|||||||
providers:
|
providers:
|
||||||
- name: 'default'
|
- name: 'default'
|
||||||
orgId: 1
|
orgId: 1
|
||||||
folder: 'quicproquo'
|
folder: 'quicprochat'
|
||||||
type: file
|
type: file
|
||||||
disableDeletion: false
|
disableDeletion: false
|
||||||
editable: true
|
editable: true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Incident Response Playbook
|
# Incident Response Playbook
|
||||||
|
|
||||||
This document provides procedures for responding to common operational incidents in a quicproquo deployment.
|
This document provides procedures for responding to common operational incidents in a quicprochat deployment.
|
||||||
|
|
||||||
## Severity Levels
|
## Severity Levels
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ This document provides procedures for responding to common operational incidents
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check server logs
|
# Check server logs
|
||||||
journalctl -u qpq-server --since "10 min ago" --no-pager
|
journalctl -u qpc-server --since "10 min ago" --no-pager
|
||||||
|
|
||||||
# Docker
|
# Docker
|
||||||
docker compose logs --tail=50 server
|
docker compose logs --tail=50 server
|
||||||
@@ -39,17 +39,17 @@ ls -la data/server-cert.der data/server-key.der
|
|||||||
|
|
||||||
**Missing auth token (production mode)**
|
**Missing auth token (production mode)**
|
||||||
```bash
|
```bash
|
||||||
# Production requires QPQ_AUTH_TOKEN >= 16 chars, not "devtoken"
|
# Production requires QPC_AUTH_TOKEN >= 16 chars, not "devtoken"
|
||||||
echo $QPQ_AUTH_TOKEN | wc -c
|
echo $QPC_AUTH_TOKEN | wc -c
|
||||||
```
|
```
|
||||||
|
|
||||||
**Database locked or corrupt**
|
**Database locked or corrupt**
|
||||||
```bash
|
```bash
|
||||||
# Check if another process holds the database
|
# Check if another process holds the database
|
||||||
fuser data/qpq.db
|
fuser data/qpc.db
|
||||||
|
|
||||||
# Verify database integrity
|
# Verify database integrity
|
||||||
sqlite3 data/qpq.db "PRAGMA key='${QPQ_DB_KEY}'; PRAGMA integrity_check;"
|
sqlite3 data/qpc.db "PRAGMA key='${QPC_DB_KEY}'; PRAGMA integrity_check;"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Port already in use**
|
**Port already in use**
|
||||||
@@ -69,12 +69,12 @@ ss -tlnp | grep 7000
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Check if the process is running
|
# 1. Check if the process is running
|
||||||
systemctl status qpq-server
|
systemctl status qpc-server
|
||||||
# or: docker compose ps
|
# or: docker compose ps
|
||||||
|
|
||||||
# 2. Check resource usage
|
# 2. Check resource usage
|
||||||
top -bn1 | grep qpq
|
top -bn1 | grep qpc
|
||||||
df -h /var/lib/quicproquo
|
df -h /var/lib/quicprochat
|
||||||
free -h
|
free -h
|
||||||
|
|
||||||
# 3. Check QUIC port is reachable
|
# 3. Check QUIC port is reachable
|
||||||
@@ -90,10 +90,10 @@ journalctl -k | grep -i oom
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Restart the service
|
# Restart the service
|
||||||
systemctl restart qpq-server
|
systemctl restart qpc-server
|
||||||
|
|
||||||
# If OOM: increase memory limit
|
# If OOM: increase memory limit
|
||||||
systemctl edit qpq-server --force
|
systemctl edit qpc-server --force
|
||||||
# MemoryMax=2G
|
# MemoryMax=2G
|
||||||
|
|
||||||
# If disk full: see "Storage Full" incident below
|
# If disk full: see "Storage Full" incident below
|
||||||
@@ -110,15 +110,15 @@ systemctl edit qpq-server --force
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check disk usage
|
# Check disk usage
|
||||||
df -h /var/lib/quicproquo
|
df -h /var/lib/quicprochat
|
||||||
du -sh /var/lib/quicproquo/*
|
du -sh /var/lib/quicprochat/*
|
||||||
|
|
||||||
# Check largest files
|
# Check largest files
|
||||||
du -a /var/lib/quicproquo | sort -rn | head -20
|
du -a /var/lib/quicprochat | sort -rn | head -20
|
||||||
|
|
||||||
# Check blob storage specifically
|
# Check blob storage specifically
|
||||||
du -sh /var/lib/quicproquo/blobs/
|
du -sh /var/lib/quicprochat/blobs/
|
||||||
find /var/lib/quicproquo/blobs/ -type f | wc -l
|
find /var/lib/quicprochat/blobs/ -type f | wc -l
|
||||||
```
|
```
|
||||||
|
|
||||||
### Recovery
|
### Recovery
|
||||||
@@ -128,8 +128,8 @@ find /var/lib/quicproquo/blobs/ -type f | wc -l
|
|||||||
# but if it's behind, you can trigger manual cleanup)
|
# but if it's behind, you can trigger manual cleanup)
|
||||||
|
|
||||||
# For SQL backend: delete expired delivery messages
|
# For SQL backend: delete expired delivery messages
|
||||||
sqlite3 data/qpq.db <<'EOF'
|
sqlite3 data/qpc.db <<'EOF'
|
||||||
PRAGMA key = '${QPQ_DB_KEY}';
|
PRAGMA key = '${QPC_DB_KEY}';
|
||||||
DELETE FROM delivery_queue WHERE expires_at IS NOT NULL AND expires_at < unixepoch();
|
DELETE FROM delivery_queue WHERE expires_at IS NOT NULL AND expires_at < unixepoch();
|
||||||
VACUUM;
|
VACUUM;
|
||||||
EOF
|
EOF
|
||||||
@@ -142,10 +142,10 @@ EOF
|
|||||||
# Then: resize2fs /dev/xvdf
|
# Then: resize2fs /dev/xvdf
|
||||||
|
|
||||||
# 4. Move to a larger disk
|
# 4. Move to a larger disk
|
||||||
systemctl stop qpq-server
|
systemctl stop qpc-server
|
||||||
rsync -av /var/lib/quicproquo/ /mnt/new-volume/quicproquo/
|
rsync -av /var/lib/quicprochat/ /mnt/new-volume/quicprochat/
|
||||||
# Update QPQ_DATA_DIR and QPQ_DB_PATH to point to the new location
|
# Update QPC_DATA_DIR and QPC_DB_PATH to point to the new location
|
||||||
systemctl start qpq-server
|
systemctl start qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
### Prevention
|
### Prevention
|
||||||
@@ -188,10 +188,10 @@ iptables -A INPUT -s <attacker-ip> -j DROP
|
|||||||
# (Cloudflare Spectrum, AWS Shield, etc.)
|
# (Cloudflare Spectrum, AWS Shield, etc.)
|
||||||
|
|
||||||
# 4. If the server is overwhelmed, restart to clear state
|
# 4. If the server is overwhelmed, restart to clear state
|
||||||
systemctl restart qpq-server
|
systemctl restart qpc-server
|
||||||
|
|
||||||
# 5. Enable log redaction to reduce I/O pressure during attacks
|
# 5. Enable log redaction to reduce I/O pressure during attacks
|
||||||
# Set QPQ_REDACT_LOGS=true
|
# Set QPC_REDACT_LOGS=true
|
||||||
```
|
```
|
||||||
|
|
||||||
## Incident: Key Compromise
|
## Incident: Key Compromise
|
||||||
@@ -210,7 +210,7 @@ NEW_TOKEN=$(openssl rand -base64 32)
|
|||||||
# 3. Notify all legitimate clients of the new token
|
# 3. Notify all legitimate clients of the new token
|
||||||
|
|
||||||
# 4. Review logs for unauthorized access
|
# 4. Review logs for unauthorized access
|
||||||
journalctl -u qpq-server | grep "auth_login_success" | tail -100
|
journalctl -u qpc-server | grep "auth_login_success" | tail -100
|
||||||
```
|
```
|
||||||
|
|
||||||
### TLS Private Key Compromised
|
### TLS Private Key Compromised
|
||||||
@@ -225,7 +225,7 @@ journalctl -u qpq-server | grep "auth_login_success" | tail -100
|
|||||||
# (procedure depends on your CA)
|
# (procedure depends on your CA)
|
||||||
|
|
||||||
# 3. Restart the server with the new certificate
|
# 3. Restart the server with the new certificate
|
||||||
systemctl restart qpq-server
|
systemctl restart qpc-server
|
||||||
|
|
||||||
# 4. If clients pin certificates, notify them of the change
|
# 4. If clients pin certificates, notify them of the change
|
||||||
```
|
```
|
||||||
@@ -236,7 +236,7 @@ systemctl restart qpq-server
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Stop the server
|
# 1. Stop the server
|
||||||
systemctl stop qpq-server
|
systemctl stop qpc-server
|
||||||
|
|
||||||
# 2. Rekey the database immediately
|
# 2. Rekey the database immediately
|
||||||
# See: key-rotation.md "Database Encryption Key Rotation"
|
# See: key-rotation.md "Database Encryption Key Rotation"
|
||||||
@@ -278,8 +278,8 @@ top -bn1 | head -20
|
|||||||
iostat -x 1 3
|
iostat -x 1 3
|
||||||
|
|
||||||
# 2. Check database performance
|
# 2. Check database performance
|
||||||
sqlite3 data/qpq.db <<'EOF'
|
sqlite3 data/qpc.db <<'EOF'
|
||||||
PRAGMA key = '${QPQ_DB_KEY}';
|
PRAGMA key = '${QPC_DB_KEY}';
|
||||||
PRAGMA integrity_check;
|
PRAGMA integrity_check;
|
||||||
PRAGMA wal_checkpoint(PASSIVE);
|
PRAGMA wal_checkpoint(PASSIVE);
|
||||||
-- Check table sizes
|
-- Check table sizes
|
||||||
@@ -296,10 +296,10 @@ curl -s http://localhost:9090/metrics | grep delivery_queue_depth
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Checkpoint the WAL (reduces WAL file size)
|
# 1. Checkpoint the WAL (reduces WAL file size)
|
||||||
sqlite3 data/qpq.db "PRAGMA key='${QPQ_DB_KEY}'; PRAGMA wal_checkpoint(TRUNCATE);"
|
sqlite3 data/qpc.db "PRAGMA key='${QPC_DB_KEY}'; PRAGMA wal_checkpoint(TRUNCATE);"
|
||||||
|
|
||||||
# 2. VACUUM to reclaim space and defragment
|
# 2. VACUUM to reclaim space and defragment
|
||||||
sqlite3 data/qpq.db "PRAGMA key='${QPQ_DB_KEY}'; VACUUM;"
|
sqlite3 data/qpc.db "PRAGMA key='${QPC_DB_KEY}'; VACUUM;"
|
||||||
|
|
||||||
# 3. If the queue is huge, check for clients not fetching
|
# 3. If the queue is huge, check for clients not fetching
|
||||||
# (delivery_queue rows accumulate when clients are offline)
|
# (delivery_queue rows accumulate when clients are offline)
|
||||||
@@ -323,7 +323,7 @@ openssl x509 -inform DER -in data/server-cert.der -noout -enddate
|
|||||||
# See: key-rotation.md "TLS Certificate Rotation"
|
# See: key-rotation.md "TLS Certificate Rotation"
|
||||||
|
|
||||||
# 3. Verify the new certificate is loaded
|
# 3. Verify the new certificate is loaded
|
||||||
journalctl -u qpq-server --since "1 min ago" | grep -i cert
|
journalctl -u qpc-server --since "1 min ago" | grep -i cert
|
||||||
```
|
```
|
||||||
|
|
||||||
## Post-Incident Checklist
|
## Post-Incident Checklist
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Key Rotation Procedures
|
# Key Rotation Procedures
|
||||||
|
|
||||||
This document provides step-by-step procedures for rotating all cryptographic material in a quicproquo deployment.
|
This document provides step-by-step procedures for rotating all cryptographic material in a quicprochat deployment.
|
||||||
|
|
||||||
## Auth Token Rotation
|
## Auth Token Rotation
|
||||||
|
|
||||||
The auth token (`QPQ_AUTH_TOKEN`) is used for bearer-token authentication (auth version 1). OPAQUE-authenticated sessions are not affected by token rotation.
|
The auth token (`QPC_AUTH_TOKEN`) is used for bearer-token authentication (auth version 1). OPAQUE-authenticated sessions are not affected by token rotation.
|
||||||
|
|
||||||
### Procedure
|
### Procedure
|
||||||
|
|
||||||
@@ -15,22 +15,22 @@ echo "New token: $NEW_TOKEN"
|
|||||||
|
|
||||||
# 2. Update the config file or environment
|
# 2. Update the config file or environment
|
||||||
# Option A: TOML config file
|
# Option A: TOML config file
|
||||||
sed -i "s/^auth_token = .*/auth_token = \"$NEW_TOKEN\"/" qpq-server.toml
|
sed -i "s/^auth_token = .*/auth_token = \"$NEW_TOKEN\"/" qpc-server.toml
|
||||||
|
|
||||||
# Option B: Environment variable (systemd)
|
# Option B: Environment variable (systemd)
|
||||||
systemctl edit qpq-server --force
|
systemctl edit qpc-server --force
|
||||||
# Add: Environment=QPQ_AUTH_TOKEN=<new-token>
|
# Add: Environment=QPC_AUTH_TOKEN=<new-token>
|
||||||
|
|
||||||
# Option C: Docker Compose
|
# Option C: Docker Compose
|
||||||
# Update QPQ_AUTH_TOKEN in docker-compose.prod.yml or .env file
|
# Update QPC_AUTH_TOKEN in docker-compose.prod.yml or .env file
|
||||||
|
|
||||||
# 3. Restart the server
|
# 3. Restart the server
|
||||||
systemctl restart qpq-server
|
systemctl restart qpc-server
|
||||||
# or: docker compose restart server
|
# or: docker compose restart server
|
||||||
|
|
||||||
# 4. Update all clients with the new token
|
# 4. Update all clients with the new token
|
||||||
# Clients using OPAQUE auth are unaffected.
|
# Clients using OPAQUE auth are unaffected.
|
||||||
# Clients using bearer-token auth must update their QPQ_ACCESS_TOKEN.
|
# Clients using bearer-token auth must update their QPC_ACCESS_TOKEN.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Impact
|
### Impact
|
||||||
@@ -49,7 +49,7 @@ The server uses DER-encoded X.509 certificates for QUIC TLS 1.3. The server vali
|
|||||||
# 1. Obtain a new certificate (example with Let's Encrypt / certbot)
|
# 1. Obtain a new certificate (example with Let's Encrypt / certbot)
|
||||||
certbot certonly --standalone -d chat.example.com
|
certbot certonly --standalone -d chat.example.com
|
||||||
|
|
||||||
# 2. Convert PEM to DER format (qpq-server expects DER)
|
# 2. Convert PEM to DER format (qpc-server expects DER)
|
||||||
openssl x509 -in /etc/letsencrypt/live/chat.example.com/fullchain.pem \
|
openssl x509 -in /etc/letsencrypt/live/chat.example.com/fullchain.pem \
|
||||||
-outform DER -out /tmp/server-cert.der
|
-outform DER -out /tmp/server-cert.der
|
||||||
|
|
||||||
@@ -71,10 +71,10 @@ cp /tmp/server-key.der data/server-key.der
|
|||||||
openssl x509 -inform DER -in data/server-cert.der -noout -text | head -20
|
openssl x509 -inform DER -in data/server-cert.der -noout -text | head -20
|
||||||
|
|
||||||
# 7. Restart the server (QUIC requires restart for new TLS config)
|
# 7. Restart the server (QUIC requires restart for new TLS config)
|
||||||
systemctl restart qpq-server
|
systemctl restart qpc-server
|
||||||
|
|
||||||
# 8. Verify the server started with the new certificate
|
# 8. Verify the server started with the new certificate
|
||||||
journalctl -u qpq-server --since "1 min ago" | grep -i tls
|
journalctl -u qpc-server --since "1 min ago" | grep -i tls
|
||||||
```
|
```
|
||||||
|
|
||||||
### Self-Signed Certificate (Development)
|
### Self-Signed Certificate (Development)
|
||||||
@@ -83,7 +83,7 @@ In non-production mode, the server auto-generates a self-signed certificate if n
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
rm data/server-cert.der data/server-key.der
|
rm data/server-cert.der data/server-key.der
|
||||||
systemctl restart qpq-server
|
systemctl restart qpc-server
|
||||||
# Server will generate a new self-signed cert for localhost/127.0.0.1/::1
|
# Server will generate a new self-signed cert for localhost/127.0.0.1/::1
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -91,26 +91,26 @@ systemctl restart qpq-server
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# /opt/qpq/scripts/renew-cert.sh
|
# /opt/qpc/scripts/renew-cert.sh
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
DOMAIN="chat.example.com"
|
DOMAIN="chat.example.com"
|
||||||
CERT_DIR="/etc/letsencrypt/live/$DOMAIN"
|
CERT_DIR="/etc/letsencrypt/live/$DOMAIN"
|
||||||
QPQ_DATA="/var/lib/quicproquo"
|
QPC_DATA="/var/lib/quicprochat"
|
||||||
|
|
||||||
certbot renew --quiet
|
certbot renew --quiet
|
||||||
|
|
||||||
openssl x509 -in "$CERT_DIR/fullchain.pem" -outform DER -out "$QPQ_DATA/server-cert.der"
|
openssl x509 -in "$CERT_DIR/fullchain.pem" -outform DER -out "$QPC_DATA/server-cert.der"
|
||||||
openssl pkey -in "$CERT_DIR/privkey.pem" -outform DER -out "$QPQ_DATA/server-key.der"
|
openssl pkey -in "$CERT_DIR/privkey.pem" -outform DER -out "$QPC_DATA/server-key.der"
|
||||||
chmod 600 "$QPQ_DATA/server-key.der"
|
chmod 600 "$QPC_DATA/server-key.der"
|
||||||
chown qpq:qpq "$QPQ_DATA/server-cert.der" "$QPQ_DATA/server-key.der"
|
chown qpc:qpc "$QPC_DATA/server-cert.der" "$QPC_DATA/server-key.der"
|
||||||
|
|
||||||
systemctl restart qpq-server
|
systemctl restart qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
```cron
|
```cron
|
||||||
# Run cert renewal check twice daily
|
# Run cert renewal check twice daily
|
||||||
0 3,15 * * * /opt/qpq/scripts/renew-cert.sh >> /var/log/qpq-cert-renew.log 2>&1
|
0 3,15 * * * /opt/qpc/scripts/renew-cert.sh >> /var/log/qpc-cert-renew.log 2>&1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Federation Certificate Rotation
|
## Federation Certificate Rotation
|
||||||
@@ -134,43 +134,43 @@ openssl pkey -in /tmp/federation-key.pem -outform DER -out data/federation-key.d
|
|||||||
chmod 600 data/federation-key.der
|
chmod 600 data/federation-key.der
|
||||||
|
|
||||||
# 3. Restart the server
|
# 3. Restart the server
|
||||||
systemctl restart qpq-server
|
systemctl restart qpc-server
|
||||||
|
|
||||||
# 4. Coordinate with federation peers: they must trust the same CA
|
# 4. Coordinate with federation peers: they must trust the same CA
|
||||||
```
|
```
|
||||||
|
|
||||||
## Database Encryption Key Rotation
|
## Database Encryption Key Rotation
|
||||||
|
|
||||||
The SQLCipher database key (`QPQ_DB_KEY`) encrypts all data at rest.
|
The SQLCipher database key (`QPC_DB_KEY`) encrypts all data at rest.
|
||||||
|
|
||||||
### Procedure (SQLCipher PRAGMA rekey)
|
### Procedure (SQLCipher PRAGMA rekey)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Stop the server
|
# 1. Stop the server
|
||||||
systemctl stop qpq-server
|
systemctl stop qpc-server
|
||||||
|
|
||||||
# 2. Back up the database
|
# 2. Back up the database
|
||||||
cp data/qpq.db /backups/qpq-pre-rekey-$(date +%Y%m%d).db
|
cp data/qpc.db /backups/qpc-pre-rekey-$(date +%Y%m%d).db
|
||||||
|
|
||||||
# 3. Rekey the database
|
# 3. Rekey the database
|
||||||
sqlite3 data/qpq.db <<EOF
|
sqlite3 data/qpc.db <<EOF
|
||||||
PRAGMA key = 'old-encryption-key';
|
PRAGMA key = 'old-encryption-key';
|
||||||
PRAGMA rekey = 'new-encryption-key';
|
PRAGMA rekey = 'new-encryption-key';
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# 4. Verify the database opens with the new key
|
# 4. Verify the database opens with the new key
|
||||||
sqlite3 data/qpq.db "PRAGMA key = 'new-encryption-key'; PRAGMA integrity_check;"
|
sqlite3 data/qpc.db "PRAGMA key = 'new-encryption-key'; PRAGMA integrity_check;"
|
||||||
|
|
||||||
# 5. Update the environment/config with the new key
|
# 5. Update the environment/config with the new key
|
||||||
# Option A: systemd
|
# Option A: systemd
|
||||||
systemctl edit qpq-server --force
|
systemctl edit qpc-server --force
|
||||||
# Environment=QPQ_DB_KEY=new-encryption-key
|
# Environment=QPC_DB_KEY=new-encryption-key
|
||||||
|
|
||||||
# Option B: Docker Compose .env
|
# Option B: Docker Compose .env
|
||||||
echo "QPQ_DB_KEY=new-encryption-key" >> .env
|
echo "QPC_DB_KEY=new-encryption-key" >> .env
|
||||||
|
|
||||||
# 6. Start the server
|
# 6. Start the server
|
||||||
systemctl start qpq-server
|
systemctl start qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
### Full Re-encryption (Alternative)
|
### Full Re-encryption (Alternative)
|
||||||
@@ -179,18 +179,18 @@ If `PRAGMA rekey` is unavailable or you want a fresh database file:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Stop the server and back up
|
# 1. Stop the server and back up
|
||||||
systemctl stop qpq-server
|
systemctl stop qpc-server
|
||||||
cp data/qpq.db /backups/qpq-pre-rekey.db
|
cp data/qpc.db /backups/qpc-pre-rekey.db
|
||||||
|
|
||||||
# 2. Export with old key, import with new key
|
# 2. Export with old key, import with new key
|
||||||
sqlite3 data/qpq.db "PRAGMA key='old-key'; .dump" | \
|
sqlite3 data/qpc.db "PRAGMA key='old-key'; .dump" | \
|
||||||
sqlite3 data/qpq-new.db "PRAGMA key='new-key'; .read /dev/stdin"
|
sqlite3 data/qpc-new.db "PRAGMA key='new-key'; .read /dev/stdin"
|
||||||
|
|
||||||
# 3. Replace the database
|
# 3. Replace the database
|
||||||
mv data/qpq-new.db data/qpq.db
|
mv data/qpc-new.db data/qpc.db
|
||||||
|
|
||||||
# 4. Update config and restart
|
# 4. Update config and restart
|
||||||
systemctl start qpq-server
|
systemctl start qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
## OPAQUE ServerSetup Rotation
|
## OPAQUE ServerSetup Rotation
|
||||||
@@ -201,20 +201,20 @@ The OPAQUE ServerSetup is generated once and persisted. Rotating it invalidates
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Stop the server
|
# 1. Stop the server
|
||||||
systemctl stop qpq-server
|
systemctl stop qpc-server
|
||||||
|
|
||||||
# 2. Back up the database
|
# 2. Back up the database
|
||||||
cp data/qpq.db /backups/qpq-pre-opaque-rotate.db
|
cp data/qpc.db /backups/qpc-pre-opaque-rotate.db
|
||||||
|
|
||||||
# 3. Delete the persisted OPAQUE setup
|
# 3. Delete the persisted OPAQUE setup
|
||||||
# For SQL backend:
|
# For SQL backend:
|
||||||
sqlite3 data/qpq.db "PRAGMA key='${QPQ_DB_KEY}'; DELETE FROM server_state WHERE key = 'opaque_setup';"
|
sqlite3 data/qpc.db "PRAGMA key='${QPC_DB_KEY}'; DELETE FROM server_state WHERE key = 'opaque_setup';"
|
||||||
|
|
||||||
# For file backend:
|
# For file backend:
|
||||||
rm data/opaque_setup.bin 2>/dev/null || true
|
rm data/opaque_setup.bin 2>/dev/null || true
|
||||||
|
|
||||||
# 4. Start the server (it will generate a new OPAQUE ServerSetup)
|
# 4. Start the server (it will generate a new OPAQUE ServerSetup)
|
||||||
systemctl start qpq-server
|
systemctl start qpc-server
|
||||||
|
|
||||||
# 5. All users must re-register (existing OPAQUE credentials are invalid)
|
# 5. All users must re-register (existing OPAQUE credentials are invalid)
|
||||||
```
|
```
|
||||||
@@ -225,17 +225,17 @@ The Ed25519 signing key is used for delivery proofs. Rotating it means old deliv
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Stop the server
|
# 1. Stop the server
|
||||||
systemctl stop qpq-server
|
systemctl stop qpc-server
|
||||||
|
|
||||||
# 2. Back up
|
# 2. Back up
|
||||||
cp data/qpq.db /backups/qpq-pre-sigkey-rotate.db
|
cp data/qpc.db /backups/qpc-pre-sigkey-rotate.db
|
||||||
|
|
||||||
# 3. Delete the persisted signing key seed
|
# 3. Delete the persisted signing key seed
|
||||||
# For SQL backend:
|
# For SQL backend:
|
||||||
sqlite3 data/qpq.db "PRAGMA key='${QPQ_DB_KEY}'; DELETE FROM server_state WHERE key = 'signing_key_seed';"
|
sqlite3 data/qpc.db "PRAGMA key='${QPC_DB_KEY}'; DELETE FROM server_state WHERE key = 'signing_key_seed';"
|
||||||
|
|
||||||
# 4. Start the server (generates a new Ed25519 signing key)
|
# 4. Start the server (generates a new Ed25519 signing key)
|
||||||
systemctl start qpq-server
|
systemctl start qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
## Rotation Schedule
|
## Rotation Schedule
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Monitoring Guide
|
# Monitoring Guide
|
||||||
|
|
||||||
This document covers metrics collection, alerting, and dashboards for quicproquo.
|
This document covers metrics collection, alerting, and dashboards for quicprochat.
|
||||||
|
|
||||||
## Enabling Metrics
|
## Enabling Metrics
|
||||||
|
|
||||||
@@ -8,10 +8,10 @@ The server exports Prometheus metrics via HTTP when configured:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Environment variables
|
# Environment variables
|
||||||
QPQ_METRICS_LISTEN=0.0.0.0:9090
|
QPC_METRICS_LISTEN=0.0.0.0:9090
|
||||||
QPQ_METRICS_ENABLED=true
|
QPC_METRICS_ENABLED=true
|
||||||
|
|
||||||
# Or in qpq-server.toml
|
# Or in qpc-server.toml
|
||||||
metrics_listen = "0.0.0.0:9090"
|
metrics_listen = "0.0.0.0:9090"
|
||||||
metrics_enabled = true
|
metrics_enabled = true
|
||||||
```
|
```
|
||||||
@@ -48,9 +48,9 @@ global:
|
|||||||
evaluation_interval: 15s
|
evaluation_interval: 15s
|
||||||
|
|
||||||
scrape_configs:
|
scrape_configs:
|
||||||
- job_name: 'qpq-server'
|
- job_name: 'qpc-server'
|
||||||
static_targets:
|
static_targets:
|
||||||
- targets: ['qpq-server:9090']
|
- targets: ['qpc-server:9090']
|
||||||
scrape_interval: 10s
|
scrape_interval: 10s
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -59,17 +59,17 @@ scrape_configs:
|
|||||||
```yaml
|
```yaml
|
||||||
# prometheus-alerts.yml
|
# prometheus-alerts.yml
|
||||||
groups:
|
groups:
|
||||||
- name: qpq-server
|
- name: qpc-server
|
||||||
rules:
|
rules:
|
||||||
# Server down
|
# Server down
|
||||||
- alert: QpqServerDown
|
- alert: QpqServerDown
|
||||||
expr: up{job="qpq-server"} == 0
|
expr: up{job="qpc-server"} == 0
|
||||||
for: 1m
|
for: 1m
|
||||||
labels:
|
labels:
|
||||||
severity: critical
|
severity: critical
|
||||||
annotations:
|
annotations:
|
||||||
summary: "qpq-server is down"
|
summary: "qpc-server is down"
|
||||||
description: "Prometheus cannot scrape qpq-server metrics for > 1 minute."
|
description: "Prometheus cannot scrape qpc-server metrics for > 1 minute."
|
||||||
|
|
||||||
# High auth failure rate (potential brute force)
|
# High auth failure rate (potential brute force)
|
||||||
- alert: QpqHighAuthFailureRate
|
- alert: QpqHighAuthFailureRate
|
||||||
@@ -136,7 +136,7 @@ groups:
|
|||||||
|
|
||||||
## Key Dashboard Panels
|
## Key Dashboard Panels
|
||||||
|
|
||||||
See `dashboards/qpq-overview.json` for the full Grafana dashboard. Key panels:
|
See `dashboards/qpc-overview.json` for the full Grafana dashboard. Key panels:
|
||||||
|
|
||||||
### Message Throughput
|
### Message Throughput
|
||||||
- **Enqueue rate**: `rate(enqueue_total[5m])`
|
- **Enqueue rate**: `rate(enqueue_total[5m])`
|
||||||
@@ -160,10 +160,10 @@ See `dashboards/qpq-overview.json` for the full Grafana dashboard. Key panels:
|
|||||||
|
|
||||||
## Grafana Dashboard
|
## Grafana Dashboard
|
||||||
|
|
||||||
Import the dashboard from `dashboards/qpq-overview.json`:
|
Import the dashboard from `dashboards/qpc-overview.json`:
|
||||||
|
|
||||||
1. Open Grafana -> Dashboards -> Import
|
1. Open Grafana -> Dashboards -> Import
|
||||||
2. Upload `docs/operations/dashboards/qpq-overview.json`
|
2. Upload `docs/operations/dashboards/qpc-overview.json`
|
||||||
3. Select your Prometheus data source
|
3. Select your Prometheus data source
|
||||||
4. Save
|
4. Save
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ The server uses `tracing` with `RUST_LOG` environment variable:
|
|||||||
RUST_LOG=info
|
RUST_LOG=info
|
||||||
|
|
||||||
# Debug specific modules
|
# Debug specific modules
|
||||||
RUST_LOG=info,quicproquo_server::node_service=debug
|
RUST_LOG=info,quicprochat_server::node_service=debug
|
||||||
|
|
||||||
# Verbose debugging
|
# Verbose debugging
|
||||||
RUST_LOG=debug
|
RUST_LOG=debug
|
||||||
@@ -189,7 +189,7 @@ RUST_LOG=debug
|
|||||||
| `"TLS certificate expires within 30 days"` | Cert expiring soon | Rotate certificate |
|
| `"TLS certificate expires within 30 days"` | Cert expiring soon | Rotate certificate |
|
||||||
| `"TLS certificate is self-signed"` | Self-signed cert in use | Replace with CA-signed cert in production |
|
| `"TLS certificate is self-signed"` | Self-signed cert in use | Replace with CA-signed cert in production |
|
||||||
| `"connection rate limit exceeded"` | IP being rate limited | Check for DDoS |
|
| `"connection rate limit exceeded"` | IP being rate limited | Check for DDoS |
|
||||||
| `"running without QPQ_AUTH_TOKEN"` | Insecure mode | Must not appear in production |
|
| `"running without QPC_AUTH_TOKEN"` | Insecure mode | Must not appear in production |
|
||||||
| `"db_key is empty; SQL store will be plaintext"` | Unencrypted DB | Must not appear in production |
|
| `"db_key is empty; SQL store will be plaintext"` | Unencrypted DB | Must not appear in production |
|
||||||
| `"shutdown signal received"` | Graceful shutdown started | Expected during deploys |
|
| `"shutdown signal received"` | Graceful shutdown started | Expected during deploys |
|
||||||
| `"generated and persisted new OPAQUE ServerSetup"` | Fresh OPAQUE setup | Expected on first start only |
|
| `"generated and persisted new OPAQUE ServerSetup"` | Fresh OPAQUE setup | Expected on first start only |
|
||||||
@@ -200,13 +200,13 @@ For production, pipe logs to a log aggregator:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Systemd -> journald -> Loki/Elasticsearch
|
# Systemd -> journald -> Loki/Elasticsearch
|
||||||
journalctl -u qpq-server -f --output=json | \
|
journalctl -u qpc-server -f --output=json | \
|
||||||
promtail --stdin --client.url=http://loki:3100/loki/api/v1/push
|
promtail --stdin --client.url=http://loki:3100/loki/api/v1/push
|
||||||
|
|
||||||
# Docker -> Loki driver
|
# Docker -> Loki driver
|
||||||
docker run --log-driver=loki \
|
docker run --log-driver=loki \
|
||||||
--log-opt loki-url="http://loki:3100/loki/api/v1/push" \
|
--log-opt loki-url="http://loki:3100/loki/api/v1/push" \
|
||||||
qpq-server
|
qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
## Health Checking
|
## Health Checking
|
||||||
@@ -221,5 +221,5 @@ ss -ulnp | grep 7000
|
|||||||
curl -sf http://localhost:9090/metrics > /dev/null
|
curl -sf http://localhost:9090/metrics > /dev/null
|
||||||
|
|
||||||
# Full client connection test
|
# Full client connection test
|
||||||
qpq-client --server 127.0.0.1:7000 --auth-token "$TOKEN" --ping
|
qpc-client --server 127.0.0.1:7000 --auth-token "$TOKEN" --ping
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
groups:
|
groups:
|
||||||
- name: qpq-server
|
- name: qpc-server
|
||||||
rules:
|
rules:
|
||||||
- alert: QpqServerDown
|
- alert: QpcServerDown
|
||||||
expr: up{job="qpq-server"} == 0
|
expr: up{job="qpc-server"} == 0
|
||||||
for: 1m
|
for: 1m
|
||||||
labels:
|
labels:
|
||||||
severity: critical
|
severity: critical
|
||||||
annotations:
|
annotations:
|
||||||
summary: "qpq-server is down"
|
summary: "qpc-server is down"
|
||||||
description: "Prometheus cannot scrape qpq-server metrics for > 1 minute."
|
description: "Prometheus cannot scrape qpc-server metrics for > 1 minute."
|
||||||
|
|
||||||
- alert: QpqHighAuthFailureRate
|
- alert: QpcHighAuthFailureRate
|
||||||
expr: rate(auth_login_failure_total[5m]) > 10
|
expr: rate(auth_login_failure_total[5m]) > 10
|
||||||
for: 2m
|
for: 2m
|
||||||
labels:
|
labels:
|
||||||
@@ -19,7 +19,7 @@ groups:
|
|||||||
summary: "High authentication failure rate"
|
summary: "High authentication failure rate"
|
||||||
description: "{{ $value | printf \"%.1f\" }} auth failures/sec over 5 minutes."
|
description: "{{ $value | printf \"%.1f\" }} auth failures/sec over 5 minutes."
|
||||||
|
|
||||||
- alert: QpqRateLimitActive
|
- alert: QpcRateLimitActive
|
||||||
expr: rate(rate_limit_hit_total[5m]) > 5
|
expr: rate(rate_limit_hit_total[5m]) > 5
|
||||||
for: 5m
|
for: 5m
|
||||||
labels:
|
labels:
|
||||||
@@ -27,7 +27,7 @@ groups:
|
|||||||
annotations:
|
annotations:
|
||||||
summary: "Rate limiting is actively rejecting requests"
|
summary: "Rate limiting is actively rejecting requests"
|
||||||
|
|
||||||
- alert: QpqDeliveryQueueHigh
|
- alert: QpcDeliveryQueueHigh
|
||||||
expr: delivery_queue_depth > 10000
|
expr: delivery_queue_depth > 10000
|
||||||
for: 10m
|
for: 10m
|
||||||
labels:
|
labels:
|
||||||
@@ -35,7 +35,7 @@ groups:
|
|||||||
annotations:
|
annotations:
|
||||||
summary: "Delivery queue depth is high ({{ $value }})"
|
summary: "Delivery queue depth is high ({{ $value }})"
|
||||||
|
|
||||||
- alert: QpqDeliveryQueueCritical
|
- alert: QpcDeliveryQueueCritical
|
||||||
expr: delivery_queue_depth > 100000
|
expr: delivery_queue_depth > 100000
|
||||||
for: 5m
|
for: 5m
|
||||||
labels:
|
labels:
|
||||||
@@ -43,7 +43,7 @@ groups:
|
|||||||
annotations:
|
annotations:
|
||||||
summary: "Delivery queue depth is critical ({{ $value }})"
|
summary: "Delivery queue depth is critical ({{ $value }})"
|
||||||
|
|
||||||
- alert: QpqLowAuthSuccessRatio
|
- alert: QpcLowAuthSuccessRatio
|
||||||
expr: >
|
expr: >
|
||||||
rate(auth_login_success_total[5m])
|
rate(auth_login_success_total[5m])
|
||||||
/ (rate(auth_login_success_total[5m]) + rate(auth_login_failure_total[5m]))
|
/ (rate(auth_login_success_total[5m]) + rate(auth_login_failure_total[5m]))
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ rule_files:
|
|||||||
- "alerts.yml"
|
- "alerts.yml"
|
||||||
|
|
||||||
scrape_configs:
|
scrape_configs:
|
||||||
- job_name: 'qpq-server'
|
- job_name: 'qpc-server'
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ['server:9090']
|
- targets: ['server:9090']
|
||||||
scrape_interval: 10s
|
scrape_interval: 10s
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Scaling Guide
|
# Scaling Guide
|
||||||
|
|
||||||
This document covers resource sizing, scaling triggers, and capacity planning for quicproquo deployments.
|
This document covers resource sizing, scaling triggers, and capacity planning for quicprochat deployments.
|
||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
quicproquo runs as a single-process server handling QUIC connections. Key resource consumers:
|
quicprochat runs as a single-process server handling QUIC connections. Key resource consumers:
|
||||||
|
|
||||||
- **CPU**: TLS 1.3 handshakes (QUIC), OPAQUE PAKE authentication, message routing
|
- **CPU**: TLS 1.3 handshakes (QUIC), OPAQUE PAKE authentication, message routing
|
||||||
- **Memory**: In-memory session state (DashMap), QUIC connection state, delivery waiters, rate limit entries
|
- **Memory**: In-memory session state (DashMap), QUIC connection state, delivery waiters, rate limit entries
|
||||||
@@ -70,7 +70,7 @@ The server is async (Tokio) and benefits from multiple cores. QUIC TLS handshake
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check current CPU usage
|
# Check current CPU usage
|
||||||
top -bn1 -p $(pgrep qpq-server)
|
top -bn1 -p $(pgrep qpc-server)
|
||||||
|
|
||||||
# For Docker: increase CPU limits
|
# For Docker: increase CPU limits
|
||||||
# docker-compose.prod.yml:
|
# docker-compose.prod.yml:
|
||||||
@@ -107,22 +107,22 @@ iostat -x 1 5
|
|||||||
|
|
||||||
# Move to NVMe if on spinning disk
|
# Move to NVMe if on spinning disk
|
||||||
# Increase WAL autocheckpoint threshold for burst writes
|
# Increase WAL autocheckpoint threshold for burst writes
|
||||||
sqlite3 data/qpq.db "PRAGMA key='${QPQ_DB_KEY}'; PRAGMA wal_autocheckpoint=2000;"
|
sqlite3 data/qpc.db "PRAGMA key='${QPC_DB_KEY}'; PRAGMA wal_autocheckpoint=2000;"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Horizontal Scaling
|
## Horizontal Scaling
|
||||||
|
|
||||||
quicproquo does not yet have built-in multi-node clustering. For horizontal scaling, use these patterns:
|
quicprochat does not yet have built-in multi-node clustering. For horizontal scaling, use these patterns:
|
||||||
|
|
||||||
### Load Balancer (UDP/QUIC)
|
### Load Balancer (UDP/QUIC)
|
||||||
|
|
||||||
Place a UDP load balancer in front of multiple qpq-server instances. Each instance runs independently with its own database.
|
Place a UDP load balancer in front of multiple qpc-server instances. Each instance runs independently with its own database.
|
||||||
|
|
||||||
```
|
```
|
||||||
+-----------+
|
+-----------+
|
||||||
clients ------> | L4 LB | ----> qpq-server-1 (db-1)
|
clients ------> | L4 LB | ----> qpc-server-1 (db-1)
|
||||||
| (UDP/QUIC)| ----> qpq-server-2 (db-2)
|
| (UDP/QUIC)| ----> qpc-server-2 (db-2)
|
||||||
+-----------+ qpq-server-3 (db-3)
|
+-----------+ qpc-server-3 (db-3)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Requirements:**
|
**Requirements:**
|
||||||
@@ -134,7 +134,7 @@ Place a UDP load balancer in front of multiple qpq-server instances. Each instan
|
|||||||
Enable federation to relay messages between nodes:
|
Enable federation to relay messages between nodes:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# qpq-server.toml on node-1
|
# qpc-server.toml on node-1
|
||||||
[federation]
|
[federation]
|
||||||
enabled = true
|
enabled = true
|
||||||
domain = "node1.chat.example.com"
|
domain = "node1.chat.example.com"
|
||||||
@@ -153,9 +153,9 @@ address = "10.0.1.2:7001"
|
|||||||
For true horizontal scaling, migrate from SQLCipher to a shared PostgreSQL instance. This is not yet implemented but is the planned approach for multi-node deployments.
|
For true horizontal scaling, migrate from SQLCipher to a shared PostgreSQL instance. This is not yet implemented but is the planned approach for multi-node deployments.
|
||||||
|
|
||||||
```
|
```
|
||||||
qpq-server-1 --\
|
qpc-server-1 --\
|
||||||
qpq-server-2 ---+--> PostgreSQL (shared)
|
qpc-server-2 ---+--> PostgreSQL (shared)
|
||||||
qpq-server-3 --/
|
qpc-server-3 --/
|
||||||
```
|
```
|
||||||
|
|
||||||
## Connection Tuning
|
## Connection Tuning
|
||||||
@@ -174,7 +174,7 @@ For high connection counts, consider:
|
|||||||
- Increasing UDP buffer sizes:
|
- Increasing UDP buffer sizes:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# /etc/sysctl.d/99-qpq.conf
|
# /etc/sysctl.d/99-qpc.conf
|
||||||
net.core.rmem_max = 26214400
|
net.core.rmem_max = 26214400
|
||||||
net.core.wmem_max = 26214400
|
net.core.wmem_max = 26214400
|
||||||
net.core.rmem_default = 1048576
|
net.core.rmem_default = 1048576
|
||||||
@@ -182,7 +182,7 @@ net.core.wmem_default = 1048576
|
|||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sysctl -p /etc/sysctl.d/99-qpq.conf
|
sysctl -p /etc/sysctl.d/99-qpc.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker Resource Limits
|
## Docker Resource Limits
|
||||||
@@ -211,11 +211,11 @@ Use the included test infrastructure to benchmark:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build the test client
|
# Build the test client
|
||||||
cargo build --release --bin qpq-client
|
cargo build --release --bin qpc-client
|
||||||
|
|
||||||
# Run concurrent connection test (example)
|
# Run concurrent connection test (example)
|
||||||
for i in $(seq 1 100); do
|
for i in $(seq 1 100); do
|
||||||
qpq-client --server 127.0.0.1:7000 --auth-token "$QPQ_AUTH_TOKEN" &
|
qpc-client --server 127.0.0.1:7000 --auth-token "$QPC_AUTH_TOKEN" &
|
||||||
done
|
done
|
||||||
wait
|
wait
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# Build Your Own SDK
|
# Build Your Own SDK
|
||||||
|
|
||||||
This guide explains how to implement a quicproquo client SDK in any language.
|
This guide explains how to implement a quicprochat client SDK in any language.
|
||||||
|
|
||||||
## Two Approaches
|
## Two Approaches
|
||||||
|
|
||||||
### Approach 1: C FFI Wrapper (recommended)
|
### Approach 1: C FFI Wrapper (recommended)
|
||||||
|
|
||||||
The simplest path. Wrap `libquicproquo_ffi` using your language's FFI mechanism (e.g., Python CFFI, Ruby FFI, JNI, Swift C interop).
|
The simplest path. Wrap `libquicprochat_ffi` using your language's FFI mechanism (e.g., Python CFFI, Ruby FFI, JNI, Swift C interop).
|
||||||
|
|
||||||
**Pros**: Full Rust crypto stack (MLS, OPAQUE, hybrid KEM) with zero reimplementation.
|
**Pros**: Full Rust crypto stack (MLS, OPAQUE, hybrid KEM) with zero reimplementation.
|
||||||
**Cons**: Requires shipping a native library, synchronous API only.
|
**Cons**: Requires shipping a native library, synchronous API only.
|
||||||
@@ -26,7 +26,7 @@ Implement the QUIC transport and protobuf serialization natively in your languag
|
|||||||
|
|
||||||
Open a QUIC connection to the server:
|
Open a QUIC connection to the server:
|
||||||
|
|
||||||
- **ALPN**: `qpq`
|
- **ALPN**: `qpc`
|
||||||
- **TLS**: 1.3 with server certificate verification
|
- **TLS**: 1.3 with server certificate verification
|
||||||
- **Port**: 5001 (default)
|
- **Port**: 5001 (default)
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ payload = data[10 : 10 + length]
|
|||||||
|
|
||||||
### 3. Protobuf Messages
|
### 3. Protobuf Messages
|
||||||
|
|
||||||
Generate or hand-write protobuf encode/decode for the message types in `proto/qpq/v1/*.proto`.
|
Generate or hand-write protobuf encode/decode for the message types in `proto/qpc/v1/*.proto`.
|
||||||
|
|
||||||
Minimum required messages for a basic client:
|
Minimum required messages for a basic client:
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ Client Server
|
|||||||
|
|
||||||
Core operations (implement in order):
|
Core operations (implement in order):
|
||||||
|
|
||||||
- [ ] **Connect**: Open QUIC connection with TLS 1.3 + ALPN `qpq`
|
- [ ] **Connect**: Open QUIC connection with TLS 1.3 + ALPN `qpc`
|
||||||
- [ ] **Health**: Send `HealthRequest` (method 802), verify server is running
|
- [ ] **Health**: Send `HealthRequest` (method 802), verify server is running
|
||||||
- [ ] **Register**: OPAQUE registration (methods 100-101)
|
- [ ] **Register**: OPAQUE registration (methods 100-101)
|
||||||
- [ ] **Login**: OPAQUE login (methods 102-103), store session token
|
- [ ] **Login**: OPAQUE login (methods 102-103), store session token
|
||||||
@@ -146,7 +146,7 @@ Study these SDKs for patterns:
|
|||||||
- **Go** (`sdks/go/`): Native QUIC + Cap'n Proto, full OPAQUE flow
|
- **Go** (`sdks/go/`): Native QUIC + Cap'n Proto, full OPAQUE flow
|
||||||
- **Python** (`sdks/python/`): Native QUIC + Protobuf v2 wire format
|
- **Python** (`sdks/python/`): Native QUIC + Protobuf v2 wire format
|
||||||
- **TypeScript** (`sdks/typescript/`): WebSocket bridge, WASM crypto
|
- **TypeScript** (`sdks/typescript/`): WebSocket bridge, WASM crypto
|
||||||
- **C FFI** (`crates/quicproquo-ffi/`): Synchronous wrapper
|
- **C FFI** (`crates/quicprochat-ffi/`): Synchronous wrapper
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ Test your SDK against a local server:
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Start the server
|
# Start the server
|
||||||
cargo run -p quicproquo-server
|
cargo run -p quicprochat-server
|
||||||
|
|
||||||
# Run your SDK's tests
|
# Run your SDK's tests
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
# C FFI Bindings
|
# C FFI Bindings
|
||||||
|
|
||||||
The C FFI layer (`crates/quicproquo-ffi/`) provides synchronous C-callable functions that wrap the Rust client library. This is the foundation for language bindings in Python (CFFI), Swift, Kotlin/JNI, Java, and Ruby.
|
The C FFI layer (`crates/quicprochat-ffi/`) provides synchronous C-callable functions that wrap the Rust client library. This is the foundation for language bindings in Python (CFFI), Swift, Kotlin/JNI, Java, and Ruby.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo build --release -p quicproquo-ffi
|
cargo build --release -p quicprochat-ffi
|
||||||
```
|
```
|
||||||
|
|
||||||
This produces:
|
This produces:
|
||||||
- Linux: `target/release/libquicproquo_ffi.so`
|
- Linux: `target/release/libquicprochat_ffi.so`
|
||||||
- macOS: `target/release/libquicproquo_ffi.dylib`
|
- macOS: `target/release/libquicprochat_ffi.dylib`
|
||||||
- Windows: `target/release/quicproquo_ffi.dll`
|
- Windows: `target/release/quicprochat_ffi.dll`
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### Status Codes
|
### Status Codes
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define QPQ_OK 0
|
#define QPC_OK 0
|
||||||
#define QPQ_ERROR 1
|
#define QPC_ERROR 1
|
||||||
#define QPQ_AUTH_FAILED 2
|
#define QPC_AUTH_FAILED 2
|
||||||
#define QPQ_TIMEOUT 3
|
#define QPC_TIMEOUT 3
|
||||||
#define QPQ_NOT_CONNECTED 4
|
#define QPC_NOT_CONNECTED 4
|
||||||
```
|
```
|
||||||
|
|
||||||
### Functions
|
### Functions
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// Connect to a server. Returns opaque handle or NULL on failure.
|
// Connect to a server. Returns opaque handle or NULL on failure.
|
||||||
QpqHandle* qpq_connect(
|
QpqHandle* qpc_connect(
|
||||||
const char* server, // "host:port"
|
const char* server, // "host:port"
|
||||||
const char* ca_cert, // path to CA certificate PEM
|
const char* ca_cert, // path to CA certificate PEM
|
||||||
const char* server_name // TLS SNI name
|
const char* server_name // TLS SNI name
|
||||||
);
|
);
|
||||||
|
|
||||||
// Authenticate with OPAQUE. Returns status code.
|
// Authenticate with OPAQUE. Returns status code.
|
||||||
int qpq_login(
|
int qpc_login(
|
||||||
QpqHandle* handle,
|
QpqHandle* handle,
|
||||||
const char* username,
|
const char* username,
|
||||||
const char* password
|
const char* password
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send a message to a recipient (by username).
|
// Send a message to a recipient (by username).
|
||||||
int qpq_send(
|
int qpc_send(
|
||||||
QpqHandle* handle,
|
QpqHandle* handle,
|
||||||
const char* recipient, // recipient username
|
const char* recipient, // recipient username
|
||||||
const uint8_t* message, // message bytes
|
const uint8_t* message, // message bytes
|
||||||
@@ -52,20 +52,20 @@ int qpq_send(
|
|||||||
|
|
||||||
// Receive pending messages. Blocks up to timeout_ms.
|
// Receive pending messages. Blocks up to timeout_ms.
|
||||||
// On success, *out_json is a JSON array of strings.
|
// On success, *out_json is a JSON array of strings.
|
||||||
int qpq_receive(
|
int qpc_receive(
|
||||||
QpqHandle* handle,
|
QpqHandle* handle,
|
||||||
uint32_t timeout_ms,
|
uint32_t timeout_ms,
|
||||||
char** out_json // caller must free with qpq_free_string
|
char** out_json // caller must free with qpc_free_string
|
||||||
);
|
);
|
||||||
|
|
||||||
// Disconnect and free the handle.
|
// Disconnect and free the handle.
|
||||||
void qpq_disconnect(QpqHandle* handle);
|
void qpc_disconnect(QpqHandle* handle);
|
||||||
|
|
||||||
// Get last error message (valid until next FFI call on this handle).
|
// Get last error message (valid until next FFI call on this handle).
|
||||||
const char* qpq_last_error(const QpqHandle* handle);
|
const char* qpc_last_error(const QpqHandle* handle);
|
||||||
|
|
||||||
// Free a string returned by qpq_receive.
|
// Free a string returned by qpc_receive.
|
||||||
void qpq_free_string(char* ptr);
|
void qpc_free_string(char* ptr);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage Example (C)
|
## Usage Example (C)
|
||||||
@@ -76,53 +76,53 @@ void qpq_free_string(char* ptr);
|
|||||||
|
|
||||||
// Forward declarations (or include a header).
|
// Forward declarations (or include a header).
|
||||||
typedef struct QpqHandle QpqHandle;
|
typedef struct QpqHandle QpqHandle;
|
||||||
extern QpqHandle* qpq_connect(const char*, const char*, const char*);
|
extern QpqHandle* qpc_connect(const char*, const char*, const char*);
|
||||||
extern int qpq_login(QpqHandle*, const char*, const char*);
|
extern int qpc_login(QpqHandle*, const char*, const char*);
|
||||||
extern int qpq_send(QpqHandle*, const char*, const unsigned char*, size_t);
|
extern int qpc_send(QpqHandle*, const char*, const unsigned char*, size_t);
|
||||||
extern int qpq_receive(QpqHandle*, unsigned int, char**);
|
extern int qpc_receive(QpqHandle*, unsigned int, char**);
|
||||||
extern void qpq_disconnect(QpqHandle*);
|
extern void qpc_disconnect(QpqHandle*);
|
||||||
extern const char* qpq_last_error(const QpqHandle*);
|
extern const char* qpc_last_error(const QpqHandle*);
|
||||||
extern void qpq_free_string(char*);
|
extern void qpc_free_string(char*);
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
QpqHandle* h = qpq_connect("127.0.0.1:5001", "ca.pem", "localhost");
|
QpqHandle* h = qpc_connect("127.0.0.1:5001", "ca.pem", "localhost");
|
||||||
if (!h) {
|
if (!h) {
|
||||||
fprintf(stderr, "connect failed\n");
|
fprintf(stderr, "connect failed\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc = qpq_login(h, "alice", "password123");
|
int rc = qpc_login(h, "alice", "password123");
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
fprintf(stderr, "login failed: %s\n", qpq_last_error(h));
|
fprintf(stderr, "login failed: %s\n", qpc_last_error(h));
|
||||||
qpq_disconnect(h);
|
qpc_disconnect(h);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* msg = "hello from C!";
|
const char* msg = "hello from C!";
|
||||||
qpq_send(h, "bob", (const unsigned char*)msg, strlen(msg));
|
qpc_send(h, "bob", (const unsigned char*)msg, strlen(msg));
|
||||||
|
|
||||||
char* json = NULL;
|
char* json = NULL;
|
||||||
rc = qpq_receive(h, 5000, &json);
|
rc = qpc_receive(h, 5000, &json);
|
||||||
if (rc == 0 && json) {
|
if (rc == 0 && json) {
|
||||||
printf("received: %s\n", json);
|
printf("received: %s\n", json);
|
||||||
qpq_free_string(json);
|
qpc_free_string(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
qpq_disconnect(h);
|
qpc_disconnect(h);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Compile with:
|
Compile with:
|
||||||
```sh
|
```sh
|
||||||
gcc -o demo demo.c -L target/release -lquicproquo_ffi
|
gcc -o demo demo.c -L target/release -lquicprochat_ffi
|
||||||
```
|
```
|
||||||
|
|
||||||
## Memory Management
|
## Memory Management
|
||||||
|
|
||||||
- `qpq_connect` returns a heap-allocated handle. The caller **must** call `qpq_disconnect` to free it.
|
- `qpc_connect` returns a heap-allocated handle. The caller **must** call `qpc_disconnect` to free it.
|
||||||
- `qpq_receive` writes a heap-allocated JSON string to `*out_json`. The caller **must** call `qpq_free_string` to free it.
|
- `qpc_receive` writes a heap-allocated JSON string to `*out_json`. The caller **must** call `qpc_free_string` to free it.
|
||||||
- `qpq_last_error` returns a pointer owned by the handle. Do **not** free it. It is valid until the next FFI call on the same handle.
|
- `qpc_last_error` returns a pointer owned by the handle. Do **not** free it. It is valid until the next FFI call on the same handle.
|
||||||
|
|
||||||
## Thread Safety
|
## Thread Safety
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ Each `QpqHandle` owns its own Tokio runtime. Concurrent calls on the **same** ha
|
|||||||
The FFI layer bridges synchronous C callers to the async Rust client:
|
The FFI layer bridges synchronous C callers to the async Rust client:
|
||||||
|
|
||||||
```
|
```
|
||||||
C caller ─── qpq_login() ───► QpqHandle ─── runtime.block_on() ───► async Rust client
|
C caller ─── qpc_login() ───► QpqHandle ─── runtime.block_on() ───► async Rust client
|
||||||
```
|
```
|
||||||
|
|
||||||
Each handle contains:
|
Each handle contains:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Location: `sdks/go/`
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
go get quicproquo.dev/sdk/go
|
go get quicprochat.dev/sdk/go
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
@@ -18,13 +18,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"quicproquo.dev/sdk/go/qpq"
|
"quicprochat.dev/sdk/go/qpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
client, err := qpq.Connect(ctx, qpq.Options{
|
client, err := qpc.Connect(ctx, qpc.Options{
|
||||||
Addr: "127.0.0.1:5001",
|
Addr: "127.0.0.1:5001",
|
||||||
InsecureSkipVerify: true, // dev only
|
InsecureSkipVerify: true, // dev only
|
||||||
})
|
})
|
||||||
@@ -63,7 +63,7 @@ func main() {
|
|||||||
|
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `qpq.Connect(ctx, opts)` | Connect to server |
|
| `qpc.Connect(ctx, opts)` | Connect to server |
|
||||||
| `client.Close()` | Disconnect |
|
| `client.Close()` | Disconnect |
|
||||||
| `client.Health(ctx)` | Health check |
|
| `client.Health(ctx)` | Health check |
|
||||||
| `client.SetSessionToken(token)` | Set pre-existing token |
|
| `client.SetSessionToken(token)` | Set pre-existing token |
|
||||||
@@ -81,7 +81,7 @@ func main() {
|
|||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
- `qpq/` -- High-level client API
|
- `qpc/` -- High-level client API
|
||||||
- `transport/` -- QUIC + TLS 1.3 transport, Cap'n Proto RPC framing
|
- `transport/` -- QUIC + TLS 1.3 transport, Cap'n Proto RPC framing
|
||||||
- `proto/node/` -- Generated Cap'n Proto Go types
|
- `proto/node/` -- Generated Cap'n Proto Go types
|
||||||
- `cmd/example/` -- Example usage
|
- `cmd/example/` -- Example usage
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
# quicproquo SDK Documentation
|
# quicprochat SDK Documentation
|
||||||
|
|
||||||
This guide covers how to build clients for the quicproquo E2E encrypted messenger using the official SDKs or by implementing your own.
|
This guide covers how to build clients for the quicprochat E2E encrypted messenger using the official SDKs or by implementing your own.
|
||||||
|
|
||||||
## Official SDKs
|
## Official SDKs
|
||||||
|
|
||||||
| Language | Location | Transport | Status |
|
| Language | Location | Transport | Status |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **Rust** | `crates/quicproquo-client` | QUIC + Cap'n Proto | Production |
|
| **Rust** | `crates/quicprochat-client` | QUIC + Cap'n Proto | Production |
|
||||||
| **Go** | `sdks/go/` | QUIC + Cap'n Proto | Production |
|
| **Go** | `sdks/go/` | QUIC + Cap'n Proto | Production |
|
||||||
| **TypeScript** | `sdks/typescript/` | WebSocket bridge + WASM crypto | Production |
|
| **TypeScript** | `sdks/typescript/` | WebSocket bridge + WASM crypto | Production |
|
||||||
| **Python** | `sdks/python/` | QUIC + Protobuf (v2) / Rust FFI | Production |
|
| **Python** | `sdks/python/` | QUIC + Protobuf (v2) / Rust FFI | Production |
|
||||||
| **C** | `crates/quicproquo-ffi/` | Rust FFI (synchronous) | Production |
|
| **C** | `crates/quicprochat-ffi/` | Rust FFI (synchronous) | Production |
|
||||||
| **Swift** | `sdks/swift/` | C FFI wrapper | In progress |
|
| **Swift** | `sdks/swift/` | C FFI wrapper | In progress |
|
||||||
| **Kotlin** | `sdks/kotlin/` | JNI + C FFI | In progress |
|
| **Kotlin** | `sdks/kotlin/` | JNI + C FFI | In progress |
|
||||||
| **Java** | `sdks/java/` | JNI + C FFI | In progress |
|
| **Java** | `sdks/java/` | JNI + C FFI | In progress |
|
||||||
@@ -57,7 +57,7 @@ Each RPC call opens a new QUIC bidirectional stream. The request and response us
|
|||||||
|
|
||||||
## Canonical Schemas
|
## Canonical Schemas
|
||||||
|
|
||||||
- **Protobuf** (v2): `proto/qpq/v1/*.proto` -- 14 service definitions
|
- **Protobuf** (v2): `proto/qpc/v1/*.proto` -- 14 service definitions
|
||||||
- **Cap'n Proto** (v1): `schemas/*.capnp` -- legacy RPC interface
|
- **Cap'n Proto** (v1): `schemas/*.capnp` -- legacy RPC interface
|
||||||
|
|
||||||
The protobuf schemas in `proto/qpq/v1/` are the canonical API contract for the v2 protocol. New SDKs should implement against these definitions.
|
The protobuf schemas in `proto/qpc/v1/` are the canonical API contract for the v2 protocol. New SDKs should implement against these definitions.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Location: `sdks/python/`
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip install quicproquo
|
pip install quicprochat
|
||||||
```
|
```
|
||||||
|
|
||||||
## Transport Backends
|
## Transport Backends
|
||||||
@@ -18,7 +18,7 @@ Uses [aioquic](https://github.com/aiortc/aioquic) for native QUIC transport with
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
import asyncio
|
import asyncio
|
||||||
from quicproquo import QpqClient, ConnectOptions
|
from quicprochat import QpqClient, ConnectOptions
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
client = await QpqClient.connect(ConnectOptions(
|
client = await QpqClient.connect(ConnectOptions(
|
||||||
@@ -45,14 +45,14 @@ asyncio.run(main())
|
|||||||
|
|
||||||
### Rust FFI (synchronous)
|
### Rust FFI (synchronous)
|
||||||
|
|
||||||
Wraps `libquicproquo_ffi` via CFFI for full Rust crypto stack at native speed.
|
Wraps `libquicprochat_ffi` via CFFI for full Rust crypto stack at native speed.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo build --release -p quicproquo-ffi
|
cargo build --release -p quicprochat-ffi
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from quicproquo import QpqClient, ConnectOptions
|
from quicprochat import QpqClient, ConnectOptions
|
||||||
|
|
||||||
client = QpqClient.connect_ffi(ConnectOptions(
|
client = QpqClient.connect_ffi(ConnectOptions(
|
||||||
addr="127.0.0.1:5001",
|
addr="127.0.0.1:5001",
|
||||||
@@ -112,9 +112,9 @@ client.close_sync()
|
|||||||
|
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `quicproquo/client.py` | High-level client API |
|
| `quicprochat/client.py` | High-level client API |
|
||||||
| `quicproquo/transport.py` | QUIC transport (aioquic) |
|
| `quicprochat/transport.py` | QUIC transport (aioquic) |
|
||||||
| `quicproquo/ffi.py` | Rust FFI transport (CFFI) |
|
| `quicprochat/ffi.py` | Rust FFI transport (CFFI) |
|
||||||
| `quicproquo/proto.py` | Protobuf encode/decode |
|
| `quicprochat/proto.py` | Protobuf encode/decode |
|
||||||
| `quicproquo/wire.py` | v2 wire format framing |
|
| `quicprochat/wire.py` | v2 wire format framing |
|
||||||
| `quicproquo/types.py` | Data types and exceptions |
|
| `quicprochat/types.py` | Data types and exceptions |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Rust SDK
|
# Rust SDK
|
||||||
|
|
||||||
The Rust client is the reference implementation, located in `crates/quicproquo-client/`.
|
The Rust client is the reference implementation, located in `crates/quicprochat-client/`.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ Add to your `Cargo.toml`:
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quicproquo-client = { path = "crates/quicproquo-client" }
|
quicprochat-client = { path = "crates/quicprochat-client" }
|
||||||
```
|
```
|
||||||
|
|
||||||
## Connection
|
## Connection
|
||||||
@@ -16,7 +16,7 @@ quicproquo-client = { path = "crates/quicproquo-client" }
|
|||||||
The Rust client connects directly over QUIC with Cap'n Proto RPC:
|
The Rust client connects directly over QUIC with Cap'n Proto RPC:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use quicproquo_client::{cmd_health, cmd_login, cmd_send, connect_node};
|
use quicprochat_client::{cmd_health, cmd_login, cmd_send, connect_node};
|
||||||
|
|
||||||
// Health check
|
// Health check
|
||||||
cmd_health("127.0.0.1:5001", &ca_cert_path, "localhost").await?;
|
cmd_health("127.0.0.1:5001", &ca_cert_path, "localhost").await?;
|
||||||
@@ -38,14 +38,14 @@ cmd_login(
|
|||||||
- OPAQUE authentication with zeroizing credential storage
|
- OPAQUE authentication with zeroizing credential storage
|
||||||
- SQLCipher local state with Argon2id key derivation
|
- SQLCipher local state with Argon2id key derivation
|
||||||
- Sealed sender metadata protection
|
- Sealed sender metadata protection
|
||||||
- v2 QUIC + Protobuf transport (via `quicproquo-sdk` crate)
|
- v2 QUIC + Protobuf transport (via `quicprochat-sdk` crate)
|
||||||
|
|
||||||
## v2 SDK Crate
|
## v2 SDK Crate
|
||||||
|
|
||||||
The `quicproquo-sdk` crate provides the higher-level v2 API:
|
The `quicprochat-sdk` crate provides the higher-level v2 API:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use quicproquo_sdk::QpqClient;
|
use quicprochat_sdk::QpqClient;
|
||||||
|
|
||||||
let client = QpqClient::connect("127.0.0.1:5001", &tls_config).await?;
|
let client = QpqClient::connect("127.0.0.1:5001", &tls_config).await?;
|
||||||
let health = client.health().await?;
|
let health = client.health().await?;
|
||||||
@@ -55,9 +55,9 @@ let health = client.health().await?;
|
|||||||
|
|
||||||
| Crate | Purpose |
|
| Crate | Purpose |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `quicproquo-core` | Crypto primitives, MLS, hybrid KEM |
|
| `quicprochat-core` | Crypto primitives, MLS, hybrid KEM |
|
||||||
| `quicproquo-proto` | Protobuf + Cap'n Proto generated types |
|
| `quicprochat-proto` | Protobuf + Cap'n Proto generated types |
|
||||||
| `quicproquo-rpc` | QUIC RPC framework (framing, dispatch) |
|
| `quicprochat-rpc` | QUIC RPC framework (framing, dispatch) |
|
||||||
| `quicproquo-sdk` | High-level client SDK |
|
| `quicprochat-sdk` | High-level client SDK |
|
||||||
| `quicproquo-client` | CLI/TUI client application |
|
| `quicprochat-client` | CLI/TUI client application |
|
||||||
| `quicproquo-ffi` | C FFI bindings |
|
| `quicprochat-ffi` | C FFI bindings |
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ Location: `sdks/typescript/`
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm install quicproquo
|
npm install quicprochat
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { QpqClient } from "quicproquo";
|
import { QpqClient } from "quicprochat";
|
||||||
|
|
||||||
// Connect via WebSocket bridge
|
// Connect via WebSocket bridge
|
||||||
const client = await QpqClient.connect({
|
const client = await QpqClient.connect({
|
||||||
@@ -115,7 +115,7 @@ The TypeScript SDK uses a WebSocket bridge proxy because browsers cannot open ra
|
|||||||
Browser ─── WebSocket ───► Bridge Proxy ─── QUIC/capnp ───► Server
|
Browser ─── WebSocket ───► Bridge Proxy ─── QUIC/capnp ───► Server
|
||||||
```
|
```
|
||||||
|
|
||||||
Crypto operations (Ed25519, hybrid KEM, sealed sender) run entirely in WASM, compiled from the Rust `quicproquo-core` crate.
|
Crypto operations (Ed25519, hybrid KEM, sealed sender) run entirely in WASM, compiled from the Rust `quicprochat-core` crate.
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# v2 Wire Format
|
# v2 Wire Format
|
||||||
|
|
||||||
The quicproquo v2 protocol uses QUIC (RFC 9000) with TLS 1.3 as the transport layer and Protocol Buffers for message serialization.
|
The quicprochat v2 protocol uses QUIC (RFC 9000) with TLS 1.3 as the transport layer and Protocol Buffers for message serialization.
|
||||||
|
|
||||||
## Connection
|
## Connection
|
||||||
|
|
||||||
- **Protocol**: QUIC with TLS 1.3
|
- **Protocol**: QUIC with TLS 1.3
|
||||||
- **ALPN**: `qpq`
|
- **ALPN**: `qpc`
|
||||||
- **Port**: 5001 (default)
|
- **Port**: 5001 (default)
|
||||||
- **Certificate**: Server presents a TLS certificate; clients verify against a CA cert
|
- **Certificate**: Server presents a TLS certificate; clients verify against a CA cert
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ This allows concurrent RPCs without head-of-line blocking.
|
|||||||
|
|
||||||
## Protobuf Definitions
|
## Protobuf Definitions
|
||||||
|
|
||||||
All message types are defined in `proto/qpq/v1/*.proto`:
|
All message types are defined in `proto/qpc/v1/*.proto`:
|
||||||
|
|
||||||
| File | Services |
|
| File | Services |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Why quicproquo?
|
# Why quicprochat?
|
||||||
|
|
||||||
- [Comparison with Classical Chat Protocols](design-rationale/protocol-comparison.md)
|
- [Comparison with Classical Chat Protocols](design-rationale/protocol-comparison.md)
|
||||||
- [Why This Design, Not Signal/Matrix/...](design-rationale/why-not-signal.md)
|
- [Why This Design, Not Signal/Matrix/...](design-rationale/why-not-signal.md)
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
- [REPL Command Reference](getting-started/repl-reference.md)
|
- [REPL Command Reference](getting-started/repl-reference.md)
|
||||||
- [Rich Messaging](getting-started/rich-messaging.md)
|
- [Rich Messaging](getting-started/rich-messaging.md)
|
||||||
- [File Transfer](getting-started/file-transfer.md)
|
- [File Transfer](getting-started/file-transfer.md)
|
||||||
- [TLS in quicproquo](getting-started/tls.md)
|
- [TLS in quicprochat](getting-started/tls.md)
|
||||||
- [Certificate Lifecycle and CA-Signed TLS](getting-started/certificate-lifecycle.md)
|
- [Certificate Lifecycle and CA-Signed TLS](getting-started/certificate-lifecycle.md)
|
||||||
- [Docker Deployment](getting-started/docker.md)
|
- [Docker Deployment](getting-started/docker.md)
|
||||||
- [Mesh Networking](getting-started/mesh-networking.md)
|
- [Mesh Networking](getting-started/mesh-networking.md)
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
# Glossary
|
# Glossary
|
||||||
|
|
||||||
Alphabetical glossary of terms used throughout the quicproquo documentation.
|
Alphabetical glossary of terms used throughout the quicprochat documentation.
|
||||||
Each entry includes a brief definition and, where applicable, a reference to the
|
Each entry includes a brief definition and, where applicable, a reference to the
|
||||||
relevant specification or documentation page.
|
relevant specification or documentation page.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**AEAD** -- Authenticated Encryption with Associated Data. A symmetric encryption
|
**AEAD** -- Authenticated Encryption with Associated Data. A symmetric encryption
|
||||||
scheme that provides both confidentiality and integrity. quicproquo uses
|
scheme that provides both confidentiality and integrity. quicprochat uses
|
||||||
AES-128-GCM (in the MLS ciphersuite). See [Cryptography Overview](../cryptography/overview.md).
|
AES-128-GCM (in the MLS ciphersuite). See [Cryptography Overview](../cryptography/overview.md).
|
||||||
|
|
||||||
**ALPN** -- Application-Layer Protocol Negotiation. A TLS extension that allows
|
**ALPN** -- Application-Layer Protocol Negotiation. A TLS extension that allows
|
||||||
the client and server to agree on an application protocol during the TLS
|
the client and server to agree on an application protocol during the TLS
|
||||||
handshake. quicproquo v2 uses the ALPN token `"qpq"` (replacing the legacy
|
handshake. quicprochat v2 uses the ALPN token `"qpc"` (replacing the legacy
|
||||||
`"capnp"` token used in v1). See [Wire Format Overview](../wire-format/overview.md).
|
`"capnp"` token used in v1). See [Wire Format Overview](../wire-format/overview.md).
|
||||||
|
|
||||||
**Argon2id** -- A memory-hard password hashing and key derivation function
|
**Argon2id** -- A memory-hard password hashing and key derivation function
|
||||||
(winner of the Password Hashing Competition, 2015). quicproquo uses Argon2id
|
(winner of the Password Hashing Competition, 2015). quicprochat uses Argon2id
|
||||||
to derive the SQLCipher encryption key from the server's passphrase, and
|
to derive the SQLCipher encryption key from the server's passphrase, and
|
||||||
optionally for client-side key derivation. See [Storage Backend](../internals/storage-backend.md).
|
optionally for client-side key derivation. See [Storage Backend](../internals/storage-backend.md).
|
||||||
|
|
||||||
@@ -25,10 +25,10 @@ registration and login, stores single-use MLS KeyPackages, and manages hybrid
|
|||||||
post-quantum public keys. See [Authentication Service Internals](../internals/authentication-service.md).
|
post-quantum public keys. See [Authentication Service Internals](../internals/authentication-service.md).
|
||||||
|
|
||||||
**Cap'n Proto** -- A zero-copy serialisation format with a built-in RPC system.
|
**Cap'n Proto** -- A zero-copy serialisation format with a built-in RPC system.
|
||||||
Used in quicproquo v1 for all wire messages and service RPCs. Schemas lived in
|
Used in quicprochat v1 for all wire messages and service RPCs. Schemas lived in
|
||||||
`schemas/*.capnp`. In v2, Cap'n Proto is replaced by Protobuf (prost) for RPC
|
`schemas/*.capnp`. In v2, Cap'n Proto is replaced by Protobuf (prost) for RPC
|
||||||
messages, though the legacy Cap'n Proto types remain in `quicproquo-proto` for
|
messages, though the legacy Cap'n Proto types remain in `quicprochat-proto` for
|
||||||
backward compatibility. See the v1 archive in `crates/quicproquo-proto/`.
|
backward compatibility. See the v1 archive in `crates/quicprochat-proto/`.
|
||||||
|
|
||||||
**Commit** -- An MLS message type that advances the group to a new epoch. When a
|
**Commit** -- An MLS message type that advances the group to a new epoch. When a
|
||||||
member sends a Commit (e.g., after adding or removing a member), all group
|
member sends a Commit (e.g., after adding or removing a member), all group
|
||||||
@@ -37,13 +37,13 @@ forward secrecy and post-compromise security.
|
|||||||
See [MLS (RFC 9420)](../protocol-layers/mls.md).
|
See [MLS (RFC 9420)](../protocol-layers/mls.md).
|
||||||
|
|
||||||
**Credential** -- An MLS identity binding that associates a member's signing key
|
**Credential** -- An MLS identity binding that associates a member's signing key
|
||||||
with their identity. quicproquo uses `BasicCredential`, which contains the
|
with their identity. quicprochat uses `BasicCredential`, which contains the
|
||||||
raw Ed25519 public key bytes. See
|
raw Ed25519 public key bytes. See
|
||||||
[Ed25519 Identity Keys](../cryptography/identity-keys.md).
|
[Ed25519 Identity Keys](../cryptography/identity-keys.md).
|
||||||
|
|
||||||
**DER** -- Distinguished Encoding Rules. A binary encoding format for ASN.1
|
**DER** -- Distinguished Encoding Rules. A binary encoding format for ASN.1
|
||||||
structures, used for X.509 certificates and TLS certificate chains. The
|
structures, used for X.509 certificates and TLS certificate chains. The
|
||||||
self-signed TLS certificate generated by quicproquo is DER-encoded.
|
self-signed TLS certificate generated by quicprochat is DER-encoded.
|
||||||
|
|
||||||
**DS** -- Delivery Service. The server component that provides store-and-forward
|
**DS** -- Delivery Service. The server component that provides store-and-forward
|
||||||
relay for opaque MLS payloads. The DS never inspects ciphertext -- it routes
|
relay for opaque MLS payloads. The DS never inspects ciphertext -- it routes
|
||||||
@@ -52,7 +52,7 @@ See [Architecture Overview](../architecture/overview.md).
|
|||||||
|
|
||||||
**Ed25519** -- Edwards-curve Digital Signature Algorithm on Curve25519. Used for
|
**Ed25519** -- Edwards-curve Digital Signature Algorithm on Curve25519. Used for
|
||||||
MLS identity credentials and signing (KeyPackages, Commits, group operations).
|
MLS identity credentials and signing (KeyPackages, Commits, group operations).
|
||||||
quicproquo uses the `ed25519-dalek` crate.
|
quicprochat uses the `ed25519-dalek` crate.
|
||||||
See [Ed25519 Identity Keys](../cryptography/identity-keys.md).
|
See [Ed25519 Identity Keys](../cryptography/identity-keys.md).
|
||||||
|
|
||||||
**Epoch** -- The version number of an MLS group's key state. Each Commit
|
**Epoch** -- The version number of an MLS group's key state. Each Commit
|
||||||
@@ -66,11 +66,11 @@ the epoch ratchet: key material from earlier epochs is deleted when the epoch
|
|||||||
advances. See [Forward Secrecy](../cryptography/forward-secrecy.md).
|
advances. See [Forward Secrecy](../cryptography/forward-secrecy.md).
|
||||||
|
|
||||||
**HKDF** -- HMAC-based Key Derivation Function. Used in MLS to derive symmetric
|
**HKDF** -- HMAC-based Key Derivation Function. Used in MLS to derive symmetric
|
||||||
keys from shared secrets. quicproquo uses HKDF-SHA256.
|
keys from shared secrets. quicprochat uses HKDF-SHA256.
|
||||||
|
|
||||||
**HPKE** -- Hybrid Public Key Encryption. The public-key encryption scheme used
|
**HPKE** -- Hybrid Public Key Encryption. The public-key encryption scheme used
|
||||||
in MLS for key exchange (encrypting to a KeyPackage's init key). Defined in
|
in MLS for key exchange (encrypting to a KeyPackage's init key). Defined in
|
||||||
RFC 9180. In quicproquo, HPKE uses DHKEM(X25519, HKDF-SHA256).
|
RFC 9180. In quicprochat, HPKE uses DHKEM(X25519, HKDF-SHA256).
|
||||||
See [Hybrid KEM](../protocol-layers/hybrid-kem.md).
|
See [Hybrid KEM](../protocol-layers/hybrid-kem.md).
|
||||||
|
|
||||||
**KEM** -- Key Encapsulation Mechanism. A cryptographic primitive that generates
|
**KEM** -- Key Encapsulation Mechanism. A cryptographic primitive that generates
|
||||||
@@ -85,7 +85,7 @@ is consumed on fetch. See
|
|||||||
|
|
||||||
**ML-KEM-768** -- Module-Lattice-based Key Encapsulation Mechanism, security
|
**ML-KEM-768** -- Module-Lattice-based Key Encapsulation Mechanism, security
|
||||||
level 3 (NIST FIPS 203). A post-quantum KEM based on the hardness of the
|
level 3 (NIST FIPS 203). A post-quantum KEM based on the hardness of the
|
||||||
module learning-with-errors (MLWE) problem. quicproquo uses ML-KEM-768 in a
|
module learning-with-errors (MLWE) problem. quicprochat uses ML-KEM-768 in a
|
||||||
hybrid construction with X25519 for post-quantum sealed envelope encryption.
|
hybrid construction with X25519 for post-quantum sealed envelope encryption.
|
||||||
See [Post-Quantum Readiness](../cryptography/post-quantum-readiness.md).
|
See [Post-Quantum Readiness](../cryptography/post-quantum-readiness.md).
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ See [MLS (RFC 9420)](../protocol-layers/mls.md).
|
|||||||
**OPAQUE** -- Asymmetric Password-Authenticated Key Exchange (RFC 9497). A
|
**OPAQUE** -- Asymmetric Password-Authenticated Key Exchange (RFC 9497). A
|
||||||
password authentication protocol in which the server never learns the user's
|
password authentication protocol in which the server never learns the user's
|
||||||
password, not even during registration. The server stores an OPAQUE registration
|
password, not even during registration. The server stores an OPAQUE registration
|
||||||
record derived from the password. quicproquo uses OPAQUE for all user
|
record derived from the password. quicprochat uses OPAQUE for all user
|
||||||
authentication (replacing static token auth in v1).
|
authentication (replacing static token auth in v1).
|
||||||
See [Authentication Service Internals](../internals/authentication-service.md).
|
See [Authentication Service Internals](../internals/authentication-service.md).
|
||||||
|
|
||||||
@@ -114,19 +114,19 @@ KeyPackage and message fetch.
|
|||||||
See [Future Research](../roadmap/future-research.md).
|
See [Future Research](../roadmap/future-research.md).
|
||||||
|
|
||||||
**prost** -- A Rust Protobuf code generation and runtime library. Used in
|
**prost** -- A Rust Protobuf code generation and runtime library. Used in
|
||||||
quicproquo v2 to generate Rust types from `proto/qpq/v1/*.proto` files at
|
quicprochat v2 to generate Rust types from `proto/qpc/v1/*.proto` files at
|
||||||
build time. The generated types live in `crates/quicproquo-proto/`.
|
build time. The generated types live in `crates/quicprochat-proto/`.
|
||||||
See [Rust Crate Documentation](references.md).
|
See [Rust Crate Documentation](references.md).
|
||||||
|
|
||||||
**Protobuf** -- Protocol Buffers. A language-neutral, binary serialisation format
|
**Protobuf** -- Protocol Buffers. A language-neutral, binary serialisation format
|
||||||
from Google. quicproquo v2 uses Protobuf for all RPC message payloads, encoded
|
from Google. quicprochat v2 uses Protobuf for all RPC message payloads, encoded
|
||||||
using the `prost` crate. Proto definitions live in `proto/qpq/v1/`.
|
using the `prost` crate. Proto definitions live in `proto/qpc/v1/`.
|
||||||
See [Wire Format Overview](../wire-format/overview.md).
|
See [Wire Format Overview](../wire-format/overview.md).
|
||||||
|
|
||||||
**QUIC** -- A UDP-based, multiplexed, encrypted transport protocol defined in
|
**QUIC** -- A UDP-based, multiplexed, encrypted transport protocol defined in
|
||||||
RFC 9000. QUIC integrates TLS 1.3 for authentication and confidentiality and
|
RFC 9000. QUIC integrates TLS 1.3 for authentication and confidentiality and
|
||||||
provides 0-RTT connection establishment, stream multiplexing, and built-in
|
provides 0-RTT connection establishment, stream multiplexing, and built-in
|
||||||
congestion control. quicproquo uses the `quinn` crate.
|
congestion control. quicprochat uses the `quinn` crate.
|
||||||
See [QUIC + TLS 1.3](../protocol-layers/quic-tls.md).
|
See [QUIC + TLS 1.3](../protocol-layers/quic-tls.md).
|
||||||
|
|
||||||
**Ratchet Tree** -- The binary tree data structure used in MLS for efficient
|
**Ratchet Tree** -- The binary tree data structure used in MLS for efficient
|
||||||
@@ -135,13 +135,13 @@ hold derived key material. Updates propagate along the path from a leaf to the
|
|||||||
root, giving O(log N) cost for key updates in a group of N members.
|
root, giving O(log N) cost for key updates in a group of N members.
|
||||||
|
|
||||||
**SQLCipher** -- An open-source extension to SQLite that provides transparent,
|
**SQLCipher** -- An open-source extension to SQLite that provides transparent,
|
||||||
page-level AES-256 encryption of the database file. quicproquo uses SQLCipher
|
page-level AES-256 encryption of the database file. quicprochat uses SQLCipher
|
||||||
as the primary server-side storage backend via the `rusqlite` crate with the
|
as the primary server-side storage backend via the `rusqlite` crate with the
|
||||||
`sqlcipher` feature. The encryption key is derived from a server passphrase
|
`sqlcipher` feature. The encryption key is derived from a server passphrase
|
||||||
using Argon2id. See [Storage Backend](../internals/storage-backend.md).
|
using Argon2id. See [Storage Backend](../internals/storage-backend.md).
|
||||||
|
|
||||||
**TLS 1.3** -- Transport Layer Security version 1.3, defined in RFC 8446. The
|
**TLS 1.3** -- Transport Layer Security version 1.3, defined in RFC 8446. The
|
||||||
standard for authenticated, encrypted transport. quicproquo uses TLS 1.3
|
standard for authenticated, encrypted transport. quicprochat uses TLS 1.3
|
||||||
exclusively (via `rustls` with `TLS13` cipher suites only) as part of the QUIC
|
exclusively (via `rustls` with `TLS13` cipher suites only) as part of the QUIC
|
||||||
transport. See [QUIC + TLS 1.3](../protocol-layers/quic-tls.md).
|
transport. See [QUIC + TLS 1.3](../protocol-layers/quic-tls.md).
|
||||||
|
|
||||||
@@ -153,11 +153,11 @@ KeyPackage. See [MLS (RFC 9420)](../protocol-layers/mls.md).
|
|||||||
**X25519** -- Elliptic curve Diffie-Hellman key exchange on Curve25519 (using
|
**X25519** -- Elliptic curve Diffie-Hellman key exchange on Curve25519 (using
|
||||||
the Montgomery form). Used as the classical component of DHKEM in MLS HPKE
|
the Montgomery form). Used as the classical component of DHKEM in MLS HPKE
|
||||||
and in the hybrid KEM (X25519 + ML-KEM-768).
|
and in the hybrid KEM (X25519 + ML-KEM-768).
|
||||||
quicproquo uses the `x25519-dalek` crate.
|
quicprochat uses the `x25519-dalek` crate.
|
||||||
See [Cryptography Overview](../cryptography/overview.md).
|
See [Cryptography Overview](../cryptography/overview.md).
|
||||||
|
|
||||||
**Zeroize** -- The practice of securely clearing sensitive data (private keys,
|
**Zeroize** -- The practice of securely clearing sensitive data (private keys,
|
||||||
shared secrets) from memory when it is no longer needed. quicproquo uses the
|
shared secrets) from memory when it is no longer needed. quicprochat uses the
|
||||||
`zeroize` crate with the `ZeroizeOnDrop` derive macro to ensure that key material
|
`zeroize` crate with the `ZeroizeOnDrop` derive macro to ensure that key material
|
||||||
is overwritten on drop.
|
is overwritten on drop.
|
||||||
See [Key Lifecycle and Zeroization](../cryptography/key-lifecycle.md).
|
See [Key Lifecycle and Zeroization](../cryptography/key-lifecycle.md).
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# References and Further Reading
|
# References and Further Reading
|
||||||
|
|
||||||
This page collects the standards, crate documentation, and research papers
|
This page collects the standards, crate documentation, and research papers
|
||||||
referenced throughout the quicproquo documentation. Entries are organised by
|
referenced throughout the quicprochat documentation. Entries are organised by
|
||||||
category.
|
category.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -10,26 +10,26 @@ category.
|
|||||||
|
|
||||||
| Reference | Description |
|
| Reference | Description |
|
||||||
|-----------|-------------|
|
|-----------|-------------|
|
||||||
| [RFC 9420 -- The Messaging Layer Security (MLS) Protocol](https://datatracker.ietf.org/doc/rfc9420/) | The group key agreement protocol used by quicproquo. Defines KeyPackages, Welcome messages, Commits, the ratchet tree, epoch advancement, and the security properties (forward secrecy, post-compromise security). See [MLS (RFC 9420)](../protocol-layers/mls.md). |
|
| [RFC 9420 -- The Messaging Layer Security (MLS) Protocol](https://datatracker.ietf.org/doc/rfc9420/) | The group key agreement protocol used by quicprochat. Defines KeyPackages, Welcome messages, Commits, the ratchet tree, epoch advancement, and the security properties (forward secrecy, post-compromise security). See [MLS (RFC 9420)](../protocol-layers/mls.md). |
|
||||||
| [RFC 9497 -- The OPAQUE Asymmetric PAKE Protocol](https://datatracker.ietf.org/doc/rfc9497/) | Asymmetric password-authenticated key exchange. quicproquo uses OPAQUE for all user registration and login. The server never learns the user's password. See [Authentication Service Internals](../internals/authentication-service.md). |
|
| [RFC 9497 -- The OPAQUE Asymmetric PAKE Protocol](https://datatracker.ietf.org/doc/rfc9497/) | Asymmetric password-authenticated key exchange. quicprochat uses OPAQUE for all user registration and login. The server never learns the user's password. See [Authentication Service Internals](../internals/authentication-service.md). |
|
||||||
| [RFC 9000 -- QUIC: A UDP-Based Multiplexed and Secure Transport](https://datatracker.ietf.org/doc/rfc9000/) | The transport protocol underlying quicproquo's primary connection layer. Provides multiplexed streams, 0-RTT connection establishment, and built-in congestion control. See [QUIC + TLS 1.3](../protocol-layers/quic-tls.md). |
|
| [RFC 9000 -- QUIC: A UDP-Based Multiplexed and Secure Transport](https://datatracker.ietf.org/doc/rfc9000/) | The transport protocol underlying quicprochat's primary connection layer. Provides multiplexed streams, 0-RTT connection establishment, and built-in congestion control. See [QUIC + TLS 1.3](../protocol-layers/quic-tls.md). |
|
||||||
| [RFC 9001 -- Using TLS to Secure QUIC](https://datatracker.ietf.org/doc/rfc9001/) | Defines how TLS 1.3 is integrated into QUIC for authentication and key exchange. quicproquo uses this via the `quinn` + `rustls` stack. |
|
| [RFC 9001 -- Using TLS to Secure QUIC](https://datatracker.ietf.org/doc/rfc9001/) | Defines how TLS 1.3 is integrated into QUIC for authentication and key exchange. quicprochat uses this via the `quinn` + `rustls` stack. |
|
||||||
| [RFC 8446 -- The Transport Layer Security (TLS) Protocol Version 1.3](https://datatracker.ietf.org/doc/rfc8446/) | The TLS version used exclusively by quicproquo (no TLS 1.2 fallback). Provides the handshake, key schedule, and record layer for QUIC transport security. |
|
| [RFC 8446 -- The Transport Layer Security (TLS) Protocol Version 1.3](https://datatracker.ietf.org/doc/rfc8446/) | The TLS version used exclusively by quicprochat (no TLS 1.2 fallback). Provides the handshake, key schedule, and record layer for QUIC transport security. |
|
||||||
| [RFC 9180 -- Hybrid Public Key Encryption (HPKE)](https://datatracker.ietf.org/doc/rfc9180/) | The public-key encryption scheme used internally by MLS for encrypting to KeyPackage init keys. quicproquo's MLS ciphersuite uses DHKEM(X25519, HKDF-SHA256) with AES-128-GCM. |
|
| [RFC 9180 -- Hybrid Public Key Encryption (HPKE)](https://datatracker.ietf.org/doc/rfc9180/) | The public-key encryption scheme used internally by MLS for encrypting to KeyPackage init keys. quicprochat's MLS ciphersuite uses DHKEM(X25519, HKDF-SHA256) with AES-128-GCM. |
|
||||||
| [NIST FIPS 203 -- Module-Lattice-Based Key-Encapsulation Mechanism Standard (ML-KEM)](https://csrc.nist.gov/pubs/fips/203/final) | The post-quantum KEM standard. quicproquo uses ML-KEM-768 in a hybrid construction with X25519 for post-quantum sealed envelope encryption. See [Post-Quantum Readiness](../cryptography/post-quantum-readiness.md). |
|
| [NIST FIPS 203 -- Module-Lattice-Based Key-Encapsulation Mechanism Standard (ML-KEM)](https://csrc.nist.gov/pubs/fips/203/final) | The post-quantum KEM standard. quicprochat uses ML-KEM-768 in a hybrid construction with X25519 for post-quantum sealed envelope encryption. See [Post-Quantum Readiness](../cryptography/post-quantum-readiness.md). |
|
||||||
| [Protocol Buffers Language Guide (proto3)](https://protobuf.dev/programming-guides/proto3/) | The binary serialisation format used for all v2 RPC message payloads. quicproquo proto definitions live in `proto/qpq/v1/`. See [Wire Format Overview](../wire-format/overview.md). |
|
| [Protocol Buffers Language Guide (proto3)](https://protobuf.dev/programming-guides/proto3/) | The binary serialisation format used for all v2 RPC message payloads. quicprochat proto definitions live in `proto/qpc/v1/`. See [Wire Format Overview](../wire-format/overview.md). |
|
||||||
| [draft-ietf-tls-hybrid-design -- Hybrid Key Exchange in TLS 1.3](https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/) | The combiner approach used by quicproquo's hybrid KEM construction (X25519 shared secret concatenated with ML-KEM-768 shared secret, fed through HKDF). See [Hybrid KEM](../protocol-layers/hybrid-kem.md). |
|
| [draft-ietf-tls-hybrid-design -- Hybrid Key Exchange in TLS 1.3](https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/) | The combiner approach used by quicprochat's hybrid KEM construction (X25519 shared secret concatenated with ML-KEM-768 shared secret, fed through HKDF). See [Hybrid KEM](../protocol-layers/hybrid-kem.md). |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Rust Crate Documentation
|
## Rust Crate Documentation
|
||||||
|
|
||||||
| Crate | docs.rs | Role in quicproquo |
|
| Crate | docs.rs | Role in quicprochat |
|
||||||
|-------|---------|----------------------|
|
|-------|---------|----------------------|
|
||||||
| `openmls` | [docs.rs/openmls](https://docs.rs/openmls/) | MLS protocol implementation: group creation, member addition, Welcome processing, application message encryption/decryption. See [MLS (RFC 9420)](../protocol-layers/mls.md). |
|
| `openmls` | [docs.rs/openmls](https://docs.rs/openmls/) | MLS protocol implementation: group creation, member addition, Welcome processing, application message encryption/decryption. See [MLS (RFC 9420)](../protocol-layers/mls.md). |
|
||||||
| `openmls_rust_crypto` | [docs.rs/openmls_rust_crypto](https://docs.rs/openmls_rust_crypto/) | Pure-Rust cryptographic backend for openmls. Provides the `OpenMlsRustCrypto` provider used by `GroupMember`. |
|
| `openmls_rust_crypto` | [docs.rs/openmls_rust_crypto](https://docs.rs/openmls_rust_crypto/) | Pure-Rust cryptographic backend for openmls. Provides the `OpenMlsRustCrypto` provider used by `GroupMember`. |
|
||||||
| `prost` | [docs.rs/prost](https://docs.rs/prost/) | Protobuf runtime for Rust. Used to encode/decode all v2 RPC messages. Generated types are in `crates/quicproquo-proto/`. |
|
| `prost` | [docs.rs/prost](https://docs.rs/prost/) | Protobuf runtime for Rust. Used to encode/decode all v2 RPC messages. Generated types are in `crates/quicprochat-proto/`. |
|
||||||
| `prost-build` | [docs.rs/prost-build](https://docs.rs/prost-build/) | Build-time Protobuf code generator invoked from `crates/quicproquo-proto/build.rs`. Reads `.proto` files and emits Rust structs. |
|
| `prost-build` | [docs.rs/prost-build](https://docs.rs/prost-build/) | Build-time Protobuf code generator invoked from `crates/quicprochat-proto/build.rs`. Reads `.proto` files and emits Rust structs. |
|
||||||
| `protobuf-src` | [docs.rs/protobuf-src](https://docs.rs/protobuf-src/) | Vendors the `protoc` compiler as a build dependency. No system-installed protoc required. |
|
| `protobuf-src` | [docs.rs/protobuf-src](https://docs.rs/protobuf-src/) | Vendors the `protoc` compiler as a build dependency. No system-installed protoc required. |
|
||||||
| `quinn` | [docs.rs/quinn](https://docs.rs/quinn/) | QUIC transport implementation. Provides the `Endpoint`, `Connection`, and stream types for client and server. See [QUIC + TLS 1.3](../protocol-layers/quic-tls.md). |
|
| `quinn` | [docs.rs/quinn](https://docs.rs/quinn/) | QUIC transport implementation. Provides the `Endpoint`, `Connection`, and stream types for client and server. See [QUIC + TLS 1.3](../protocol-layers/quic-tls.md). |
|
||||||
| `rustls` | [docs.rs/rustls](https://docs.rs/rustls/) | TLS 1.3 implementation used by `quinn`. Configured with `TLS13` cipher suites only and custom certificate verification. |
|
| `rustls` | [docs.rs/rustls](https://docs.rs/rustls/) | TLS 1.3 implementation used by `quinn`. Configured with `TLS13` cipher suites only and custom certificate verification. |
|
||||||
@@ -40,9 +40,9 @@ category.
|
|||||||
| `ed25519-dalek` | [docs.rs/ed25519-dalek](https://docs.rs/ed25519-dalek/) | Ed25519 signing and verification. Used for MLS identity credentials (`BasicCredential`). See [Ed25519 Identity Keys](../cryptography/identity-keys.md). |
|
| `ed25519-dalek` | [docs.rs/ed25519-dalek](https://docs.rs/ed25519-dalek/) | Ed25519 signing and verification. Used for MLS identity credentials (`BasicCredential`). See [Ed25519 Identity Keys](../cryptography/identity-keys.md). |
|
||||||
| `x25519-dalek` | [docs.rs/x25519-dalek](https://docs.rs/x25519-dalek/) | X25519 Diffie-Hellman key exchange. Used in hybrid KEM (X25519 + ML-KEM-768) and as the classical component of DHKEM in MLS HPKE. See [Hybrid KEM](../protocol-layers/hybrid-kem.md). |
|
| `x25519-dalek` | [docs.rs/x25519-dalek](https://docs.rs/x25519-dalek/) | X25519 Diffie-Hellman key exchange. Used in hybrid KEM (X25519 + ML-KEM-768) and as the classical component of DHKEM in MLS HPKE. See [Hybrid KEM](../protocol-layers/hybrid-kem.md). |
|
||||||
| `zeroize` | [docs.rs/zeroize](https://docs.rs/zeroize/) | Secure memory zeroisation. All private key types implement `Zeroize + ZeroizeOnDrop`. See [Key Lifecycle and Zeroization](../cryptography/key-lifecycle.md). |
|
| `zeroize` | [docs.rs/zeroize](https://docs.rs/zeroize/) | Secure memory zeroisation. All private key types implement `Zeroize + ZeroizeOnDrop`. See [Key Lifecycle and Zeroization](../cryptography/key-lifecycle.md). |
|
||||||
| `bytes` | [docs.rs/bytes](https://docs.rs/bytes/) | Zero-copy byte buffer abstraction. Used in the RPC framing layer (`quicproquo-rpc`) for efficient frame encoding/decoding without copying. |
|
| `bytes` | [docs.rs/bytes](https://docs.rs/bytes/) | Zero-copy byte buffer abstraction. Used in the RPC framing layer (`quicprochat-rpc`) for efficient frame encoding/decoding without copying. |
|
||||||
| `tokio` | [docs.rs/tokio](https://docs.rs/tokio/) | Async runtime. All server and client I/O runs on Tokio. |
|
| `tokio` | [docs.rs/tokio](https://docs.rs/tokio/) | Async runtime. All server and client I/O runs on Tokio. |
|
||||||
| `tower` | [docs.rs/tower](https://docs.rs/tower/) | Service abstraction and middleware framework. Used in `quicproquo-rpc` for RPC middleware (auth, rate limiting, tracing). |
|
| `tower` | [docs.rs/tower](https://docs.rs/tower/) | Service abstraction and middleware framework. Used in `quicprochat-rpc` for RPC middleware (auth, rate limiting, tracing). |
|
||||||
| `clap` | [docs.rs/clap](https://docs.rs/clap/) | CLI argument parser for the client binary. |
|
| `clap` | [docs.rs/clap](https://docs.rs/clap/) | CLI argument parser for the client binary. |
|
||||||
| `tracing` | [docs.rs/tracing](https://docs.rs/tracing/) | Structured logging framework. Used throughout the server for request logging and diagnostics. |
|
| `tracing` | [docs.rs/tracing](https://docs.rs/tracing/) | Structured logging framework. Used throughout the server for request logging and diagnostics. |
|
||||||
| `thiserror` | [docs.rs/thiserror](https://docs.rs/thiserror/) | Derive macro for typed error enums in library crates. |
|
| `thiserror` | [docs.rs/thiserror](https://docs.rs/thiserror/) | Derive macro for typed error enums in library crates. |
|
||||||
@@ -61,7 +61,7 @@ Katriel Cohn-Gordon, Cas Cremers, Luke Garratt, Jon Millican, and Kevin Milner.
|
|||||||
This paper analyses the security properties of group messaging protocols and
|
This paper analyses the security properties of group messaging protocols and
|
||||||
motivates the design of MLS. It defines the security goals (forward secrecy,
|
motivates the design of MLS. It defines the security goals (forward secrecy,
|
||||||
post-compromise security, asynchronous operation) that MLS formalises into a
|
post-compromise security, asynchronous operation) that MLS formalises into a
|
||||||
protocol. Essential background for understanding why quicproquo uses MLS
|
protocol. Essential background for understanding why quicprochat uses MLS
|
||||||
rather than extending the Signal protocol to groups.
|
rather than extending the Signal protocol to groups.
|
||||||
|
|
||||||
### Signal Protocol
|
### Signal Protocol
|
||||||
@@ -71,7 +71,7 @@ Trevor Perrin and Moxie Marlinspike.
|
|||||||
[signal.org/docs/specifications/doubleratchet](https://signal.org/docs/specifications/doubleratchet/)
|
[signal.org/docs/specifications/doubleratchet](https://signal.org/docs/specifications/doubleratchet/)
|
||||||
|
|
||||||
Defines the double ratchet used in Signal's 1:1 messaging. Relevant as a
|
Defines the double ratchet used in Signal's 1:1 messaging. Relevant as a
|
||||||
potential optimisation for quicproquo's 1:1 channels (see
|
potential optimisation for quicprochat's 1:1 channels (see
|
||||||
[Future Research: Double-Ratchet DM Layer](../roadmap/future-research.md#double-ratchet-dm-layer))
|
[Future Research: Double-Ratchet DM Layer](../roadmap/future-research.md#double-ratchet-dm-layer))
|
||||||
and as background for understanding how MLS generalises ratcheting to groups.
|
and as background for understanding how MLS generalises ratcheting to groups.
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ Roberto Avanzi et al.
|
|||||||
[NIST PQC Round 3 submission](https://pq-crystals.org/kyber/)
|
[NIST PQC Round 3 submission](https://pq-crystals.org/kyber/)
|
||||||
|
|
||||||
The predecessor to ML-KEM (NIST FIPS 203). CRYSTALS-Kyber was selected by NIST
|
The predecessor to ML-KEM (NIST FIPS 203). CRYSTALS-Kyber was selected by NIST
|
||||||
and standardised as ML-KEM. quicproquo uses the `ml-kem` crate which
|
and standardised as ML-KEM. quicprochat uses the `ml-kem` crate which
|
||||||
implements the final FIPS 203 standard.
|
implements the final FIPS 203 standard.
|
||||||
|
|
||||||
### OPAQUE
|
### OPAQUE
|
||||||
@@ -100,7 +100,7 @@ Stanislaw Jarecki, Hugo Krawczyk, and Jiayu Xu.
|
|||||||
*EUROCRYPT 2018.*
|
*EUROCRYPT 2018.*
|
||||||
|
|
||||||
The original academic paper introducing OPAQUE. Standardised as RFC 9497.
|
The original academic paper introducing OPAQUE. Standardised as RFC 9497.
|
||||||
Relevant background for understanding the security guarantees of quicproquo's
|
Relevant background for understanding the security guarantees of quicprochat's
|
||||||
authentication system: the server stores a verifier (not the password), and the
|
authentication system: the server stores a verifier (not the password), and the
|
||||||
protocol is resistant to pre-computation attacks even if the server's verifier
|
protocol is resistant to pre-computation attacks even if the server's verifier
|
||||||
database is stolen.
|
database is stolen.
|
||||||
@@ -112,7 +112,7 @@ Signal Blog.
|
|||||||
[signal.org/blog/sealed-sender](https://signal.org/blog/sealed-sender/)
|
[signal.org/blog/sealed-sender](https://signal.org/blog/sealed-sender/)
|
||||||
|
|
||||||
Describes Signal's approach to hiding sender identity from the server. Relevant
|
Describes Signal's approach to hiding sender identity from the server. Relevant
|
||||||
to quicproquo's future research on metadata resistance (see
|
to quicprochat's future research on metadata resistance (see
|
||||||
[Future Research](../roadmap/future-research.md)).
|
[Future Research](../roadmap/future-research.md)).
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -120,7 +120,7 @@ to quicproquo's future research on metadata resistance (see
|
|||||||
## Cross-references
|
## Cross-references
|
||||||
|
|
||||||
- [Glossary](glossary.md) -- definitions of terms used in these references
|
- [Glossary](glossary.md) -- definitions of terms used in these references
|
||||||
- [Protocol Layers Overview](../protocol-layers/overview.md) -- how the protocols layer in quicproquo
|
- [Protocol Layers Overview](../protocol-layers/overview.md) -- how the protocols layer in quicprochat
|
||||||
- [Cryptography Overview](../cryptography/overview.md) -- cryptographic properties and threat model
|
- [Cryptography Overview](../cryptography/overview.md) -- cryptographic properties and threat model
|
||||||
- [Future Research](../roadmap/future-research.md) -- technologies under consideration
|
- [Future Research](../roadmap/future-research.md) -- technologies under consideration
|
||||||
- [Milestones](../roadmap/milestones.md) -- current project status
|
- [Milestones](../roadmap/milestones.md) -- current project status
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Crate Responsibilities
|
# Crate Responsibilities
|
||||||
|
|
||||||
The quicproquo workspace contains nine crates. The core four (proto, core,
|
The quicprochat workspace contains nine crates. The core four (proto, core,
|
||||||
server, client) follow strict layering rules; each owns one concern and depends
|
server, client) follow strict layering rules; each owns one concern and depends
|
||||||
only on the crates below it. The workspace also includes dedicated crates for
|
only on the crates below it. The workspace also includes dedicated crates for
|
||||||
the RPC framework, client SDK, key transparency, plugin API, and P2P. This
|
the RPC framework, client SDK, key transparency, plugin API, and P2P. This
|
||||||
@@ -13,7 +13,7 @@ crates relate to one another.
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
+-------------------+ +-------------------+
|
+-------------------+ +-------------------+
|
||||||
| quicproquo-client | | quicproquo-sdk |
|
| quicprochat-client | | quicprochat-sdk |
|
||||||
| (CLI/TUI binary) | | (QpqClient, store)|
|
| (CLI/TUI binary) | | (QpqClient, store)|
|
||||||
+--------+----------+ +--------+----------+
|
+--------+----------+ +--------+----------+
|
||||||
| |
|
| |
|
||||||
@@ -21,7 +21,7 @@ crates relate to one another.
|
|||||||
| |
|
| |
|
||||||
v v
|
v v
|
||||||
+-----------+----------+
|
+-----------+----------+
|
||||||
| quicproquo-rpc |
|
| quicprochat-rpc |
|
||||||
| (framing, server, |
|
| (framing, server, |
|
||||||
| client, middleware) |
|
| client, middleware) |
|
||||||
+--------+-------------+
|
+--------+-------------+
|
||||||
@@ -30,20 +30,20 @@ crates relate to one another.
|
|||||||
| |
|
| |
|
||||||
v v
|
v v
|
||||||
+------------------------+ +-----------------------------+
|
+------------------------+ +-----------------------------+
|
||||||
| quicproquo-core | | quicproquo-server |
|
| quicprochat-core | | quicprochat-server |
|
||||||
| (crypto, MLS, | | (RPC server + domain |
|
| (crypto, MLS, | | (RPC server + domain |
|
||||||
| hybrid KEM) | | services) |
|
| hybrid KEM) | | services) |
|
||||||
+----------+-------------+ +-------------+---------------+
|
+----------+-------------+ +-------------+---------------+
|
||||||
| |
|
| |
|
||||||
| +-------------------+ |
|
| +-------------------+ |
|
||||||
+------>| quicproquo-proto |<--+
|
+------>| quicprochat-proto |<--+
|
||||||
| (capnp legacy + |
|
| (capnp legacy + |
|
||||||
| prost v2 types) |
|
| prost v2 types) |
|
||||||
+-------------------+
|
+-------------------+
|
||||||
|
|
||||||
(separate, no shared deps)
|
(separate, no shared deps)
|
||||||
+-------------------+ +-------------------+ +-------------------+
|
+-------------------+ +-------------------+ +-------------------+
|
||||||
| quicproquo-kt | | quicproquo-p2p | | quicproquo- |
|
| quicprochat-kt | | quicprochat-p2p | | quicprochat- |
|
||||||
| (key transparency)| | (iroh P2P) | | plugin-api |
|
| (key transparency)| | (iroh P2P) | | plugin-api |
|
||||||
+-------------------+ +-------------------+ | (#![no_std] C-ABI)|
|
+-------------------+ +-------------------+ | (#![no_std] C-ABI)|
|
||||||
+-------------------+
|
+-------------------+
|
||||||
@@ -56,7 +56,7 @@ both the sdk and server.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## quicproquo-core
|
## quicprochat-core
|
||||||
|
|
||||||
**Role:** Pure cryptographic logic. No network I/O. No async runtime
|
**Role:** Pure cryptographic logic. No network I/O. No async runtime
|
||||||
dependency.
|
dependency.
|
||||||
@@ -83,11 +83,11 @@ dependency.
|
|||||||
|
|
||||||
`ed25519-dalek`, `openmls`, `openmls_rust_crypto`,
|
`ed25519-dalek`, `openmls`, `openmls_rust_crypto`,
|
||||||
`openmls_traits`, `tls_codec`, `ml-kem`, `x25519-dalek`, `chacha20poly1305`,
|
`openmls_traits`, `tls_codec`, `ml-kem`, `x25519-dalek`, `chacha20poly1305`,
|
||||||
`hkdf`, `sha2`, `zeroize`, `quicproquo-proto`, `serde`, `bincode`, `thiserror`.
|
`hkdf`, `sha2`, `zeroize`, `quicprochat-proto`, `serde`, `bincode`, `thiserror`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## quicproquo-proto
|
## quicprochat-proto
|
||||||
|
|
||||||
**Role:** Protocol type definitions for both v1 (legacy Cap'n Proto) and v2
|
**Role:** Protocol type definitions for both v1 (legacy Cap'n Proto) and v2
|
||||||
(Protobuf/prost). This crate is the single source of truth for wire types and
|
(Protobuf/prost). This crate is the single source of truth for wire types and
|
||||||
@@ -98,9 +98,9 @@ method ID constants.
|
|||||||
| Item | Description |
|
| Item | Description |
|
||||||
|-------------------------------|-------------|
|
|-------------------------------|-------------|
|
||||||
| `schemas/*.capnp` | Legacy Cap'n Proto schemas (auth, delivery, node, federation). |
|
| `schemas/*.capnp` | Legacy Cap'n Proto schemas (auth, delivery, node, federation). |
|
||||||
| `proto/qpq/v1/*.proto` | 14 Protobuf files defining all v2 message types. |
|
| `proto/qpc/v1/*.proto` | 14 Protobuf files defining all v2 message types. |
|
||||||
| `build.rs` | Invokes `capnpc` for legacy types and `prost-build` for v2 types. |
|
| `build.rs` | Invokes `capnpc` for legacy types and `prost-build` for v2 types. |
|
||||||
| `pub mod qpq::v1` | All Protobuf-generated types, included via `prost` `include!`. |
|
| `pub mod qpc::v1` | All Protobuf-generated types, included via `prost` `include!`. |
|
||||||
| `pub mod method_ids` | All 44 RPC method ID constants (u16) plus 4 push event type constants. |
|
| `pub mod method_ids` | All 44 RPC method ID constants (u16) plus 4 push event type constants. |
|
||||||
| `auth_capnp`, `node_capnp`... | Re-exported legacy Cap'n Proto generated modules. |
|
| `auth_capnp`, `node_capnp`... | Re-exported legacy Cap'n Proto generated modules. |
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ method ID constants.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## quicproquo-rpc
|
## quicprochat-rpc
|
||||||
|
|
||||||
**Role:** v2 RPC framework. Implements the custom binary framing protocol,
|
**Role:** v2 RPC framework. Implements the custom binary framing protocol,
|
||||||
server-side dispatch, client-side request/response handling, and Tower
|
server-side dispatch, client-side request/response handling, and Tower
|
||||||
@@ -169,12 +169,12 @@ Push: [event_type: u16 BE][payload_len: u32 BE][protobuf]
|
|||||||
|
|
||||||
### Key dependencies
|
### Key dependencies
|
||||||
|
|
||||||
`quinn`, `rustls`, `tokio`, `bytes`, `tower`, `prost`, `quicproquo-proto`,
|
`quinn`, `rustls`, `tokio`, `bytes`, `tower`, `prost`, `quicprochat-proto`,
|
||||||
`tracing`, `thiserror`.
|
`tracing`, `thiserror`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## quicproquo-sdk
|
## quicprochat-sdk
|
||||||
|
|
||||||
**Role:** High-level client SDK. `QpqClient` wraps the RPC client with
|
**Role:** High-level client SDK. `QpqClient` wraps the RPC client with
|
||||||
typed methods, an async event broadcast channel, and a `ConversationStore`
|
typed methods, an async event broadcast channel, and a `ConversationStore`
|
||||||
@@ -190,17 +190,17 @@ for local conversation state.
|
|||||||
|
|
||||||
### What this crate does NOT do
|
### What this crate does NOT do
|
||||||
|
|
||||||
- No raw frame handling -- delegates to `quicproquo-rpc`.
|
- No raw frame handling -- delegates to `quicprochat-rpc`.
|
||||||
- No MLS group state management -- delegates to `quicproquo-core`.
|
- No MLS group state management -- delegates to `quicprochat-core`.
|
||||||
|
|
||||||
### Key dependencies
|
### Key dependencies
|
||||||
|
|
||||||
`quicproquo-rpc`, `quicproquo-core`, `quicproquo-proto`, `tokio`, `rusqlite`,
|
`quicprochat-rpc`, `quicprochat-core`, `quicprochat-proto`, `tokio`, `rusqlite`,
|
||||||
`prost`, `tracing`, `thiserror`, `anyhow`.
|
`prost`, `tracing`, `thiserror`, `anyhow`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## quicproquo-server
|
## quicprochat-server
|
||||||
|
|
||||||
**Role:** Network-facing server binary. Accepts QUIC + TLS 1.3 connections,
|
**Role:** Network-facing server binary. Accepts QUIC + TLS 1.3 connections,
|
||||||
dispatches 44 Protobuf RPC methods through registered handlers in `domain/`,
|
dispatches 44 Protobuf RPC methods through registered handlers in `domain/`,
|
||||||
@@ -213,13 +213,13 @@ and persists state to SQLCipher.
|
|||||||
| `v2_handlers/` | One handler module per method category (auth, delivery, keys, channel, group, user, kt, blob, device, p2p, federation, moderation, recovery, account). |
|
| `v2_handlers/` | One handler module per method category (auth, delivery, keys, channel, group, user, kt, blob, device, p2p, federation, moderation, recovery, account). |
|
||||||
| `domain/` | Protocol-agnostic domain types and service logic (e.g., `AuthService`, `DeliveryService`, `KeyService`). |
|
| `domain/` | Protocol-agnostic domain types and service logic (e.g., `AuthService`, `DeliveryService`, `KeyService`). |
|
||||||
| `ServerState` | Shared state: SQLCipher connection pool, DashMap waiters, OPAQUE server state. |
|
| `ServerState` | Shared state: SQLCipher connection pool, DashMap waiters, OPAQUE server state. |
|
||||||
| TLS config | Self-signed certificate auto-generated on first run (`rcgen`). TLS 1.3 only, ALPN `qpq`. |
|
| TLS config | Self-signed certificate auto-generated on first run (`rcgen`). TLS 1.3 only, ALPN `qpc`. |
|
||||||
| CLI (`clap`) | `--listen` (default `0.0.0.0:5001`), `--data-dir`, `--tls-cert`, `--tls-key`. |
|
| CLI (`clap`) | `--listen` (default `0.0.0.0:5001`), `--data-dir`, `--tls-cert`, `--tls-key`. |
|
||||||
|
|
||||||
### Connection lifecycle
|
### Connection lifecycle
|
||||||
|
|
||||||
```
|
```
|
||||||
QUIC accept (ALPN: "qpq")
|
QUIC accept (ALPN: "qpc")
|
||||||
+- TLS 1.3 handshake (self-signed cert)
|
+- TLS 1.3 handshake (self-signed cert)
|
||||||
+- Per-stream: read RequestFrame -> dispatch to handler -> write ResponseFrame
|
+- Per-stream: read RequestFrame -> dispatch to handler -> write ResponseFrame
|
||||||
+- Uni-stream (server -> client): write PushFrame for events
|
+- Uni-stream (server -> client): write PushFrame for events
|
||||||
@@ -235,13 +235,13 @@ via `tokio::spawn`.
|
|||||||
|
|
||||||
### Key dependencies
|
### Key dependencies
|
||||||
|
|
||||||
`quicproquo-core`, `quicproquo-proto`, `quicproquo-rpc`, `quinn`, `rustls`,
|
`quicprochat-core`, `quicprochat-proto`, `quicprochat-rpc`, `quinn`, `rustls`,
|
||||||
`rcgen`, `tokio`, `dashmap`, `rusqlite`, `prost`, `clap`, `tracing`, `anyhow`,
|
`rcgen`, `tokio`, `dashmap`, `rusqlite`, `prost`, `clap`, `tracing`, `anyhow`,
|
||||||
`thiserror`.
|
`thiserror`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## quicproquo-client
|
## quicprochat-client
|
||||||
|
|
||||||
**Role:** CLI/TUI client binary. Connects to the server, orchestrates MLS
|
**Role:** CLI/TUI client binary. Connects to the server, orchestrates MLS
|
||||||
group operations via `GroupMember`, and persists identity and group state.
|
group operations via `GroupMember`, and persists identity and group state.
|
||||||
@@ -249,16 +249,16 @@ group operations via `GroupMember`, and persists identity and group state.
|
|||||||
### What this crate does NOT do
|
### What this crate does NOT do
|
||||||
|
|
||||||
- No server-side logic.
|
- No server-side logic.
|
||||||
- No raw frame parsing -- delegates to `quicproquo-sdk` / `quicproquo-rpc`.
|
- No raw frame parsing -- delegates to `quicprochat-sdk` / `quicprochat-rpc`.
|
||||||
|
|
||||||
### Key dependencies
|
### Key dependencies
|
||||||
|
|
||||||
`quicproquo-sdk`, `quicproquo-core`, `quicproquo-proto`, `tokio`, `clap`,
|
`quicprochat-sdk`, `quicprochat-core`, `quicprochat-proto`, `tokio`, `clap`,
|
||||||
`rustyline`, `tracing`, `anyhow`.
|
`rustyline`, `tracing`, `anyhow`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## quicproquo-kt
|
## quicprochat-kt
|
||||||
|
|
||||||
**Role:** Key transparency. Implements an append-only transparency log for
|
**Role:** Key transparency. Implements an append-only transparency log for
|
||||||
Ed25519 public keys with revocation checking and audit support.
|
Ed25519 public keys with revocation checking and audit support.
|
||||||
@@ -268,7 +268,7 @@ Methods exposed: `RevokeKey` (510), `CheckRevocation` (511),
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## quicproquo-plugin-api
|
## quicprochat-plugin-api
|
||||||
|
|
||||||
**Role:** `#![no_std]` C-ABI plugin interface. Defines a stable ABI for
|
**Role:** `#![no_std]` C-ABI plugin interface. Defines a stable ABI for
|
||||||
dynamically loaded plugins with 6 hook points (on_message_send,
|
dynamically loaded plugins with 6 hook points (on_message_send,
|
||||||
@@ -279,11 +279,11 @@ allow plugins compiled for embedded or WASM targets.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## quicproquo-p2p
|
## quicprochat-p2p
|
||||||
|
|
||||||
**Role:** P2P endpoint publish and resolve via iroh. Used by the server and
|
**Role:** P2P endpoint publish and resolve via iroh. Used by the server and
|
||||||
clients for direct peer discovery when the `mesh` feature is enabled on
|
clients for direct peer discovery when the `mesh` feature is enabled on
|
||||||
`quicproquo-client`.
|
`quicprochat-client`.
|
||||||
|
|
||||||
Methods exposed: `PublishEndpoint` (800), `ResolveEndpoint` (801).
|
Methods exposed: `PublishEndpoint` (800), `ResolveEndpoint` (801).
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ build targets due to iroh's large dependency footprint (~90 extra deps).
|
|||||||
|
|
||||||
This layering ensures that:
|
This layering ensures that:
|
||||||
|
|
||||||
- Crypto code can be tested in isolation (`cargo test -p quicproquo-core`).
|
- Crypto code can be tested in isolation (`cargo test -p quicprochat-core`).
|
||||||
- Schema changes propagate automatically through codegen.
|
- Schema changes propagate automatically through codegen.
|
||||||
- The server binary contains no client-side MLS orchestration logic.
|
- The server binary contains no client-side MLS orchestration logic.
|
||||||
- The client binary contains no server-side storage or listener logic.
|
- The client binary contains no server-side storage or listener logic.
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# End-to-End Data Flow
|
# End-to-End Data Flow
|
||||||
|
|
||||||
This page traces the three core data flows through the quicproquo system:
|
This page traces the three core data flows through the quicprochat system:
|
||||||
registration, group creation, and message exchange. Each flow is illustrated
|
registration, group creation, and message exchange. Each flow is illustrated
|
||||||
with an ASCII sequence diagram showing control-plane (AS) and data-plane (DS)
|
with an ASCII sequence diagram showing control-plane (AS) and data-plane (DS)
|
||||||
traffic.
|
traffic.
|
||||||
|
|
||||||
Throughout these flows the server is **MLS-unaware** -- it stores and forwards
|
Throughout these flows the server is **MLS-unaware** -- it stores and forwards
|
||||||
opaque byte blobs without parsing their MLS content. All RPC calls use the v2
|
opaque byte blobs without parsing their MLS content. All RPC calls use the v2
|
||||||
Protobuf framing protocol over QUIC (ALPN: `qpq`, port 5001).
|
Protobuf framing protocol over QUIC (ALPN: `qpc`, port 5001).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Architecture Overview
|
# Architecture Overview
|
||||||
|
|
||||||
quicproquo is an end-to-end encrypted group messaging system built in Rust.
|
quicprochat is an end-to-end encrypted group messaging system built in Rust.
|
||||||
This page describes the high-level architecture: the services that compose the
|
This page describes the high-level architecture: the services that compose the
|
||||||
system, the dual-key cryptographic model, and how the pieces fit together.
|
system, the dual-key cryptographic model, and how the pieces fit together.
|
||||||
|
|
||||||
@@ -27,11 +27,11 @@ connection lifecycle, and push event delivery.
|
|||||||
|
|
||||||
## Identity Key Model
|
## Identity Key Model
|
||||||
|
|
||||||
Each quicproquo client holds a single Ed25519 signing keypair that serves
|
Each quicprochat client holds a single Ed25519 signing keypair that serves
|
||||||
as its long-term identity:
|
as its long-term identity:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
quicproquo Key Model
|
quicprochat Key Model
|
||||||
+--------------------------------------------------+
|
+--------------------------------------------------+
|
||||||
| |
|
| |
|
||||||
| Ed25519 signing keypair (MLS identity) |
|
| Ed25519 signing keypair (MLS identity) |
|
||||||
@@ -71,11 +71,11 @@ For details on the cryptographic properties, see
|
|||||||
| (SDK) | | (SDK) |
|
| (SDK) | | (SDK) |
|
||||||
+--------+---------+ +--------+---------+
|
+--------+---------+ +--------+---------+
|
||||||
| |
|
| |
|
||||||
| QUIC + TLS 1.3 (quinn/rustls) ALPN: "qpq" |
|
| QUIC + TLS 1.3 (quinn/rustls) ALPN: "qpc" |
|
||||||
| |
|
| |
|
||||||
v v
|
v v
|
||||||
+------------------------------------------------------------------------+
|
+------------------------------------------------------------------------+
|
||||||
| quicproquo-server (port 5001) |
|
| quicprochat-server (port 5001) |
|
||||||
| |
|
| |
|
||||||
| +---------------------------+ +--------------------------------+ |
|
| +---------------------------+ +--------------------------------+ |
|
||||||
| | Authentication Service | | Delivery Service | |
|
| | Authentication Service | | Delivery Service | |
|
||||||
@@ -106,7 +106,7 @@ For details on the cryptographic properties, see
|
|||||||
2. KeyPackages are single-use (RFC 9420 requirement). The AS atomically removes
|
2. KeyPackages are single-use (RFC 9420 requirement). The AS atomically removes
|
||||||
a KeyPackage on fetch to enforce this invariant.
|
a KeyPackage on fetch to enforce this invariant.
|
||||||
|
|
||||||
3. QUIC + TLS 1.3 is the sole transport layer. The ALPN identifier is `qpq`.
|
3. QUIC + TLS 1.3 is the sole transport layer. The ALPN identifier is `qpc`.
|
||||||
|
|
||||||
4. Push events (new messages, typing, presence, membership changes) are
|
4. Push events (new messages, typing, presence, membership changes) are
|
||||||
delivered server-to-client on QUIC uni-streams using a separate push frame
|
delivered server-to-client on QUIC uni-streams using a separate push frame
|
||||||
@@ -142,15 +142,15 @@ The implementation is split across nine workspace crates:
|
|||||||
|
|
||||||
| Crate | Role |
|
| Crate | Role |
|
||||||
|------------------------------|-------------------------------------------------------------------|
|
|------------------------------|-------------------------------------------------------------------|
|
||||||
| `quicproquo-core` | Crypto primitives, MLS state machine, hybrid KEM |
|
| `quicprochat-core` | Crypto primitives, MLS state machine, hybrid KEM |
|
||||||
| `quicproquo-proto` | Cap'n Proto legacy types + Protobuf (prost) v2 generated types |
|
| `quicprochat-proto` | Cap'n Proto legacy types + Protobuf (prost) v2 generated types |
|
||||||
| `quicproquo-kt` | Key transparency (append-only log, revocation) |
|
| `quicprochat-kt` | Key transparency (append-only log, revocation) |
|
||||||
| `quicproquo-plugin-api` | `#![no_std]` C-ABI plugin interface |
|
| `quicprochat-plugin-api` | `#![no_std]` C-ABI plugin interface |
|
||||||
| `quicproquo-rpc` | QUIC RPC framework: framing, server dispatch, client, middleware |
|
| `quicprochat-rpc` | QUIC RPC framework: framing, server dispatch, client, middleware |
|
||||||
| `quicproquo-sdk` | Client SDK: `QpqClient`, event broadcast, `ConversationStore` |
|
| `quicprochat-sdk` | Client SDK: `QpqClient`, event broadcast, `ConversationStore` |
|
||||||
| `quicproquo-server` | RPC server + domain services |
|
| `quicprochat-server` | RPC server + domain services |
|
||||||
| `quicproquo-client` | CLI/TUI client binary |
|
| `quicprochat-client` | CLI/TUI client binary |
|
||||||
| `quicproquo-p2p` | iroh P2P endpoint publish/resolve (feature-flagged) |
|
| `quicprochat-p2p` | iroh P2P endpoint publish/resolve (feature-flagged) |
|
||||||
|
|
||||||
See [Crate Responsibilities](crate-responsibilities.md) for a full breakdown
|
See [Crate Responsibilities](crate-responsibilities.md) for a full breakdown
|
||||||
and dependency diagram.
|
and dependency diagram.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Protocol Stack
|
# Protocol Stack
|
||||||
|
|
||||||
quicproquo layers three protocol stages to move a plaintext message from
|
quicprochat layers three protocol stages to move a plaintext message from
|
||||||
sender to recipient with end-to-end encryption, typed RPC framing, and
|
sender to recipient with end-to-end encryption, typed RPC framing, and
|
||||||
authenticated transport. This page describes each layer and provides a
|
authenticated transport. This page describes each layer and provides a
|
||||||
comparison table.
|
comparison table.
|
||||||
@@ -32,12 +32,12 @@ one per RPC call.
|
|||||||
- TLS 1.3 provides perfect forward secrecy per connection via ephemeral ECDHE.
|
- TLS 1.3 provides perfect forward secrecy per connection via ephemeral ECDHE.
|
||||||
- The server presents a self-signed certificate by default; the client pins
|
- The server presents a self-signed certificate by default; the client pins
|
||||||
the server certificate via `--ca-cert`.
|
the server certificate via `--ca-cert`.
|
||||||
- ALPN protocol identifier: `qpq`.
|
- ALPN protocol identifier: `qpc`.
|
||||||
- Multiplexed streams over a single UDP socket -- one bidirectional stream
|
- Multiplexed streams over a single UDP socket -- one bidirectional stream
|
||||||
per RPC call, preventing head-of-line blocking.
|
per RPC call, preventing head-of-line blocking.
|
||||||
- Uni-directional streams for server-to-client push events.
|
- Uni-directional streams for server-to-client push events.
|
||||||
|
|
||||||
**Protobuf framing** (`quicproquo-rpc`, `quicproquo-proto`)
|
**Protobuf framing** (`quicprochat-rpc`, `quicprochat-proto`)
|
||||||
|
|
||||||
- Three frame types: Request, Response, Push.
|
- Three frame types: Request, Response, Push.
|
||||||
- Fixed-length binary headers carry method/status codes, request correlation
|
- Fixed-length binary headers carry method/status codes, request correlation
|
||||||
@@ -66,7 +66,7 @@ one per RPC call.
|
|||||||
| Layer | Provides | Crate(s) |
|
| Layer | Provides | Crate(s) |
|
||||||
|-------------|------------------------------------------------------------------|-----------------------------------------|
|
|-------------|------------------------------------------------------------------|-----------------------------------------|
|
||||||
| **Transport: QUIC + TLS 1.3** | Confidentiality, server authentication, forward secrecy, multiplexed streams, congestion control | `quinn`, `rustls` |
|
| **Transport: QUIC + TLS 1.3** | Confidentiality, server authentication, forward secrecy, multiplexed streams, congestion control | `quinn`, `rustls` |
|
||||||
| **Framing: Protobuf** | Typed serialisation, length-prefixed framing, method dispatch, push events | `quicproquo-rpc`, `prost` |
|
| **Framing: Protobuf** | Typed serialisation, length-prefixed framing, method dispatch, push events | `quicprochat-rpc`, `prost` |
|
||||||
| **Encryption: MLS** | Group key agreement, forward secrecy, post-compromise security, identity binding | `openmls`, `openmls_rust_crypto` |
|
| **Encryption: MLS** | Group key agreement, forward secrecy, post-compromise security, identity binding | `openmls`, `openmls_rust_crypto` |
|
||||||
| **Encryption: Hybrid KEM** (optional) | Post-quantum confidentiality for individual payloads (X25519 + ML-KEM-768) | `ml-kem`, `x25519-dalek`, `chacha20poly1305`, `hkdf` |
|
| **Encryption: Hybrid KEM** (optional) | Post-quantum confidentiality for individual payloads (X25519 + ML-KEM-768) | `ml-kem`, `x25519-dalek`, `chacha20poly1305`, `hkdf` |
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Service Architecture
|
# Service Architecture
|
||||||
|
|
||||||
The quicproquo server exposes 44 RPC methods through a single QUIC + TLS 1.3
|
The quicprochat server exposes 44 RPC methods through a single QUIC + TLS 1.3
|
||||||
endpoint on **port 5001**. Methods are dispatched by numeric method ID using
|
endpoint on **port 5001**. Methods are dispatched by numeric method ID using
|
||||||
the v2 Protobuf framing protocol. This page documents the method reference,
|
the v2 Protobuf framing protocol. This page documents the method reference,
|
||||||
connection lifecycle, storage model, and authentication flow.
|
connection lifecycle, storage model, and authentication flow.
|
||||||
@@ -10,11 +10,11 @@ connection lifecycle, storage model, and authentication flow.
|
|||||||
## RPC Endpoint
|
## RPC Endpoint
|
||||||
|
|
||||||
A single QUIC + TLS 1.3 listener on **port 5001** serves all operations.
|
A single QUIC + TLS 1.3 listener on **port 5001** serves all operations.
|
||||||
The ALPN identifier is `qpq`. Each RPC call uses a dedicated QUIC
|
The ALPN identifier is `qpc`. Each RPC call uses a dedicated QUIC
|
||||||
bidirectional stream; calls are concurrent and do not block each other.
|
bidirectional stream; calls are concurrent and do not block each other.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
quicproquo-server (port 5001, ALPN: "qpq")
|
quicprochat-server (port 5001, ALPN: "qpc")
|
||||||
|
|
|
|
||||||
+-- Auth (100-103)
|
+-- Auth (100-103)
|
||||||
| +-- 100: OpaqueRegisterStart
|
| +-- 100: OpaqueRegisterStart
|
||||||
@@ -243,7 +243,7 @@ Client Server
|
|||||||
2. <- QUIC HANDSHAKE
|
2. <- QUIC HANDSHAKE
|
||||||
TLS 1.3 ServerHello +
|
TLS 1.3 ServerHello +
|
||||||
Certificate (self-signed)
|
Certificate (self-signed)
|
||||||
ALPN: "qpq"
|
ALPN: "qpc"
|
||||||
|
|
||||||
3. Client verifies server cert against
|
3. Client verifies server cert against
|
||||||
pinned CA cert (--ca-cert flag)
|
pinned CA cert (--ca-cert flag)
|
||||||
@@ -345,10 +345,10 @@ validates it on every authenticated method call.
|
|||||||
|
|
||||||
| Flag | Env var | Default | Description |
|
| Flag | Env var | Default | Description |
|
||||||
|----------------|----------------------------|------------------------|-------------|
|
|----------------|----------------------------|------------------------|-------------|
|
||||||
| `--listen` | `QPQ_LISTEN` | `0.0.0.0:5001` | QUIC listen address (host:port). |
|
| `--listen` | `QPC_LISTEN` | `0.0.0.0:5001` | QUIC listen address (host:port). |
|
||||||
| `--data-dir` | `QPQ_DATA_DIR` | `data` | Directory for persisted state. |
|
| `--data-dir` | `QPC_DATA_DIR` | `data` | Directory for persisted state. |
|
||||||
| `--tls-cert` | `QPQ_TLS_CERT` | `data/server-cert.der` | Path to TLS certificate (DER). Auto-generated if missing. |
|
| `--tls-cert` | `QPC_TLS_CERT` | `data/server-cert.der` | Path to TLS certificate (DER). Auto-generated if missing. |
|
||||||
| `--tls-key` | `QPQ_TLS_KEY` | `data/server-key.der` | Path to TLS private key (DER). Auto-generated if missing. |
|
| `--tls-key` | `QPC_TLS_KEY` | `data/server-key.der` | Path to TLS private key (DER). Auto-generated if missing. |
|
||||||
|
|
||||||
Logging level is controlled by the `RUST_LOG` environment variable (default: `info`).
|
Logging level is controlled by the `RUST_LOG` environment variable (default: `info`).
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Coding Standards
|
# Coding Standards
|
||||||
|
|
||||||
This page defines the engineering standards for quicproquo. These are
|
This page defines the engineering standards for quicprochat. These are
|
||||||
non-negotiable -- all code merged into the repository must conform to these
|
non-negotiable -- all code merged into the repository must conform to these
|
||||||
rules. The standards exist to ensure that every milestone produces
|
rules. The standards exist to ensure that every milestone produces
|
||||||
production-ready, auditable, and secure code.
|
production-ready, auditable, and secure code.
|
||||||
@@ -85,9 +85,9 @@ pub fn create_group(
|
|||||||
|
|
||||||
- No `unwrap()` or `expect()` on cryptographic operations or I/O in non-test
|
- No `unwrap()` or `expect()` on cryptographic operations or I/O in non-test
|
||||||
paths. All crypto errors must be typed and propagated.
|
paths. All crypto errors must be typed and propagated.
|
||||||
- Use `thiserror` for library error types (`quicproquo-core`,
|
- Use `thiserror` for library error types (`quicprochat-core`,
|
||||||
`quicproquo-proto`, `quicproquo-rpc`, `quicproquo-sdk`) and `anyhow` for
|
`quicprochat-proto`, `quicprochat-rpc`, `quicprochat-sdk`) and `anyhow` for
|
||||||
application-level error handling (`quicproquo-server`, `quicproquo-client`).
|
application-level error handling (`quicprochat-server`, `quicprochat-client`).
|
||||||
- `unwrap()` is acceptable only in:
|
- `unwrap()` is acceptable only in:
|
||||||
- Test code.
|
- Test code.
|
||||||
- Cases where the invariant is provably guaranteed by the type system
|
- Cases where the invariant is provably guaranteed by the type system
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Testing Strategy
|
# Testing Strategy
|
||||||
|
|
||||||
This page describes the testing structure, conventions, and current coverage for
|
This page describes the testing structure, conventions, and current coverage for
|
||||||
quicproquo. All tests run with `cargo test --workspace` (or `just test`) and
|
quicprochat. All tests run with `cargo test --workspace` (or `just test`) and
|
||||||
must pass before any code is merged.
|
must pass before any code is merged.
|
||||||
|
|
||||||
For the coding standards that tests must follow, see
|
For the coding standards that tests must follow, see
|
||||||
@@ -17,7 +17,7 @@ Unit tests live alongside the code they test, in `#[cfg(test)] mod tests` blocks
|
|||||||
at the bottom of each source file. They test individual functions and types in
|
at the bottom of each source file. They test individual functions and types in
|
||||||
isolation.
|
isolation.
|
||||||
|
|
||||||
**quicproquo-core (96 tests):**
|
**quicprochat-core (96 tests):**
|
||||||
|
|
||||||
| Module | Tests | What they cover |
|
| Module | Tests | What they cover |
|
||||||
|--------|-------|----------------|
|
|--------|-------|----------------|
|
||||||
@@ -28,21 +28,21 @@ isolation.
|
|||||||
| `opaque_auth` | 12 | OPAQUE registration + login full flow, bad password rejection |
|
| `opaque_auth` | 12 | OPAQUE registration + login full flow, bad password rejection |
|
||||||
| `mls_*` | 61 | MLS key schedule, member add/remove, Welcome processing, key exhaustion |
|
| `mls_*` | 61 | MLS key schedule, member add/remove, Welcome processing, key exhaustion |
|
||||||
|
|
||||||
**quicproquo-rpc (18 tests):**
|
**quicprochat-rpc (18 tests):**
|
||||||
|
|
||||||
| Module | Tests | What they cover |
|
| Module | Tests | What they cover |
|
||||||
|--------|-------|----------------|
|
|--------|-------|----------------|
|
||||||
| `framing` | 8 | Wire framing round-trips, method ID encoding, length-prefix correctness |
|
| `framing` | 8 | Wire framing round-trips, method ID encoding, length-prefix correctness |
|
||||||
| `dispatch` | 10 | Handler dispatch, method not found, middleware chain, timeout enforcement |
|
| `dispatch` | 10 | Handler dispatch, method not found, middleware chain, timeout enforcement |
|
||||||
|
|
||||||
**quicproquo-sdk (30 tests):**
|
**quicprochat-sdk (30 tests):**
|
||||||
|
|
||||||
| Module | Tests | What they cover |
|
| Module | Tests | What they cover |
|
||||||
|--------|-------|----------------|
|
|--------|-------|----------------|
|
||||||
| `client` | 15 | `QpqClient` connect, send, receive, event broadcast |
|
| `client` | 15 | `QpqClient` connect, send, receive, event broadcast |
|
||||||
| `conversation_store` | 15 | `ConversationStore` CRUD, pagination, message ordering |
|
| `conversation_store` | 15 | `ConversationStore` CRUD, pagination, message ordering |
|
||||||
|
|
||||||
**quicproquo-server (65 tests):**
|
**quicprochat-server (65 tests):**
|
||||||
|
|
||||||
| Module | Tests | What they cover |
|
| Module | Tests | What they cover |
|
||||||
|--------|-------|----------------|
|
|--------|-------|----------------|
|
||||||
@@ -51,13 +51,13 @@ isolation.
|
|||||||
| `storage` | 15 | `FileBackedStore` and `SqlStore` CRUD, MLS entity serialisation |
|
| `storage` | 15 | `FileBackedStore` and `SqlStore` CRUD, MLS entity serialisation |
|
||||||
| `federation` | 10 | Federation peer relay, mTLS validation, domain routing |
|
| `federation` | 10 | Federation peer relay, mTLS validation, domain routing |
|
||||||
|
|
||||||
**quicproquo-kt (21 tests):**
|
**quicprochat-kt (21 tests):**
|
||||||
|
|
||||||
| Module | Tests | What they cover |
|
| Module | Tests | What they cover |
|
||||||
|--------|-------|----------------|
|
|--------|-------|----------------|
|
||||||
| `merkle_log` | 21 | Merkle tree insertion, consistency proofs, root hash correctness |
|
| `merkle_log` | 21 | Merkle tree insertion, consistency proofs, root hash correctness |
|
||||||
|
|
||||||
**quicproquo-p2p (34 tests):**
|
**quicprochat-p2p (34 tests):**
|
||||||
|
|
||||||
| Module | Tests | What they cover |
|
| Module | Tests | What they cover |
|
||||||
|--------|-------|----------------|
|
|--------|-------|----------------|
|
||||||
@@ -65,18 +65,18 @@ isolation.
|
|||||||
|
|
||||||
### Integration and E2E Tests
|
### Integration and E2E Tests
|
||||||
|
|
||||||
E2E tests live in `crates/quicproquo-client/tests/e2e.rs` (20 tests) and
|
E2E tests live in `crates/quicprochat-client/tests/e2e.rs` (20 tests) and
|
||||||
exercise the full client-server stack in-process. Each test spawns a real server
|
exercise the full client-server stack in-process. Each test spawns a real server
|
||||||
using `tokio::spawn`, runs client operations against it, and asserts on the
|
using `tokio::spawn`, runs client operations against it, and asserts on the
|
||||||
results.
|
results.
|
||||||
|
|
||||||
**quicproquo-client unit (16 tests):**
|
**quicprochat-client unit (16 tests):**
|
||||||
|
|
||||||
| File | What it covers |
|
| File | What it covers |
|
||||||
|------|---------------|
|
|------|---------------|
|
||||||
| `src/lib.rs` | CLI command parsing, client state machine, error formatting |
|
| `src/lib.rs` | CLI command parsing, client state machine, error formatting |
|
||||||
|
|
||||||
**quicproquo-client E2E (20 tests):**
|
**quicprochat-client E2E (20 tests):**
|
||||||
|
|
||||||
| Test | What it covers |
|
| Test | What it covers |
|
||||||
|------|---------------|
|
|------|---------------|
|
||||||
@@ -141,7 +141,7 @@ The E2E test suite shares an `AUTH_LOCK` `tokio::Mutex` to prevent port binding
|
|||||||
conflicts when tests run in parallel. Always run E2E tests with a single thread:
|
conflicts when tests run in parallel. Always run E2E tests with a single thread:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo test -p quicproquo-client --test e2e -- --test-threads 1
|
cargo test -p quicprochat-client --test e2e -- --test-threads 1
|
||||||
```
|
```
|
||||||
|
|
||||||
Running without `--test-threads 1` may cause intermittent bind errors if two
|
Running without `--test-threads 1` may cause intermittent bind errors if two
|
||||||
@@ -150,19 +150,19 @@ tests try to use the same port concurrently.
|
|||||||
### Single Crate
|
### Single Crate
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo test -p quicproquo-core
|
cargo test -p quicprochat-core
|
||||||
cargo test -p quicproquo-rpc
|
cargo test -p quicprochat-rpc
|
||||||
cargo test -p quicproquo-sdk
|
cargo test -p quicprochat-sdk
|
||||||
cargo test -p quicproquo-server
|
cargo test -p quicprochat-server
|
||||||
cargo test -p quicproquo-kt
|
cargo test -p quicprochat-kt
|
||||||
cargo test -p quicproquo-p2p
|
cargo test -p quicprochat-p2p
|
||||||
```
|
```
|
||||||
|
|
||||||
### Single Test
|
### Single Test
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo test -p quicproquo-core -- codec::tests::test_round_trip
|
cargo test -p quicprochat-core -- codec::tests::test_round_trip
|
||||||
cargo test -p quicproquo-client --test e2e -- opaque_flow --test-threads 1
|
cargo test -p quicprochat-client --test e2e -- opaque_flow --test-threads 1
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Output
|
### With Output
|
||||||
@@ -179,13 +179,13 @@ All 301 tests pass on branch `v2`.
|
|||||||
|
|
||||||
| Crate | Unit / Integration Tests | E2E Tests | Total |
|
| Crate | Unit / Integration Tests | E2E Tests | Total |
|
||||||
|-------|--------------------------|-----------|-------|
|
|-------|--------------------------|-----------|-------|
|
||||||
| `quicproquo-core` | 96 | -- | 96 |
|
| `quicprochat-core` | 96 | -- | 96 |
|
||||||
| `quicproquo-rpc` | 18 | -- | 18 |
|
| `quicprochat-rpc` | 18 | -- | 18 |
|
||||||
| `quicproquo-sdk` | 30 | -- | 30 |
|
| `quicprochat-sdk` | 30 | -- | 30 |
|
||||||
| `quicproquo-server` | 65 | -- | 65 |
|
| `quicprochat-server` | 65 | -- | 65 |
|
||||||
| `quicproquo-kt` | 21 | -- | 21 |
|
| `quicprochat-kt` | 21 | -- | 21 |
|
||||||
| `quicproquo-p2p` | 34 | -- | 34 |
|
| `quicprochat-p2p` | 34 | -- | 34 |
|
||||||
| `quicproquo-client` | 16 unit + 1 doctest | 20 | 37 |
|
| `quicprochat-client` | 16 unit + 1 doctest | 20 | 37 |
|
||||||
| **Total** | **281** | **20** | **301** |
|
| **Total** | **281** | **20** | **301** |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ compromised, past session keys cannot be recovered.** In other words, an
|
|||||||
attacker who obtains today's long-term key cannot use it to decrypt messages
|
attacker who obtains today's long-term key cannot use it to decrypt messages
|
||||||
recorded yesterday.
|
recorded yesterday.
|
||||||
|
|
||||||
quicproquo provides forward secrecy at two independent layers: the transport
|
quicprochat provides forward secrecy at two independent layers: the transport
|
||||||
layer and the application layer. Even if one layer's FS mechanism is defeated,
|
layer and the application layer. Even if one layer's FS mechanism is defeated,
|
||||||
the other continues to protect message confidentiality.
|
the other continues to protect message confidentiality.
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ In each TLS 1.3 handshake:
|
|||||||
|
|
||||||
Because the ephemeral keys exist only for the duration of the handshake,
|
Because the ephemeral keys exist only for the duration of the handshake,
|
||||||
compromising the server's long-term TLS certificate key (currently self-signed
|
compromising the server's long-term TLS certificate key (currently self-signed
|
||||||
in quicproquo) does not reveal past session keys.
|
in quicprochat) does not reveal past session keys.
|
||||||
|
|
||||||
## Application Layer Forward Secrecy
|
## Application Layer Forward Secrecy
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ This deletion is the mechanism that provides forward secrecy: once old epoch
|
|||||||
keys are erased, messages encrypted under those keys cannot be decrypted, even
|
keys are erased, messages encrypted under those keys cannot be decrypted, even
|
||||||
if the current group state is compromised.
|
if the current group state is compromised.
|
||||||
|
|
||||||
In quicproquo, epoch advancement occurs when:
|
In quicprochat, epoch advancement occurs when:
|
||||||
|
|
||||||
- `add_member()` is called, which creates a Commit and calls
|
- `add_member()` is called, which creates a Commit and calls
|
||||||
`merge_pending_commit()`.
|
`merge_pending_commit()`.
|
||||||
@@ -91,7 +91,7 @@ HPKE init keys.
|
|||||||
|
|
||||||
## Layered Forward Secrecy
|
## Layered Forward Secrecy
|
||||||
|
|
||||||
A distinctive property of quicproquo's design is that forward secrecy
|
A distinctive property of quicprochat's design is that forward secrecy
|
||||||
operates at two independent layers:
|
operates at two independent layers:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -135,7 +135,7 @@ unless they also break the transport encryption.
|
|||||||
Signal's Double Ratchet protocol also provides forward secrecy, but the
|
Signal's Double Ratchet protocol also provides forward secrecy, but the
|
||||||
mechanisms differ:
|
mechanisms differ:
|
||||||
|
|
||||||
| Property | Signal Double Ratchet | MLS (quicproquo) |
|
| Property | Signal Double Ratchet | MLS (quicprochat) |
|
||||||
|----------|----------------------|---------------------|
|
|----------|----------------------|---------------------|
|
||||||
| Scope | Pairwise (1:1 sessions) | Group (n-party) |
|
| Scope | Pairwise (1:1 sessions) | Group (n-party) |
|
||||||
| Ratchet granularity | Per message (symmetric ratchet) + per DH round (DH ratchet) | Per epoch (Commit) |
|
| Ratchet granularity | Per message (symmetric ratchet) + per DH round (DH ratchet) | Per epoch (Commit) |
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Ed25519 Identity Keys
|
# Ed25519 Identity Keys
|
||||||
|
|
||||||
The Ed25519 identity keypair is the long-term cryptographic identity of a
|
The Ed25519 identity keypair is the long-term cryptographic identity of a
|
||||||
quicproquo client. It is generated once, persisted across sessions, and used
|
quicprochat client. It is generated once, persisted across sessions, and used
|
||||||
for MLS credential signing, Authentication Service registration, and delivery
|
for MLS credential signing, Authentication Service registration, and delivery
|
||||||
queue addressing.
|
queue addressing.
|
||||||
|
|
||||||
**Source:** `crates/quicproquo-core/src/identity.rs`
|
**Source:** `crates/quicprochat-core/src/identity.rs`
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ A fresh identity keypair is generated from the OS CSPRNG (`OsRng`) via
|
|||||||
`ed25519-dalek`:
|
`ed25519-dalek`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use quicproquo_core::identity::IdentityKeypair;
|
use quicprochat_core::identity::IdentityKeypair;
|
||||||
|
|
||||||
let identity = IdentityKeypair::generate();
|
let identity = IdentityKeypair::generate();
|
||||||
// The signing key seed is generated from OsRng (getrandom on Linux).
|
// The signing key seed is generated from OsRng (getrandom on Linux).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Key Lifecycle and Zeroization
|
# Key Lifecycle and Zeroization
|
||||||
|
|
||||||
quicproquo uses multiple key types with different lifetimes, creation
|
quicprochat uses multiple key types with different lifetimes, creation
|
||||||
patterns, and destruction guarantees. This page provides a comprehensive
|
patterns, and destruction guarantees. This page provides a comprehensive
|
||||||
lifecycle diagram for every key type in the system, from generation through
|
lifecycle diagram for every key type in the system, from generation through
|
||||||
zeroization.
|
zeroization.
|
||||||
@@ -25,7 +25,7 @@ Hybrid KEM Keys Per peer (future) Public portion X25519+ML-KEM-768
|
|||||||
|
|
||||||
## Ed25519 Identity Key
|
## Ed25519 Identity Key
|
||||||
|
|
||||||
**Source:** `crates/quicproquo-core/src/identity.rs`
|
**Source:** `crates/quicprochat-core/src/identity.rs`
|
||||||
|
|
||||||
The Ed25519 identity key is the most long-lived secret in the system. It
|
The Ed25519 identity key is the most long-lived secret in the system. It
|
||||||
represents the client's cryptographic identity across all sessions and groups.
|
represents the client's cryptographic identity across all sessions and groups.
|
||||||
@@ -92,8 +92,8 @@ zeroization.
|
|||||||
|
|
||||||
## HPKE Init Keys
|
## HPKE Init Keys
|
||||||
|
|
||||||
**Source:** `crates/quicproquo-core/src/keystore.rs` and
|
**Source:** `crates/quicprochat-core/src/keystore.rs` and
|
||||||
`crates/quicproquo-core/src/group.rs`
|
`crates/quicprochat-core/src/group.rs`
|
||||||
|
|
||||||
HPKE init keys are generated by the openmls backend as part of MLS KeyPackage
|
HPKE init keys are generated by the openmls backend as part of MLS KeyPackage
|
||||||
creation. They are single-use: each init key is consumed exactly once when
|
creation. They are single-use: each init key is consumed exactly once when
|
||||||
@@ -167,7 +167,7 @@ processing a Welcome message.
|
|||||||
**Managed by:** `openmls` (internal to the `MlsGroup` state machine)
|
**Managed by:** `openmls` (internal to the `MlsGroup` state machine)
|
||||||
|
|
||||||
MLS epoch keys are derived internally by the openmls ratchet tree. They are not
|
MLS epoch keys are derived internally by the openmls ratchet tree. They are not
|
||||||
directly accessible in quicproquo code but are critical to understanding the
|
directly accessible in quicprochat code but are critical to understanding the
|
||||||
system's security properties.
|
system's security properties.
|
||||||
|
|
||||||
### Lifecycle
|
### Lifecycle
|
||||||
@@ -216,12 +216,12 @@ system's security properties.
|
|||||||
- **Deletion:** Old epoch keys are deleted after the Commit is processed. This
|
- **Deletion:** Old epoch keys are deleted after the Commit is processed. This
|
||||||
deletion is what provides [forward secrecy](forward-secrecy.md) at the MLS
|
deletion is what provides [forward secrecy](forward-secrecy.md) at the MLS
|
||||||
layer.
|
layer.
|
||||||
- **No direct access:** quicproquo code interacts with epoch keys only
|
- **No direct access:** quicprochat code interacts with epoch keys only
|
||||||
indirectly through `send_message()` and `receive_message()`.
|
indirectly through `send_message()` and `receive_message()`.
|
||||||
|
|
||||||
## Hybrid KEM Keys (Future -- M5+)
|
## Hybrid KEM Keys (Future -- M5+)
|
||||||
|
|
||||||
**Source:** `crates/quicproquo-core/src/hybrid_kem.rs`
|
**Source:** `crates/quicprochat-core/src/hybrid_kem.rs`
|
||||||
|
|
||||||
The hybrid KEM keypair combines X25519 (classical) with ML-KEM-768
|
The hybrid KEM keypair combines X25519 (classical) with ML-KEM-768
|
||||||
(post-quantum) for content encryption that resists both classical and quantum
|
(post-quantum) for content encryption that resists both classical and quantum
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Cryptography Overview
|
# Cryptography Overview
|
||||||
|
|
||||||
quicproquo layers multiple cryptographic protocols to provide confidentiality,
|
quicprochat layers multiple cryptographic protocols to provide confidentiality,
|
||||||
integrity, authentication, forward secrecy, and post-compromise security. This
|
integrity, authentication, forward secrecy, and post-compromise security. This
|
||||||
page catalogues every algorithm in the system, the crate that supplies it, and
|
page catalogues every algorithm in the system, the crate that supplies it, and
|
||||||
the security margin it provides.
|
the security margin it provides.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ PCS is the complement of [forward secrecy](forward-secrecy.md):
|
|||||||
- **Post-compromise security** protects the **future** from a past compromise.
|
- **Post-compromise security** protects the **future** from a past compromise.
|
||||||
|
|
||||||
MLS (RFC 9420) is specifically designed to provide both properties simultaneously
|
MLS (RFC 9420) is specifically designed to provide both properties simultaneously
|
||||||
for group messaging. This is a key differentiator of quicproquo's design.
|
for group messaging. This is a key differentiator of quicprochat's design.
|
||||||
|
|
||||||
## How MLS Provides PCS
|
## How MLS Provides PCS
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ This means:
|
|||||||
For a group of 1,000 members, the path length is approximately 10 nodes --
|
For a group of 1,000 members, the path length is approximately 10 nodes --
|
||||||
making PCS practical even for large groups.
|
making PCS practical even for large groups.
|
||||||
|
|
||||||
## Epoch Advancement in quicproquo
|
## Epoch Advancement in quicprochat
|
||||||
|
|
||||||
In the current implementation, epoch advancement occurs through the `GroupMember`
|
In the current implementation, epoch advancement occurs through the `GroupMember`
|
||||||
methods in `group.rs`:
|
methods in `group.rs`:
|
||||||
@@ -145,7 +145,7 @@ deleted), and future epochs are protected by PCS (new key material generated).
|
|||||||
|
|
||||||
Signal's group messaging uses **Sender Keys**, a fundamentally different
|
Signal's group messaging uses **Sender Keys**, a fundamentally different
|
||||||
mechanism from MLS's ratchet tree. The comparison is instructive because it
|
mechanism from MLS's ratchet tree. The comparison is instructive because it
|
||||||
highlights why MLS was chosen for quicproquo:
|
highlights why MLS was chosen for quicprochat:
|
||||||
|
|
||||||
### Signal Sender Keys
|
### Signal Sender Keys
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ security. If an attacker compromises a member's Sender Key:
|
|||||||
membership changes.
|
membership changes.
|
||||||
- There is no automatic healing mechanism analogous to MLS's ratchet tree.
|
- There is no automatic healing mechanism analogous to MLS's ratchet tree.
|
||||||
|
|
||||||
### MLS Ratchet Tree (quicproquo)
|
### MLS Ratchet Tree (quicprochat)
|
||||||
|
|
||||||
In contrast, MLS's ratchet tree provides PCS because:
|
In contrast, MLS's ratchet tree provides PCS because:
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ periodic Updates (planned) will bound the healing window.
|
|||||||
|
|
||||||
### Server compromise does not prevent PCS
|
### Server compromise does not prevent PCS
|
||||||
|
|
||||||
The quicproquo server is MLS-unaware -- it stores and forwards encrypted
|
The quicprochat server is MLS-unaware -- it stores and forwards encrypted
|
||||||
MLS messages without access to the group state. A compromised server cannot:
|
MLS messages without access to the group state. A compromised server cannot:
|
||||||
|
|
||||||
- Prevent PCS by blocking Commits (it could perform denial-of-service, but
|
- Prevent PCS by blocking Commits (it could perform denial-of-service, but
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
# Post-Quantum Readiness
|
# Post-Quantum Readiness
|
||||||
|
|
||||||
quicproquo includes a fully implemented and tested hybrid key encapsulation
|
quicprochat includes a fully implemented and tested hybrid key encapsulation
|
||||||
mechanism (KEM) combining X25519 (classical) with ML-KEM-768 (post-quantum).
|
mechanism (KEM) combining X25519 (classical) with ML-KEM-768 (post-quantum).
|
||||||
This page describes the current implementation, the integration plan, the
|
This page describes the current implementation, the integration plan, the
|
||||||
security rationale, and the known gaps.
|
security rationale, and the known gaps.
|
||||||
|
|
||||||
**Source:** `crates/quicproquo-core/src/hybrid_kem.rs`
|
**Source:** `crates/quicprochat-core/src/hybrid_kem.rs`
|
||||||
|
|
||||||
## Current State
|
## Current State
|
||||||
|
|
||||||
The hybrid KEM is **fully implemented and tested** in `quicproquo-core`. The
|
The hybrid KEM is **fully implemented and tested** in `quicprochat-core`. The
|
||||||
implementation provides:
|
implementation provides:
|
||||||
|
|
||||||
- `HybridKeypair::generate()` -- generate a combined X25519 + ML-KEM-768 keypair
|
- `HybridKeypair::generate()` -- generate a combined X25519 + ML-KEM-768 keypair
|
||||||
@@ -35,7 +35,7 @@ The test suite in `hybrid_kem.rs` includes 10 tests covering:
|
|||||||
## ML-KEM-768 (FIPS 203)
|
## ML-KEM-768 (FIPS 203)
|
||||||
|
|
||||||
ML-KEM (Module-Lattice-Based Key Encapsulation Mechanism) is the NIST-standardized
|
ML-KEM (Module-Lattice-Based Key Encapsulation Mechanism) is the NIST-standardized
|
||||||
post-quantum KEM, published as FIPS 203. quicproquo uses ML-KEM-768, the
|
post-quantum KEM, published as FIPS 203. quicprochat uses ML-KEM-768, the
|
||||||
middle parameter set:
|
middle parameter set:
|
||||||
|
|
||||||
| Parameter Set | NIST Level | Security (PQ) | EK Size | CT Size | SS Size |
|
| Parameter Set | NIST Level | Security (PQ) | EK Size | CT Size | SS Size |
|
||||||
@@ -69,8 +69,8 @@ executed, and their shared secrets are combined through a KDF:
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
ikm = X25519_shared_secret(32 bytes) || ML-KEM_shared_secret(32 bytes)
|
ikm = X25519_shared_secret(32 bytes) || ML-KEM_shared_secret(32 bytes)
|
||||||
key = HKDF-SHA256(salt=[], ikm, info="quicproquo-hybrid-v1", L=32)
|
key = HKDF-SHA256(salt=[], ikm, info="quicprochat-hybrid-v1", L=32)
|
||||||
nonce = HKDF-SHA256(salt=[], ikm, info="quicproquo-hybrid-nonce-v1", L=12)
|
nonce = HKDF-SHA256(salt=[], ikm, info="quicprochat-hybrid-nonce-v1", L=12)
|
||||||
```
|
```
|
||||||
|
|
||||||
The combined IKM (input key material) is wrapped in `Zeroizing<Vec<u8>>` and
|
The combined IKM (input key material) is wrapped in `Zeroizing<Vec<u8>>` and
|
||||||
@@ -165,7 +165,7 @@ hybrid KEM for HPKE init key exchange:
|
|||||||
|
|
||||||
## The PQ Gap
|
## The PQ Gap
|
||||||
|
|
||||||
There is an important asymmetry in quicproquo's post-quantum protection:
|
There is an important asymmetry in quicprochat's post-quantum protection:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Layer Classical Protection Post-Quantum Protection
|
Layer Classical Protection Post-Quantum Protection
|
||||||
@@ -191,7 +191,7 @@ This is the **PQ gap**: content is safe, but metadata is not.
|
|||||||
|
|
||||||
Post-quantum TLS (via ML-KEM in the TLS 1.3 handshake) is being standardized by
|
Post-quantum TLS (via ML-KEM in the TLS 1.3 handshake) is being standardized by
|
||||||
the IETF and is supported by some TLS libraries, but `rustls` does not yet
|
the IETF and is supported by some TLS libraries, but `rustls` does not yet
|
||||||
support it in a stable release. When `rustls` adds ML-KEM support, quicproquo
|
support it in a stable release. When `rustls` adds ML-KEM support, quicprochat
|
||||||
will adopt it to close the PQ gap at the transport layer.
|
will adopt it to close the PQ gap at the transport layer.
|
||||||
|
|
||||||
## Harvest-Now, Decrypt-Later Risk
|
## Harvest-Now, Decrypt-Later Risk
|
||||||
@@ -202,7 +202,7 @@ The "harvest-now, decrypt-later" (HNDL) threat model assumes an adversary who:
|
|||||||
2. Waits for a sufficiently powerful quantum computer (years or decades).
|
2. Waits for a sufficiently powerful quantum computer (years or decades).
|
||||||
3. Decrypts the recorded traffic retroactively.
|
3. Decrypts the recorded traffic retroactively.
|
||||||
|
|
||||||
In quicproquo's case:
|
In quicprochat's case:
|
||||||
|
|
||||||
- **Content is safe from M5 onward.** The hybrid KEM wrapping MLS content uses
|
- **Content is safe from M5 onward.** The hybrid KEM wrapping MLS content uses
|
||||||
ML-KEM-768, which resists quantum attacks. Even if the recorded traffic is
|
ML-KEM-768, which resists quantum attacks. Even if the recorded traffic is
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Threat Model
|
# Threat Model
|
||||||
|
|
||||||
This page defines the attacker models quicproquo is designed to resist,
|
This page defines the attacker models quicprochat is designed to resist,
|
||||||
catalogues what is and is not protected, identifies known gaps in the current
|
catalogues what is and is not protected, identifies known gaps in the current
|
||||||
implementation, and outlines future mitigations.
|
implementation, and outlines future mitigations.
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ state-level adversary).
|
|||||||
**What they can do:**
|
**What they can do:**
|
||||||
|
|
||||||
- Attempt TLS 1.3 MITM: TLS 1.3 prevents this if the client validates the
|
- Attempt TLS 1.3 MITM: TLS 1.3 prevents this if the client validates the
|
||||||
server's certificate. However, quicproquo currently uses **self-signed
|
server's certificate. However, quicprochat currently uses **self-signed
|
||||||
certificates**, which means the client has no CA chain to verify. On the first
|
certificates**, which means the client has no CA chain to verify. On the first
|
||||||
connection, a MITM could present their own certificate and intercept the
|
connection, a MITM could present their own certificate and intercept the
|
||||||
session (trust-on-first-use vulnerability).
|
session (trust-on-first-use vulnerability).
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
quicproquo needs an efficient, typed wire format for client-server communication. The format must support:
|
quicprochat needs an efficient, typed wire format for client-server communication. The format must support:
|
||||||
|
|
||||||
1. **Typed messages** with compile-time schema enforcement to eliminate hand-rolled serialisation bugs.
|
1. **Typed messages** with compile-time schema enforcement to eliminate hand-rolled serialisation bugs.
|
||||||
2. **Schema evolution** so that new fields and methods can be added without breaking existing clients.
|
2. **Schema evolution** so that new fields and methods can be added without breaking existing clients.
|
||||||
@@ -105,7 +105,7 @@ The Cap'n Proto schemas are stored in the `schemas/` directory:
|
|||||||
|
|
||||||
### Costs and trade-offs
|
### Costs and trade-offs
|
||||||
|
|
||||||
- **Build-time code generation.** The `capnpc` compiler must run during the build (via `build.rs` in `quicproquo-proto`). This adds a build dependency and increases compile times slightly.
|
- **Build-time code generation.** The `capnpc` compiler must run during the build (via `build.rs` in `quicprochat-proto`). This adds a build dependency and increases compile times slightly.
|
||||||
- **Learning curve.** Cap'n Proto's builder/reader API is different from typical `serde`-based Rust serialisation. Developers must learn the Cap'n Proto programming model (builders for construction, readers for traversal, owned messages for storage).
|
- **Learning curve.** Cap'n Proto's builder/reader API is different from typical `serde`-based Rust serialisation. Developers must learn the Cap'n Proto programming model (builders for construction, readers for traversal, owned messages for storage).
|
||||||
- **Generated code verbosity.** The generated Rust code is verbose and not intended to be read directly. Application code interacts with it through the builder/reader traits.
|
- **Generated code verbosity.** The generated Rust code is verbose and not intended to be read directly. Application code interacts with it through the builder/reader traits.
|
||||||
- **Smaller ecosystem than Protobuf.** Cap'n Proto has fewer users, fewer tutorials, and fewer third-party tools than Protobuf. However, the core Rust crates are well-maintained.
|
- **Smaller ecosystem than Protobuf.** Cap'n Proto has fewer users, fewer tutorials, and fewer third-party tools than Protobuf. However, the core Rust crates are well-maintained.
|
||||||
@@ -114,7 +114,7 @@ The Cap'n Proto schemas are stored in the `schemas/` directory:
|
|||||||
### Residual risks
|
### Residual risks
|
||||||
|
|
||||||
- **Crate maintenance.** The `capnp` and `capnp-rpc` crates are maintained primarily by David Renshaw. If maintenance lapses, the project would need to fork or switch serialisation formats. Mitigated by the crates' maturity and the relatively stable Cap'n Proto specification.
|
- **Crate maintenance.** The `capnp` and `capnp-rpc` crates are maintained primarily by David Renshaw. If maintenance lapses, the project would need to fork or switch serialisation formats. Mitigated by the crates' maturity and the relatively stable Cap'n Proto specification.
|
||||||
- **RPC limitations.** The Rust `capnp-rpc` crate implements Level 1 of the Cap'n Proto RPC protocol. Level 3 features (three-party handoffs) are not supported. This has not been a limitation for quicproquo's client-server architecture.
|
- **RPC limitations.** The Rust `capnp-rpc` crate implements Level 1 of the Cap'n Proto RPC protocol. Level 3 features (three-party handoffs) are not supported. This has not been a limitation for quicprochat's client-server architecture.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -126,8 +126,8 @@ The Cap'n Proto schemas are stored in the `schemas/` directory:
|
|||||||
| `schemas/auth.capnp` | AuthenticationService RPC interface |
|
| `schemas/auth.capnp` | AuthenticationService RPC interface |
|
||||||
| `schemas/delivery.capnp` | DeliveryService RPC interface |
|
| `schemas/delivery.capnp` | DeliveryService RPC interface |
|
||||||
| `schemas/node.capnp` | NodeService unified RPC interface |
|
| `schemas/node.capnp` | NodeService unified RPC interface |
|
||||||
| `crates/quicproquo-proto/build.rs` | Build script that invokes `capnpc` for code generation |
|
| `crates/quicprochat-proto/build.rs` | Build script that invokes `capnpc` for code generation |
|
||||||
| `crates/quicproquo-proto/src/lib.rs` | Re-exports generated Cap'n Proto modules |
|
| `crates/quicprochat-proto/src/lib.rs` | Re-exports generated Cap'n Proto modules |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ The RFC explicitly envisions that the DS operates on opaque blobs, not on decryp
|
|||||||
|
|
||||||
## Decision
|
## Decision
|
||||||
|
|
||||||
The quicproquo Delivery Service is **MLS-unaware**. It routes opaque byte strings by `(recipientKey, channelId)` without parsing, inspecting, or validating any MLS content.
|
The quicprochat Delivery Service is **MLS-unaware**. It routes opaque byte strings by `(recipientKey, channelId)` without parsing, inspecting, or validating any MLS content.
|
||||||
|
|
||||||
### What the DS sees
|
### What the DS sees
|
||||||
|
|
||||||
@@ -109,8 +109,8 @@ This means that sending a message to a group of n members requires n-1 enqueue c
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `schemas/delivery.capnp` | DeliveryService RPC interface (opaque `Data` payloads) |
|
| `schemas/delivery.capnp` | DeliveryService RPC interface (opaque `Data` payloads) |
|
||||||
| `schemas/node.capnp` | NodeService: `enqueue`, `fetch`, `fetchWait` methods |
|
| `schemas/node.capnp` | NodeService: `enqueue`, `fetch`, `fetchWait` methods |
|
||||||
| `crates/quicproquo-server/src/storage.rs` | Server-side queue storage (DashMap-based FIFO queues) |
|
| `crates/quicprochat-server/src/storage.rs` | Server-side queue storage (DashMap-based FIFO queues) |
|
||||||
| `crates/quicproquo-server/src/main.rs` | NodeService RPC handler implementation |
|
| `crates/quicprochat-server/src/main.rs` | NodeService RPC handler implementation |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -100,8 +100,8 @@ This is a defense-in-depth measure. In practice, MLS's own signature verificatio
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `schemas/auth.capnp` | `AuthenticationService` interface: `uploadKeyPackage`, `fetchKeyPackage` |
|
| `schemas/auth.capnp` | `AuthenticationService` interface: `uploadKeyPackage`, `fetchKeyPackage` |
|
||||||
| `schemas/node.capnp` | `NodeService` interface: same methods with `Auth` parameter |
|
| `schemas/node.capnp` | `NodeService` interface: same methods with `Auth` parameter |
|
||||||
| `crates/quicproquo-server/src/storage.rs` | Server-side KeyPackage storage (DashMap-backed queue) |
|
| `crates/quicprochat-server/src/storage.rs` | Server-side KeyPackage storage (DashMap-backed queue) |
|
||||||
| `crates/quicproquo-server/src/main.rs` | RPC handler: `fetchKeyPackage` implementation with atomic removal |
|
| `crates/quicprochat-server/src/main.rs` | RPC handler: `fetchKeyPackage` implementation with atomic removal |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Accepted (supersedes earlier REST gateway proposal)
|
|||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
quicproquo uses QUIC + Cap'n Proto RPC as its native protocol. This
|
quicprochat uses QUIC + Cap'n Proto RPC as its native protocol. This
|
||||||
combination delivers zero-copy serialization, multiplexed streams, and
|
combination delivers zero-copy serialization, multiplexed streams, and
|
||||||
sub-RTT connection establishment — ideal for high-performance clients.
|
sub-RTT connection establishment — ideal for high-performance clients.
|
||||||
|
|
||||||
@@ -58,11 +58,11 @@ Both paths use QUIC transport. The project name stays honest.
|
|||||||
|
|
||||||
### Crypto layer distribution
|
### Crypto layer distribution
|
||||||
|
|
||||||
MLS encryption/decryption must happen client-side. The `quicproquo-core`
|
MLS encryption/decryption must happen client-side. The `quicprochat-core`
|
||||||
crate is compiled to:
|
crate is compiled to:
|
||||||
|
|
||||||
- **WASM** — for browsers, Node.js, Deno
|
- **WASM** — for browsers, Node.js, Deno
|
||||||
- **C FFI** (`libquicproquo`) — for Swift, Kotlin, Python, Go (via cgo)
|
- **C FFI** (`libquicprochat`) — for Swift, Kotlin, Python, Go (via cgo)
|
||||||
- **Native Rust** — for Rust clients (existing)
|
- **Native Rust** — for Rust clients (existing)
|
||||||
|
|
||||||
### Why not REST?
|
### Why not REST?
|
||||||
@@ -104,9 +104,9 @@ gRPC may be reconsidered for server-to-server federation (Phase 7.3).
|
|||||||
|
|
||||||
### Neutral
|
### Neutral
|
||||||
|
|
||||||
- SDKs live in separate repositories (e.g., `quicproquo-go`,
|
- SDKs live in separate repositories (e.g., `quicprochat-go`,
|
||||||
`quicproquo-py`) to avoid bloating the core workspace.
|
`quicprochat-py`) to avoid bloating the core workspace.
|
||||||
- The C FFI crate (`quicproquo-ffi`) bundles both crypto and transport,
|
- The C FFI crate (`quicprochat-ffi`) bundles both crypto and transport,
|
||||||
so language bindings only need to call C functions.
|
so language bindings only need to call C functions.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Design Decisions Overview
|
# Design Decisions Overview
|
||||||
|
|
||||||
This section collects the Architecture Decision Records (ADRs) that document the key design choices in quicproquo. Each ADR follows a standard format: context (why the decision was needed), decision (what was chosen), and consequences (trade-offs, benefits, and residual risks).
|
This section collects the Architecture Decision Records (ADRs) that document the key design choices in quicprochat. Each ADR follows a standard format: context (why the decision was needed), decision (what was chosen), and consequences (trade-offs, benefits, and residual risks).
|
||||||
|
|
||||||
These decisions are not immutable. Each ADR has a status field and can be superseded by a later ADR if circumstances change. The goal is to preserve the reasoning behind each choice so that future contributors understand *why* the system works the way it does, not just *how*.
|
These decisions are not immutable. Each ADR has a status field and can be superseded by a later ADR if circumstances change. The goal is to preserve the reasoning behind each choice so that future contributors understand *why* the system works the way it does, not just *how*.
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ These decisions are not immutable. Each ADR has a status field and can be supers
|
|||||||
|
|
||||||
## Design comparison
|
## Design comparison
|
||||||
|
|
||||||
For a broader comparison of quicproquo's design against alternative messaging protocols (Signal, Matrix/Olm/Megolm), see [Why This Design, Not Signal/Matrix/...](why-not-signal.md).
|
For a broader comparison of quicprochat's design against alternative messaging protocols (Signal, Matrix/Olm/Megolm), see [Why This Design, Not Signal/Matrix/...](why-not-signal.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ Each ADR page follows this structure:
|
|||||||
|
|
||||||
**Context**
|
**Context**
|
||||||
|
|
||||||
quicproquo v1 used Cap'n Proto for both serialisation and RPC dispatch via
|
quicprochat v1 used Cap'n Proto for both serialisation and RPC dispatch via
|
||||||
`capnp-rpc`. This worked well for the initial 8-method `NodeService` interface
|
`capnp-rpc`. This worked well for the initial 8-method `NodeService` interface
|
||||||
but had several limitations as the protocol expanded:
|
but had several limitations as the protocol expanded:
|
||||||
|
|
||||||
@@ -60,15 +60,15 @@ but had several limitations as the protocol expanded:
|
|||||||
|
|
||||||
**Decision**
|
**Decision**
|
||||||
|
|
||||||
Replace `capnp-rpc` with a custom binary framing layer (`quicproquo-rpc`) and
|
Replace `capnp-rpc` with a custom binary framing layer (`quicprochat-rpc`) and
|
||||||
Protocol Buffers (`prost`) for payload serialisation:
|
Protocol Buffers (`prost`) for payload serialisation:
|
||||||
|
|
||||||
- Three frame types: Request (10-byte header), Response (9-byte header), Push
|
- Three frame types: Request (10-byte header), Response (9-byte header), Push
|
||||||
(6-byte header), all carrying Protobuf-encoded payloads.
|
(6-byte header), all carrying Protobuf-encoded payloads.
|
||||||
- Method IDs are numeric `u16` constants dispatched via a handler registry.
|
- Method IDs are numeric `u16` constants dispatched via a handler registry.
|
||||||
One QUIC bidirectional stream per RPC call; push events on QUIC uni-streams.
|
One QUIC bidirectional stream per RPC call; push events on QUIC uni-streams.
|
||||||
- ALPN changed from `b"capnp"` to `b"qpq"`. Default port changed from 7000 to 5001.
|
- ALPN changed from `b"capnp"` to `b"qpc"`. Default port changed from 7000 to 5001.
|
||||||
- Cap'n Proto legacy types are retained in `quicproquo-proto` for v1 compatibility
|
- Cap'n Proto legacy types are retained in `quicprochat-proto` for v1 compatibility
|
||||||
but are no longer used for RPC dispatch.
|
but are no longer used for RPC dispatch.
|
||||||
|
|
||||||
**Consequences**
|
**Consequences**
|
||||||
@@ -90,9 +90,9 @@ Costs:
|
|||||||
|
|
||||||
**Code references**
|
**Code references**
|
||||||
|
|
||||||
- Frame format: `crates/quicproquo-rpc/src/framing.rs`
|
- Frame format: `crates/quicprochat-rpc/src/framing.rs`
|
||||||
- Method IDs: `crates/quicproquo-proto/src/lib.rs` (`method_ids` module)
|
- Method IDs: `crates/quicprochat-proto/src/lib.rs` (`method_ids` module)
|
||||||
- Proto schemas: `proto/qpq/v1/*.proto`
|
- Proto schemas: `proto/qpc/v1/*.proto`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ ADR-004 and ADR-005 reflect a design philosophy where the server does as little
|
|||||||
### Schema-first design
|
### Schema-first design
|
||||||
|
|
||||||
The v2 protocol defines all messages and method IDs in checked-in source files
|
The v2 protocol defines all messages and method IDs in checked-in source files
|
||||||
(`proto/qpq/v1/*.proto` and `crates/quicproquo-proto/src/lib.rs`). Every wire
|
(`proto/qpc/v1/*.proto` and `crates/quicprochat-proto/src/lib.rs`). Every wire
|
||||||
type is documented, versioned, and evolvable through the standard Protobuf
|
type is documented, versioned, and evolvable through the standard Protobuf
|
||||||
schema evolution rules (adding optional fields, reserving removed field numbers).
|
schema evolution rules (adding optional fields, reserving removed field numbers).
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Comparison with Classical Chat Protocols
|
# Comparison with Classical Chat Protocols
|
||||||
|
|
||||||
This page compares quicproquo against **classical and legacy chat protocols** -- IRC+SSL, XMPP (with and without OMEMO), Telegram's MTProto, and plain TCP/TLS chat systems -- to demonstrate what a modern, cryptographically rigorous design provides over protocols that were designed before end-to-end encryption, post-compromise security, and post-quantum readiness were practical concerns.
|
This page compares quicprochat against **classical and legacy chat protocols** -- IRC+SSL, XMPP (with and without OMEMO), Telegram's MTProto, and plain TCP/TLS chat systems -- to demonstrate what a modern, cryptographically rigorous design provides over protocols that were designed before end-to-end encryption, post-compromise security, and post-quantum readiness were practical concerns.
|
||||||
|
|
||||||
For a comparison against modern E2E-encrypted protocols (Signal, Matrix/Olm/Megolm), see [Why This Design, Not Signal/Matrix/...](why-not-signal.md).
|
For a comparison against modern E2E-encrypted protocols (Signal, Matrix/Olm/Megolm), see [Why This Design, Not Signal/Matrix/...](why-not-signal.md).
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ For a comparison against modern E2E-encrypted protocols (Signal, Matrix/Olm/Mego
|
|||||||
## At a glance
|
## At a glance
|
||||||
|
|
||||||
```
|
```
|
||||||
Classical IRC+SSL quicproquo
|
Classical IRC+SSL quicprochat
|
||||||
───────────────── ──────────────
|
───────────────── ──────────────
|
||||||
|
|
||||||
You ──TLS──▶ Server ──TLS──▶ Bob You ──QUIC/TLS──▶ Server ──QUIC/TLS──▶ Bob
|
You ──TLS──▶ Server ──TLS──▶ Bob You ──QUIC/TLS──▶ Server ──QUIC/TLS──▶ Bob
|
||||||
@@ -19,13 +19,13 @@ For a comparison against modern E2E-encrypted protocols (Signal, Matrix/Olm/Mego
|
|||||||
messages (cannot decrypt)
|
messages (cannot decrypt)
|
||||||
```
|
```
|
||||||
|
|
||||||
The fundamental difference: **classical protocols trust the server with your plaintext**. quicproquo's server is cryptographically excluded from reading message content.
|
The fundamental difference: **classical protocols trust the server with your plaintext**. quicprochat's server is cryptographically excluded from reading message content.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Protocol comparison matrix
|
## Protocol comparison matrix
|
||||||
|
|
||||||
| Property | IRC+SSL | XMPP+TLS | XMPP+OMEMO | Telegram (MTProto) | quicproquo |
|
| Property | IRC+SSL | XMPP+TLS | XMPP+OMEMO | Telegram (MTProto) | quicprochat |
|
||||||
|---|---|---|---|---|---|
|
|---|---|---|---|---|---|
|
||||||
| **Transport encryption** | TLS (server-to-server optional) | STARTTLS / direct TLS | STARTTLS / direct TLS | MTProto 2.0 (custom) | QUIC + TLS 1.3 |
|
| **Transport encryption** | TLS (server-to-server optional) | STARTTLS / direct TLS | STARTTLS / direct TLS | MTProto 2.0 (custom) | QUIC + TLS 1.3 |
|
||||||
| **End-to-end encryption** | None | None | Double Ratchet (1:1) | "Secret chats" only (1:1) | MLS RFC 9420 (groups native) |
|
| **End-to-end encryption** | None | None | Double Ratchet (1:1) | "Secret chats" only (1:1) | MLS RFC 9420 (groups native) |
|
||||||
@@ -41,7 +41,7 @@ The fundamental difference: **classical protocols trust the server with your pla
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Deep dive: IRC+SSL vs. quicproquo
|
## Deep dive: IRC+SSL vs. quicprochat
|
||||||
|
|
||||||
IRC (Internet Relay Chat) is the archetypal chat protocol, designed in 1988. Adding SSL/TLS wraps the TCP connection in transport encryption, but the protocol's security model remains fundamentally unchanged.
|
IRC (Internet Relay Chat) is the archetypal chat protocol, designed in 1988. Adding SSL/TLS wraps the TCP connection in transport encryption, but the protocol's security model remains fundamentally unchanged.
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ IRC (Internet Relay Chat) is the archetypal chat protocol, designed in 1988. Add
|
|||||||
4. **No post-compromise security.** There is no mechanism to recover from a key compromise. If a server is breached, all messages flowing through it are exposed indefinitely.
|
4. **No post-compromise security.** There is no mechanism to recover from a key compromise. If a server is breached, all messages flowing through it are exposed indefinitely.
|
||||||
5. **No identity binding.** NickServ password authentication is plaintext over the IRC protocol (inside TLS, but visible to the server). There is no cryptographic binding between a user's identity and their messages.
|
5. **No identity binding.** NickServ password authentication is plaintext over the IRC protocol (inside TLS, but visible to the server). There is no cryptographic binding between a user's identity and their messages.
|
||||||
|
|
||||||
### What happens when Alice sends a message on quicproquo
|
### What happens when Alice sends a message on quicprochat
|
||||||
|
|
||||||
```
|
```
|
||||||
┌───────┐ ┌────────┐ ┌─────┐
|
┌───────┐ ┌────────┐ ┌─────┐
|
||||||
@@ -93,7 +93,7 @@ IRC (Internet Relay Chat) is the archetypal chat protocol, designed in 1988. Add
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Deep dive: XMPP+OMEMO vs. quicproquo
|
## Deep dive: XMPP+OMEMO vs. quicprochat
|
||||||
|
|
||||||
XMPP with OMEMO (XEP-0384) adds end-to-end encryption via the Signal Double Ratchet protocol. This is a significant improvement over plain XMPP, but OMEMO inherits the limitations of the Signal Protocol for group messaging.
|
XMPP with OMEMO (XEP-0384) adds end-to-end encryption via the Signal Double Ratchet protocol. This is a significant improvement over plain XMPP, but OMEMO inherits the limitations of the Signal Protocol for group messaging.
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ XMPP with OMEMO (XEP-0384) adds end-to-end encryption via the Signal Double Ratc
|
|||||||
3 encryptions per message
|
3 encryptions per message
|
||||||
O(n) cost per send
|
O(n) cost per send
|
||||||
|
|
||||||
quicproquo MLS group (4 members)
|
quicprochat MLS group (4 members)
|
||||||
|
|
||||||
Alice encrypts once with group epoch key:
|
Alice encrypts once with group epoch key:
|
||||||
┌───────┐ ── MLS_encrypt(epoch_key) ──▶ Server
|
┌───────┐ ── MLS_encrypt(epoch_key) ──▶ Server
|
||||||
@@ -120,7 +120,7 @@ XMPP with OMEMO (XEP-0384) adds end-to-end encryption via the Signal Double Ratc
|
|||||||
(all decrypt with same epoch key)
|
(all decrypt with same epoch key)
|
||||||
```
|
```
|
||||||
|
|
||||||
| Property | XMPP+OMEMO groups | quicproquo MLS groups |
|
| Property | XMPP+OMEMO groups | quicprochat MLS groups |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Encryption per message** | O(n) -- encrypt once per recipient | O(1) -- single MLS application message |
|
| **Encryption per message** | O(n) -- encrypt once per recipient | O(1) -- single MLS application message |
|
||||||
| **Add member** | O(n) -- distribute sender keys to all | O(log n) -- single MLS Commit |
|
| **Add member** | O(n) -- distribute sender keys to all | O(log n) -- single MLS Commit |
|
||||||
@@ -131,7 +131,7 @@ XMPP with OMEMO (XEP-0384) adds end-to-end encryption via the Signal Double Ratc
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Deep dive: Telegram (MTProto) vs. quicproquo
|
## Deep dive: Telegram (MTProto) vs. quicprochat
|
||||||
|
|
||||||
Telegram is often perceived as a "secure" messenger, but its default mode provides **no end-to-end encryption**. Only "Secret Chats" (1:1 only, not available on desktop) use E2E encryption.
|
Telegram is often perceived as a "secure" messenger, but its default mode provides **no end-to-end encryption**. Only "Secret Chats" (1:1 only, not available on desktop) use E2E encryption.
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ Telegram is often perceived as a "secure" messenger, but its default mode provid
|
|||||||
|
|
||||||
### Comparison
|
### Comparison
|
||||||
|
|
||||||
| Property | Telegram Cloud Chats | Telegram Secret Chats | quicproquo |
|
| Property | Telegram Cloud Chats | Telegram Secret Chats | quicprochat |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **Server reads plaintext** | Yes | No | No |
|
| **Server reads plaintext** | Yes | No | No |
|
||||||
| **Group E2E** | No | N/A (1:1 only) | Yes (MLS) |
|
| **Group E2E** | No | N/A (1:1 only) | Yes (MLS) |
|
||||||
@@ -173,7 +173,7 @@ Telegram is often perceived as a "secure" messenger, but its default mode provid
|
|||||||
| **Open source server** | No | No | Yes (MIT license) |
|
| **Open source server** | No | No | Yes (MIT license) |
|
||||||
| **Post-quantum** | None | None | Hybrid KEM (X25519 + ML-KEM-768) |
|
| **Post-quantum** | None | None | Hybrid KEM (X25519 + ML-KEM-768) |
|
||||||
|
|
||||||
**Critical concern with Telegram:** MTProto is a custom, proprietary cryptographic protocol that has not undergone the same level of independent cryptographic review as standard protocols (TLS, MLS, Signal Protocol). Multiple academic papers have identified weaknesses in earlier versions. quicproquo exclusively uses IETF-standardized protocols (TLS 1.3, MLS RFC 9420) and widely reviewed cryptographic primitives.
|
**Critical concern with Telegram:** MTProto is a custom, proprietary cryptographic protocol that has not undergone the same level of independent cryptographic review as standard protocols (TLS, MLS, Signal Protocol). Multiple academic papers have identified weaknesses in earlier versions. quicprochat exclusively uses IETF-standardized protocols (TLS 1.3, MLS RFC 9420) and widely reviewed cryptographic primitives.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ An attacker gains root access to the chat server.
|
|||||||
│ sees metadata (who talks to whom, │
|
│ sees metadata (who talks to whom, │
|
||||||
│ when, message sizes). │
|
│ when, message sizes). │
|
||||||
│ │
|
│ │
|
||||||
│ quicproquo: │
|
│ quicprochat: │
|
||||||
│ Cannot read messages (MLS E2E). │
|
│ Cannot read messages (MLS E2E). │
|
||||||
│ Sees metadata (recipient keys, │
|
│ Sees metadata (recipient keys, │
|
||||||
│ timing, sizes). │
|
│ timing, sizes). │
|
||||||
@@ -243,7 +243,7 @@ A state-level adversary records all encrypted traffic today, planning to decrypt
|
|||||||
Telegram (MTProto / custom DH):
|
Telegram (MTProto / custom DH):
|
||||||
└── Quantum computer breaks DH → all recorded messages decrypted
|
└── Quantum computer breaks DH → all recorded messages decrypted
|
||||||
|
|
||||||
quicproquo (Hybrid KEM):
|
quicprochat (Hybrid KEM):
|
||||||
└── Transport: QUIC/TLS with ECDHE → quantum computer breaks this layer
|
└── Transport: QUIC/TLS with ECDHE → quantum computer breaks this layer
|
||||||
└── Inner layer: MLS content encrypted with group epoch keys
|
└── Inner layer: MLS content encrypted with group epoch keys
|
||||||
└── Hybrid KEM envelope: X25519 + ML-KEM-768
|
└── Hybrid KEM envelope: X25519 + ML-KEM-768
|
||||||
@@ -252,7 +252,7 @@ A state-level adversary records all encrypted traffic today, planning to decrypt
|
|||||||
└── Combined key: STILL SECURE (both must be broken)
|
└── Combined key: STILL SECURE (both must be broken)
|
||||||
```
|
```
|
||||||
|
|
||||||
quicproquo's hybrid "belt and suspenders" design means that **even if X25519 falls to a quantum computer, ML-KEM-768 protects the content**. The adversary's recorded ciphertext remains useless.
|
quicprochat's hybrid "belt and suspenders" design means that **even if X25519 falls to a quantum computer, ML-KEM-768 protects the content**. The adversary's recorded ciphertext remains useless.
|
||||||
|
|
||||||
### Scenario 3: Device theft / compromise
|
### Scenario 3: Device theft / compromise
|
||||||
|
|
||||||
@@ -279,7 +279,7 @@ An attacker steals Alice's unlocked device and extracts her key material.
|
|||||||
Messages after T: all accessible (cloud sync)
|
Messages after T: all accessible (cloud sync)
|
||||||
Recovery: terminate session from another device
|
Recovery: terminate session from another device
|
||||||
|
|
||||||
quicproquo:
|
quicprochat:
|
||||||
Messages before T: protected (MLS forward secrecy, past epoch keys deleted)
|
Messages before T: protected (MLS forward secrecy, past epoch keys deleted)
|
||||||
Messages after T: exposed only until next MLS epoch advance
|
Messages after T: exposed only until next MLS epoch advance
|
||||||
Recovery: ANY group member issues an MLS Update proposal →
|
Recovery: ANY group member issues an MLS Update proposal →
|
||||||
@@ -293,7 +293,7 @@ An attacker steals Alice's unlocked device and extracts her key material.
|
|||||||
|
|
||||||
### Why QUIC over TCP
|
### Why QUIC over TCP
|
||||||
|
|
||||||
Classical protocols (IRC, XMPP) use TCP, which suffers from head-of-line (HOL) blocking. quicproquo uses QUIC, which provides independent streams over UDP.
|
Classical protocols (IRC, XMPP) use TCP, which suffers from head-of-line (HOL) blocking. quicprochat uses QUIC, which provides independent streams over UDP.
|
||||||
|
|
||||||
```
|
```
|
||||||
TCP (IRC/XMPP): all streams share one ordered byte stream
|
TCP (IRC/XMPP): all streams share one ordered byte stream
|
||||||
@@ -308,7 +308,7 @@ Classical protocols (IRC, XMPP) use TCP, which suffers from head-of-line (HOL) b
|
|||||||
└── ALL streams blocked until retransmit
|
└── ALL streams blocked until retransmit
|
||||||
|
|
||||||
|
|
||||||
QUIC (quicproquo): each stream is independent
|
QUIC (quicprochat): each stream is independent
|
||||||
──────────────────────────────────────────────────
|
──────────────────────────────────────────────────
|
||||||
|
|
||||||
Stream A: ████████░░██████████████ (only A waits)
|
Stream A: ████████░░██████████████ (only A waits)
|
||||||
@@ -334,7 +334,7 @@ Classical protocols (IRC, XMPP) use TCP, which suffers from head-of-line (HOL) b
|
|||||||
════════════════════════════════════════════════════
|
════════════════════════════════════════════════════
|
||||||
Total: 2-3 round trips before first message
|
Total: 2-3 round trips before first message
|
||||||
|
|
||||||
quicproquo: QUIC integrates crypto into handshake = 1 RTT (or 0-RTT)
|
quicprochat: QUIC integrates crypto into handshake = 1 RTT (or 0-RTT)
|
||||||
──────────────────────────────────────────────────────────────────────────
|
──────────────────────────────────────────────────────────────────────────
|
||||||
Client ──Initial(ClientHello)──▶ Server │
|
Client ──Initial(ClientHello)──▶ Server │
|
||||||
Client ◀──Initial(ServerHello)── Server │ 1 RTT total
|
Client ◀──Initial(ServerHello)── Server │ 1 RTT total
|
||||||
@@ -363,7 +363,7 @@ Classical protocols (IRC, XMPP) use TCP, which suffers from head-of-line (HOL) b
|
|||||||
Phone number + SMS OTP ← carrier and Telegram see phone number
|
Phone number + SMS OTP ← carrier and Telegram see phone number
|
||||||
(identity = phone number) ← no cryptographic identity
|
(identity = phone number) ← no cryptographic identity
|
||||||
|
|
||||||
quicproquo (OPAQUE PAKE):
|
quicprochat (OPAQUE PAKE):
|
||||||
Client ──blinded_element──▶ Server │ Server never sees password
|
Client ──blinded_element──▶ Server │ Server never sees password
|
||||||
Client ◀──evaluated_element── Server │ Mutual authentication
|
Client ◀──evaluated_element── Server │ Mutual authentication
|
||||||
Client ──finalization──▶ Server │ Session key derived
|
Client ──finalization──▶ Server │ Session key derived
|
||||||
@@ -406,7 +406,7 @@ Classical protocols (IRC, XMPP) use TCP, which suffers from head-of-line (HOL) b
|
|||||||
│ Schema via XSD exists but rarely enforced at runtime. │
|
│ Schema via XSD exists but rarely enforced at runtime. │
|
||||||
└──────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
Cap'n Proto (quicproquo):
|
Cap'n Proto (quicprochat):
|
||||||
┌──────────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────┐
|
||||||
│ [8-byte aligned struct with pointers] │
|
│ [8-byte aligned struct with pointers] │
|
||||||
│ │
|
│ │
|
||||||
@@ -434,7 +434,7 @@ The following diagram maps each protocol against the security properties it prov
|
|||||||
Telegram Cloud · · · · · · · ·
|
Telegram Cloud · · · · · · · ·
|
||||||
Telegram Secret △ · ● · · ● · ·
|
Telegram Secret △ · ● · · ● · ·
|
||||||
Signal ● · ● ● △ ● · ·
|
Signal ● · ● ● △ ● · ·
|
||||||
quicproquo ● ● ● ● ● ● ● ●
|
quicprochat ● ● ● ● ● ● ● ●
|
||||||
|
|
||||||
Legend: ● = yes △ = partial · = no
|
Legend: ● = yes △ = partial · = no
|
||||||
FS = forward secrecy PCS = post-compromise security
|
FS = forward secrecy PCS = post-compromise security
|
||||||
@@ -446,12 +446,12 @@ The following diagram maps each protocol against the security properties it prov
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## The quicproquo advantage: a layered defense
|
## The quicprochat advantage: a layered defense
|
||||||
|
|
||||||
Classical protocols rely on a **single layer** of security (transport TLS). quicproquo applies defense in depth with **three independent layers**, each of which must be broken separately:
|
Classical protocols rely on a **single layer** of security (transport TLS). quicprochat applies defense in depth with **three independent layers**, each of which must be broken separately:
|
||||||
|
|
||||||
```
|
```
|
||||||
IRC+SSL security layers: quicproquo security layers:
|
IRC+SSL security layers: quicprochat security layers:
|
||||||
|
|
||||||
┌─────────────────────────┐ ┌─────────────────────────────────┐
|
┌─────────────────────────┐ ┌─────────────────────────────────┐
|
||||||
│ TLS (transport) │ │ Layer 3: Hybrid KEM envelope │
|
│ TLS (transport) │ │ Layer 3: Hybrid KEM envelope │
|
||||||
@@ -474,7 +474,7 @@ Classical protocols rely on a **single layer** of security (transport TLS). quic
|
|||||||
|
|
||||||
To read a message, attacker must break:
|
To read a message, attacker must break:
|
||||||
IRC+SSL: TLS (1 layer)
|
IRC+SSL: TLS (1 layer)
|
||||||
quicproquo: TLS + MLS + Hybrid KEM (3 layers)
|
quicprochat: TLS + MLS + Hybrid KEM (3 layers)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -483,7 +483,7 @@ Classical protocols rely on a **single layer** of security (transport TLS). quic
|
|||||||
|
|
||||||
Fairness demands acknowledging where classical protocols genuinely excel:
|
Fairness demands acknowledging where classical protocols genuinely excel:
|
||||||
|
|
||||||
| Advantage | IRC | quicproquo |
|
| Advantage | IRC | quicprochat |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Simplicity** | Telnet-compatible text protocol | Binary protocol requiring client implementation |
|
| **Simplicity** | Telnet-compatible text protocol | Binary protocol requiring client implementation |
|
||||||
| **Maturity** | 35+ years of production use | Early-stage research project |
|
| **Maturity** | 35+ years of production use | Early-stage research project |
|
||||||
@@ -493,7 +493,7 @@ Fairness demands acknowledging where classical protocols genuinely excel:
|
|||||||
| **Public channels** | Designed for open, unencrypted discussion | Designed for private, encrypted communication |
|
| **Public channels** | Designed for open, unencrypted discussion | Designed for private, encrypted communication |
|
||||||
| **Anonymity** | No identity required | Requires Ed25519 identity keypair |
|
| **Anonymity** | No identity required | Requires Ed25519 identity keypair |
|
||||||
|
|
||||||
IRC remains an excellent choice for **public, open discussion** where encryption is not needed and simplicity is valued. quicproquo is designed for a different threat model: private communication where **confidentiality, forward secrecy, and post-compromise security** are requirements, not luxuries.
|
IRC remains an excellent choice for **public, open discussion** where encryption is not needed and simplicity is valued. quicprochat is designed for a different threat model: private communication where **confidentiality, forward secrecy, and post-compromise security** are requirements, not luxuries.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -501,10 +501,10 @@ IRC remains an excellent choice for **public, open discussion** where encryption
|
|||||||
|
|
||||||
For users and operators coming from classical chat systems, here is what changes practically:
|
For users and operators coming from classical chat systems, here is what changes practically:
|
||||||
|
|
||||||
| Concern | Classical (IRC/XMPP) | quicproquo |
|
| Concern | Classical (IRC/XMPP) | quicprochat |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Server setup** | Install IRCd, configure TLS cert | `cargo build && ./qpq-server` (auto-generates TLS cert) |
|
| **Server setup** | Install IRCd, configure TLS cert | `cargo build && ./qpc-server` (auto-generates TLS cert) |
|
||||||
| **Client setup** | Install any IRC client | `./quicproquo-client register-user` (generates Ed25519 identity) |
|
| **Client setup** | Install any IRC client | `./quicprochat-client register-user` (generates Ed25519 identity) |
|
||||||
| **Joining a group** | `/join #channel` | Receive MLS Welcome message from group creator |
|
| **Joining a group** | `/join #channel` | Receive MLS Welcome message from group creator |
|
||||||
| **Sending a message** | Type and press enter | Same -- client handles MLS encryption transparently |
|
| **Sending a message** | Type and press enter | Same -- client handles MLS encryption transparently |
|
||||||
| **Server admin sees messages** | Yes (always) | No (never -- server sees only ciphertext) |
|
| **Server admin sees messages** | Yes (always) | No (never -- server sees only ciphertext) |
|
||||||
@@ -518,7 +518,7 @@ For users and operators coming from classical chat systems, here is what changes
|
|||||||
|
|
||||||
- [Why This Design, Not Signal/Matrix/...](why-not-signal.md) -- comparison with modern E2E-encrypted protocols
|
- [Why This Design, Not Signal/Matrix/...](why-not-signal.md) -- comparison with modern E2E-encrypted protocols
|
||||||
- [Protocol Layers Overview](../protocol-layers/overview.md) -- detailed protocol stack documentation
|
- [Protocol Layers Overview](../protocol-layers/overview.md) -- detailed protocol stack documentation
|
||||||
- [Threat Model](../cryptography/threat-model.md) -- what quicproquo does and does not protect against
|
- [Threat Model](../cryptography/threat-model.md) -- what quicprochat does and does not protect against
|
||||||
- [Post-Quantum Readiness](../cryptography/post-quantum-readiness.md) -- hybrid KEM design and rationale
|
- [Post-Quantum Readiness](../cryptography/post-quantum-readiness.md) -- hybrid KEM design and rationale
|
||||||
- [MLS (RFC 9420)](../protocol-layers/mls.md) -- deep dive into the group key agreement protocol
|
- [MLS (RFC 9420)](../protocol-layers/mls.md) -- deep dive into the group key agreement protocol
|
||||||
- [Architecture Overview](../architecture/overview.md) -- system-level architecture
|
- [Architecture Overview](../architecture/overview.md) -- system-level architecture
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Why This Design, Not Signal/Matrix/...
|
# Why This Design, Not Signal/Matrix/...
|
||||||
|
|
||||||
This page compares quicproquo's protocol choices against two widely deployed secure messaging systems -- the Signal Protocol and the Matrix ecosystem (Olm/Megolm) -- to explain why a different architecture was chosen. The comparison covers four dimensions: group key agreement, transport, serialisation, and overall trade-offs.
|
This page compares quicprochat's protocol choices against two widely deployed secure messaging systems -- the Signal Protocol and the Matrix ecosystem (Olm/Megolm) -- to explain why a different architecture was chosen. The comparison covers four dimensions: group key agreement, transport, serialisation, and overall trade-offs.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -26,11 +26,11 @@ The Signal Protocol was designed for **1:1 messaging** and later extended to gro
|
|||||||
- Group membership changes require O(n) pairwise Sender Key distributions. Adding or removing a member requires the affected member to generate a new Sender Key and distribute it to all n-1 other members.
|
- Group membership changes require O(n) pairwise Sender Key distributions. Adding or removing a member requires the affected member to generate a new Sender Key and distribute it to all n-1 other members.
|
||||||
- The pairwise key exchange for initial setup is O(n^2): each of n members must establish a Double Ratchet session with each of the other n-1 members.
|
- The pairwise key exchange for initial setup is O(n^2): each of n members must establish a Double Ratchet session with each of the other n-1 members.
|
||||||
|
|
||||||
**Limitations for quicproquo's use case:**
|
**Limitations for quicprochat's use case:**
|
||||||
|
|
||||||
- O(n^2) pairwise setup cost limits practical group size.
|
- O(n^2) pairwise setup cost limits practical group size.
|
||||||
- No post-compromise security for groups is a significant gap.
|
- No post-compromise security for groups is a significant gap.
|
||||||
- The protocol requires a central server for X3DH prekey bundle distribution (similar to quicproquo's AS, but tightly coupled to the Signal server).
|
- The protocol requires a central server for X3DH prekey bundle distribution (similar to quicprochat's AS, but tightly coupled to the Signal server).
|
||||||
|
|
||||||
### Matrix / Olm / Megolm
|
### Matrix / Olm / Megolm
|
||||||
|
|
||||||
@@ -54,15 +54,15 @@ The Matrix ecosystem uses two distinct cryptographic protocols:
|
|||||||
- **Eventually consistent state** model means that room membership, key sharing, and message ordering can diverge between homeservers. The client must reconcile these inconsistencies, adding complexity to the state machine.
|
- **Eventually consistent state** model means that room membership, key sharing, and message ordering can diverge between homeservers. The client must reconcile these inconsistencies, adding complexity to the state machine.
|
||||||
- **Device verification** is a persistent UX challenge. The cross-signing mechanism is powerful but difficult for users to understand.
|
- **Device verification** is a persistent UX challenge. The cross-signing mechanism is powerful but difficult for users to understand.
|
||||||
|
|
||||||
**Limitations for quicproquo's use case:**
|
**Limitations for quicprochat's use case:**
|
||||||
|
|
||||||
- No post-compromise security for groups (same limitation as Signal's Sender Keys).
|
- No post-compromise security for groups (same limitation as Signal's Sender Keys).
|
||||||
- Federation adds latency, metadata exposure, and state management complexity that quicproquo does not need.
|
- Federation adds latency, metadata exposure, and state management complexity that quicprochat does not need.
|
||||||
- JSON-based wire format is inefficient (see serialisation comparison below).
|
- JSON-based wire format is inefficient (see serialisation comparison below).
|
||||||
|
|
||||||
### quicproquo: MLS (RFC 9420)
|
### quicprochat: MLS (RFC 9420)
|
||||||
|
|
||||||
quicproquo uses the **Messaging Layer Security (MLS)** protocol, standardized as RFC 9420 by the IETF.
|
quicprochat uses the **Messaging Layer Security (MLS)** protocol, standardized as RFC 9420 by the IETF.
|
||||||
|
|
||||||
**Key properties:**
|
**Key properties:**
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ quicproquo uses the **Messaging Layer Security (MLS)** protocol, standardized as
|
|||||||
|
|
||||||
**Cost of group operations:**
|
**Cost of group operations:**
|
||||||
|
|
||||||
| Operation | Signal (Sender Keys) | Matrix (Megolm) | MLS (quicproquo) |
|
| Operation | Signal (Sender Keys) | Matrix (Megolm) | MLS (quicprochat) |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Add member | O(n) Sender Key distributions | O(n) Megolm session shares | O(log n) tree update |
|
| Add member | O(n) Sender Key distributions | O(n) Megolm session shares | O(log n) tree update |
|
||||||
| Remove member | O(n) Sender Key rotations | O(n) new Megolm session | O(log n) tree update |
|
| Remove member | O(n) Sender Key rotations | O(n) new Megolm session | O(log n) tree update |
|
||||||
@@ -87,7 +87,7 @@ quicproquo uses the **Messaging Layer Security (MLS)** protocol, standardized as
|
|||||||
|
|
||||||
The transport layer determines how encrypted payloads reach the server and how client-server authentication is performed.
|
The transport layer determines how encrypted payloads reach the server and how client-server authentication is performed.
|
||||||
|
|
||||||
| Property | Signal | Matrix | quicproquo |
|
| Property | Signal | Matrix | quicprochat |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **Transport protocol** | TLS over TCP (HTTP/2) | HTTPS (TLS over TCP) | QUIC (UDP) + TLS 1.3 |
|
| **Transport protocol** | TLS over TCP (HTTP/2) | HTTPS (TLS over TCP) | QUIC (UDP) + TLS 1.3 |
|
||||||
| **Multiplexing** | HTTP/2 stream multiplexing | HTTP/1.1 or HTTP/2 | Native QUIC stream multiplexing |
|
| **Multiplexing** | HTTP/2 stream multiplexing | HTTP/1.1 or HTTP/2 | Native QUIC stream multiplexing |
|
||||||
@@ -106,7 +106,7 @@ QUIC eliminates TCP head-of-line blocking, which is particularly important for a
|
|||||||
|
|
||||||
The serialisation format determines the overhead of encoding and decoding messages, the type safety of the wire format, and the feasibility of schema evolution.
|
The serialisation format determines the overhead of encoding and decoding messages, the type safety of the wire format, and the feasibility of schema evolution.
|
||||||
|
|
||||||
| Property | Signal (Protobuf) | Matrix (JSON) | quicproquo (Cap'n Proto) |
|
| Property | Signal (Protobuf) | Matrix (JSON) | quicprochat (Cap'n Proto) |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **Format** | Binary, schema-defined | Text, schema-optional (JSON Schema exists but is not enforced by the wire format) | Binary, schema-defined |
|
| **Format** | Binary, schema-defined | Text, schema-optional (JSON Schema exists but is not enforced by the wire format) | Binary, schema-defined |
|
||||||
| **Deserialization cost** | Requires a decode pass (allocates and copies) | Requires a parse pass (allocates, copies, and handles UTF-8) | **Zero-copy**: the wire bytes are the in-memory representation. Readers traverse pointers in-place. |
|
| **Deserialization cost** | Requires a decode pass (allocates and copies) | Requires a parse pass (allocates, copies, and handles UTF-8) | **Zero-copy**: the wire bytes are the in-memory representation. Readers traverse pointers in-place. |
|
||||||
@@ -118,7 +118,7 @@ The serialisation format determines the overhead of encoding and decoding messag
|
|||||||
|
|
||||||
**Why Cap'n Proto over Protobuf?**
|
**Why Cap'n Proto over Protobuf?**
|
||||||
|
|
||||||
While Protobuf is a reasonable choice (and Signal uses it successfully), Cap'n Proto provides two features that are particularly valuable for quicproquo:
|
While Protobuf is a reasonable choice (and Signal uses it successfully), Cap'n Proto provides two features that are particularly valuable for quicprochat:
|
||||||
|
|
||||||
1. **Zero-copy deserialization** eliminates a class of allocation and performance overhead. In a messaging system that processes many small messages, avoiding deserialization copies adds up.
|
1. **Zero-copy deserialization** eliminates a class of allocation and performance overhead. In a messaging system that processes many small messages, avoiding deserialization copies adds up.
|
||||||
2. **Built-in RPC** means that Cap'n Proto is both the serialisation format and the RPC framework. There is no need for a separate gRPC or HTTP layer. The same `.capnp` schema file defines both the data structures and the service interface.
|
2. **Built-in RPC** means that Cap'n Proto is both the serialisation format and the RPC framework. There is no need for a separate gRPC or HTTP layer. The same `.capnp` schema file defines both the data structures and the service interface.
|
||||||
@@ -128,7 +128,7 @@ While Protobuf is a reasonable choice (and Signal uses it successfully), Cap'n P
|
|||||||
|
|
||||||
## Summary comparison table
|
## Summary comparison table
|
||||||
|
|
||||||
| Dimension | Signal | Matrix | quicproquo |
|
| Dimension | Signal | Matrix | quicprochat |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **1:1 encryption** | Double Ratchet (FS + PCS) | Olm / Double Ratchet (FS + PCS) | MLS (FS + PCS) |
|
| **1:1 encryption** | Double Ratchet (FS + PCS) | Olm / Double Ratchet (FS + PCS) | MLS (FS + PCS) |
|
||||||
| **Group encryption** | Sender Keys (FS only) | Megolm (FS only) | MLS (FS + PCS) |
|
| **Group encryption** | Sender Keys (FS only) | Megolm (FS only) | MLS (FS + PCS) |
|
||||||
@@ -143,13 +143,13 @@ While Protobuf is a reasonable choice (and Signal uses it successfully), Cap'n P
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What quicproquo gives up
|
## What quicprochat gives up
|
||||||
|
|
||||||
No design is without trade-offs. Compared to Signal and Matrix, quicproquo:
|
No design is without trade-offs. Compared to Signal and Matrix, quicprochat:
|
||||||
|
|
||||||
- **Has no federation.** A single server per deployment means no decentralized architecture. This is a deliberate simplification -- federation adds significant complexity and metadata exposure.
|
- **Has no federation.** A single server per deployment means no decentralized architecture. This is a deliberate simplification -- federation adds significant complexity and metadata exposure.
|
||||||
- **Is less mature.** Signal and Matrix have years of production hardening, formal security audits, and battle-tested implementations. quicproquo is in early development.
|
- **Is less mature.** Signal and Matrix have years of production hardening, formal security audits, and battle-tested implementations. quicprochat is in early development.
|
||||||
- **Has a smaller ecosystem.** Signal and Matrix have extensive client libraries, bridges, and integrations. quicproquo is a standalone Rust implementation.
|
- **Has a smaller ecosystem.** Signal and Matrix have extensive client libraries, bridges, and integrations. quicprochat is a standalone Rust implementation.
|
||||||
- **Requires MLS client complexity.** MLS clients must maintain a ratchet tree, process Commits, and handle epoch transitions. This is more complex than a simple symmetric ratchet (Sender Keys / Megolm), though the complexity buys post-compromise security.
|
- **Requires MLS client complexity.** MLS clients must maintain a ratchet tree, process Commits, and handle epoch transitions. This is more complex than a simple symmetric ratchet (Sender Keys / Megolm), though the complexity buys post-compromise security.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -158,6 +158,6 @@ No design is without trade-offs. Compared to Signal and Matrix, quicproquo:
|
|||||||
|
|
||||||
- [Design Decisions Overview](overview.md) -- index of all ADRs
|
- [Design Decisions Overview](overview.md) -- index of all ADRs
|
||||||
- [ADR-002: Cap'n Proto over MessagePack](adr-002-capnproto.md) -- serialisation format choice
|
- [ADR-002: Cap'n Proto over MessagePack](adr-002-capnproto.md) -- serialisation format choice
|
||||||
- [Protocol Layers Overview](../protocol-layers/overview.md) -- how quicproquo's layers compose
|
- [Protocol Layers Overview](../protocol-layers/overview.md) -- how quicprochat's layers compose
|
||||||
- [MLS (RFC 9420)](../protocol-layers/mls.md) -- deep dive into the MLS protocol layer
|
- [MLS (RFC 9420)](../protocol-layers/mls.md) -- deep dive into the MLS protocol layer
|
||||||
- [Architecture Overview](../architecture/overview.md) -- system-level architecture
|
- [Architecture Overview](../architecture/overview.md) -- system-level architecture
|
||||||
|
|||||||
@@ -22,15 +22,15 @@ This compiles all nine crates in the workspace:
|
|||||||
|
|
||||||
| Crate | Type | Purpose |
|
| Crate | Type | Purpose |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `quicproquo-core` | library | Crypto primitives, MLS `GroupMember` state machine, hybrid KEM |
|
| `quicprochat-core` | library | Crypto primitives, MLS `GroupMember` state machine, hybrid KEM |
|
||||||
| `quicproquo-proto` | library | Protobuf schemas (prost), generated types, method ID constants |
|
| `quicprochat-proto` | library | Protobuf schemas (prost), generated types, method ID constants |
|
||||||
| `quicproquo-kt` | library | Key Transparency Merkle log |
|
| `quicprochat-kt` | library | Key Transparency Merkle log |
|
||||||
| `quicproquo-plugin-api` | library | `#![no_std]` C-ABI plugin interface (`HookVTable`) |
|
| `quicprochat-plugin-api` | library | `#![no_std]` C-ABI plugin interface (`HookVTable`) |
|
||||||
| `quicproquo-rpc` | library | QUIC RPC framing, server dispatcher, client, Tower middleware |
|
| `quicprochat-rpc` | library | QUIC RPC framing, server dispatcher, client, Tower middleware |
|
||||||
| `quicproquo-sdk` | library | `QpqClient`, event broadcast, `ConversationStore` |
|
| `quicprochat-sdk` | library | `QpqClient`, event broadcast, `ConversationStore` |
|
||||||
| `quicproquo-server` | binary | Unified Authentication + Delivery Service (`qpq-server`) |
|
| `quicprochat-server` | binary | Unified Authentication + Delivery Service (`qpc-server`) |
|
||||||
| `quicproquo-client` | binary | CLI client (`qpq`) with REPL and subcommands |
|
| `quicprochat-client` | binary | CLI client (`qpc`) with REPL and subcommands |
|
||||||
| `quicproquo-p2p` | library | iroh P2P layer (compiled when the `mesh` feature is enabled) |
|
| `quicprochat-p2p` | library | iroh P2P layer (compiled when the `mesh` feature is enabled) |
|
||||||
|
|
||||||
For a release build with LTO, symbol stripping, and single codegen unit:
|
For a release build with LTO, symbol stripping, and single codegen unit:
|
||||||
|
|
||||||
@@ -61,11 +61,11 @@ A `justfile` at the repository root provides shortcuts for common tasks:
|
|||||||
| `just lint` | `cargo clippy --workspace -- -D warnings` | Check for warnings (CI-strict) |
|
| `just lint` | `cargo clippy --workspace -- -D warnings` | Check for warnings (CI-strict) |
|
||||||
| `just fmt` | `cargo fmt --all -- --check` | Check formatting |
|
| `just fmt` | `cargo fmt --all -- --check` | Check formatting |
|
||||||
| `just fmt-fix` | `cargo fmt --all` | Auto-format |
|
| `just fmt-fix` | `cargo fmt --all` | Auto-format |
|
||||||
| `just proto` | `cargo build -p quicproquo-proto` | Trigger Protobuf codegen |
|
| `just proto` | `cargo build -p quicprochat-proto` | Trigger Protobuf codegen |
|
||||||
| `just rpc` | `cargo build -p quicproquo-rpc` | Build RPC framework only |
|
| `just rpc` | `cargo build -p quicprochat-rpc` | Build RPC framework only |
|
||||||
| `just sdk` | `cargo build -p quicproquo-sdk` | Build client SDK only |
|
| `just sdk` | `cargo build -p quicprochat-sdk` | Build client SDK only |
|
||||||
| `just server` | `cargo build -p quicproquo-server` | Build server only |
|
| `just server` | `cargo build -p quicprochat-server` | Build server only |
|
||||||
| `just client` | `cargo build -p quicproquo-client` | Build CLI client only |
|
| `just client` | `cargo build -p quicprochat-client` | Build CLI client only |
|
||||||
| `just clean` | `cargo clean` | Remove build artifacts |
|
| `just clean` | `cargo clean` | Remove build artifacts |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -88,16 +88,16 @@ cargo test --workspace -- --test-threads 1
|
|||||||
To run tests for a single crate:
|
To run tests for a single crate:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo test -p quicproquo-core
|
cargo test -p quicprochat-core
|
||||||
cargo test -p quicproquo-server
|
cargo test -p quicprochat-server
|
||||||
cargo test -p quicproquo-rpc
|
cargo test -p quicprochat-rpc
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Protobuf code generation
|
## Protobuf code generation
|
||||||
|
|
||||||
The `quicproquo-proto` crate does not contain hand-written Rust types for wire messages. Instead, its `build.rs` script uses `prost-build` to generate Rust source from the `.proto` schema files in `proto/qpq/v1/`.
|
The `quicprochat-proto` crate does not contain hand-written Rust types for wire messages. Instead, its `build.rs` script uses `prost-build` to generate Rust source from the `.proto` schema files in `proto/qpc/v1/`.
|
||||||
|
|
||||||
### How it works
|
### How it works
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ The `quicproquo-proto` crate does not contain hand-written Rust types for wire m
|
|||||||
|
|
||||||
The `build.rs` script emits `cargo:rerun-if-changed` directives for each `.proto` file. Modifying a schema triggers automatic re-generation on the next `cargo build`.
|
The `build.rs` script emits `cargo:rerun-if-changed` directives for each `.proto` file. Modifying a schema triggers automatic re-generation on the next `cargo build`.
|
||||||
|
|
||||||
### Design constraints of `quicproquo-proto`
|
### Design constraints of `quicprochat-proto`
|
||||||
|
|
||||||
The proto crate is intentionally restricted:
|
The proto crate is intentionally restricted:
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ xcode-select --install
|
|||||||
Run E2E tests with `--test-threads 1` to serialise the tests and avoid bind conflicts on the shared test port:
|
Run E2E tests with `--test-threads 1` to serialise the tests and avoid bind conflicts on the shared test port:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo test -p quicproquo-client --test e2e -- --test-threads 1
|
cargo test -p quicprochat-client --test e2e -- --test-threads 1
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Certificate lifecycle and CA-signed TLS
|
# Certificate lifecycle and CA-signed TLS
|
||||||
|
|
||||||
This page describes how to use CA-issued certificates with quicproquo and how to think about certificate pinning, rotation, and lifecycle.
|
This page describes how to use CA-issued certificates with quicprochat and how to think about certificate pinning, rotation, and lifecycle.
|
||||||
|
|
||||||
For basic server TLS setup (self-signed certs, generation), see [Running the Server](running-the-server.md#tls-certificate-handling).
|
For basic server TLS setup (self-signed certs, generation), see [Running the Server](running-the-server.md#tls-certificate-handling).
|
||||||
|
|
||||||
@@ -8,8 +8,8 @@ For basic server TLS setup (self-signed certs, generation), see [Running the Ser
|
|||||||
|
|
||||||
## Current behaviour
|
## Current behaviour
|
||||||
|
|
||||||
- **Server:** Uses a single TLS certificate and private key (DER format). If the files are missing and the server is not in production mode, it generates a self-signed certificate. Production mode (`QPQ_PRODUCTION=1`) requires existing cert and key files.
|
- **Server:** Uses a single TLS certificate and private key (DER format). If the files are missing and the server is not in production mode, it generates a self-signed certificate. Production mode (`QPC_PRODUCTION=1`) requires existing cert and key files.
|
||||||
- **Client:** Trusts exactly the roots in the file given by `--ca-cert` (or `QPQ_CA_CERT`). Typically this is the server's own certificate (pinning) or a CA that signed the server cert.
|
- **Client:** Trusts exactly the roots in the file given by `--ca-cert` (or `QPC_CA_CERT`). Typically this is the server's own certificate (pinning) or a CA that signed the server cert.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ To pin the server so the client only connects to that server:
|
|||||||
1. Copy the server's certificate file (e.g. `data/server-cert.der`) from the server (or your deployment).
|
1. Copy the server's certificate file (e.g. `data/server-cert.der`) from the server (or your deployment).
|
||||||
2. Use that file as the client's CA cert:
|
2. Use that file as the client's CA cert:
|
||||||
```bash
|
```bash
|
||||||
qpq --ca-cert /path/to/server-cert.der ...
|
qpc --ca-cert /path/to/server-cert.der ...
|
||||||
```
|
```
|
||||||
3. The client will only accept a connection if the server presents that exact certificate (or a chain ending in it). No separate CA bundle is required.
|
3. The client will only accept a connection if the server presents that exact certificate (or a chain ending in it). No separate CA bundle is required.
|
||||||
|
|
||||||
@@ -43,12 +43,12 @@ To use a certificate issued by a public CA (e.g. Let's Encrypt):
|
|||||||
```
|
```
|
||||||
2. **Configure the server** to use those paths:
|
2. **Configure the server** to use those paths:
|
||||||
```bash
|
```bash
|
||||||
export QPQ_TLS_CERT=/etc/quicproquo/server-cert.der
|
export QPC_TLS_CERT=/etc/quicprochat/server-cert.der
|
||||||
export QPQ_TLS_KEY=/etc/quicproquo/server-key.der
|
export QPC_TLS_KEY=/etc/quicprochat/server-key.der
|
||||||
```
|
```
|
||||||
3. **Configure the client** to trust the CA that signed the server cert. Use the CA’s certificate (or the CA bundle) as `--ca-cert`:
|
3. **Configure the client** to trust the CA that signed the server cert. Use the CA’s certificate (or the CA bundle) as `--ca-cert`:
|
||||||
```bash
|
```bash
|
||||||
qpq --ca-cert /etc/ssl/certs/your-ca.der --server-name your.server.example ...
|
qpc --ca-cert /etc/ssl/certs/your-ca.der --server-name your.server.example ...
|
||||||
```
|
```
|
||||||
The `--server-name` must match the certificate’s SAN (e.g. DNS name).
|
The `--server-name` must match the certificate’s SAN (e.g. DNS name).
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ To use a certificate issued by a public CA (e.g. Let's Encrypt):
|
|||||||
|
|
||||||
- **Manual rotation:** Replace `server-cert.der` and `server-key.der` on disk, then restart the server. Clients that pin the new cert must be updated with the new cert file.
|
- **Manual rotation:** Replace `server-cert.der` and `server-key.der` on disk, then restart the server. Clients that pin the new cert must be updated with the new cert file.
|
||||||
- **Let’s Encrypt renewal:** After renewing (e.g. via certbot), convert the new cert and key to DER, replace the files, and restart the server. If clients use the CA cert (e.g. ISRG Root X1) as `--ca-cert`, they do not need updates when the server cert is renewed.
|
- **Let’s Encrypt renewal:** After renewing (e.g. via certbot), convert the new cert and key to DER, replace the files, and restart the server. If clients use the CA cert (e.g. ISRG Root X1) as `--ca-cert`, they do not need updates when the server cert is renewed.
|
||||||
- **OCSP / CRL:** The quicproquo server does not currently perform OCSP stapling or CRL checks. Revocation is handled by the client or by operational procedures (e.g. short-lived certs, rotation on compromise).
|
- **OCSP / CRL:** The quicprochat server does not currently perform OCSP stapling or CRL checks. Revocation is handled by the client or by operational procedures (e.g. short-lived certs, rotation on compromise).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -70,6 +70,6 @@ To use a certificate issued by a public CA (e.g. Let's Encrypt):
|
|||||||
|------------------|-------------|--------------------|
|
|------------------|-------------|--------------------|
|
||||||
| Pinned (single server) | Self-signed or any | Server’s cert file |
|
| Pinned (single server) | Self-signed or any | Server’s cert file |
|
||||||
| CA-issued | Let’s Encrypt (or other CA) | CA cert (or bundle) |
|
| CA-issued | Let’s Encrypt (or other CA) | CA cert (or bundle) |
|
||||||
| Production | Always use existing cert/key; set `QPQ_PRODUCTION=1` | CA or pinned server cert |
|
| Production | Always use existing cert/key; set `QPC_PRODUCTION=1` | CA or pinned server cert |
|
||||||
|
|
||||||
For production, prefer either (a) certificate pinning with the server’s cert or (b) a CA-issued server cert with clients trusting the CA, and plan for rotation and restart (or future reload support).
|
For production, prefer either (a) certificate pinning with the server’s cert or (b) a CA-issued server cert with clients trusting the CA, and plan for rotation and restart (or future reload support).
|
||||||
|
|||||||
@@ -75,13 +75,13 @@ You will need **three terminal windows**: one for the server, one for Alice, and
|
|||||||
In **Terminal 1** (Server):
|
In **Terminal 1** (Server):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-server
|
cargo run -p quicprochat-server
|
||||||
```
|
```
|
||||||
|
|
||||||
Wait for the log line confirming it is accepting connections:
|
Wait for the log line confirming it is accepting connections:
|
||||||
|
|
||||||
```
|
```
|
||||||
INFO quicproquo_server: accepting QUIC connections addr="0.0.0.0:7000"
|
INFO quicprochat_server: accepting QUIC connections addr="0.0.0.0:7000"
|
||||||
```
|
```
|
||||||
|
|
||||||
If this is the first run, you will also see a log line about generating the self-signed TLS certificate. The certificate is written to `data/server-cert.der`, which the client will use for TLS verification.
|
If this is the first run, you will also see a log line about generating the self-signed TLS certificate. The certificate is written to `data/server-cert.der`, which the client will use for TLS verification.
|
||||||
@@ -91,7 +91,7 @@ If this is the first run, you will also see a log line about generating the self
|
|||||||
In **Terminal 2** (Alice):
|
In **Terminal 2** (Alice):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- register-state \
|
cargo run -p quicprochat-client -- register-state \
|
||||||
--state alice.bin \
|
--state alice.bin \
|
||||||
--server 127.0.0.1:7000
|
--server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
@@ -116,7 +116,7 @@ KeyPackage uploaded successfully.
|
|||||||
In **Terminal 3** (Bob):
|
In **Terminal 3** (Bob):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- register-state \
|
cargo run -p quicprochat-client -- register-state \
|
||||||
--state bob.bin \
|
--state bob.bin \
|
||||||
--server 127.0.0.1:7000
|
--server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
@@ -137,7 +137,7 @@ In **Terminal 2** (Alice):
|
|||||||
First, create the group:
|
First, create the group:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- create-group \
|
cargo run -p quicprochat-client -- create-group \
|
||||||
--state alice.bin \
|
--state alice.bin \
|
||||||
--group-id "demo-chat"
|
--group-id "demo-chat"
|
||||||
```
|
```
|
||||||
@@ -151,7 +151,7 @@ Alice is now the sole member of the group at epoch 0.
|
|||||||
Next, invite Bob using his identity key from Step 3:
|
Next, invite Bob using his identity key from Step 3:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- invite \
|
cargo run -p quicprochat-client -- invite \
|
||||||
--state alice.bin \
|
--state alice.bin \
|
||||||
--peer-key <BOB_KEY> \
|
--peer-key <BOB_KEY> \
|
||||||
--server 127.0.0.1:7000
|
--server 127.0.0.1:7000
|
||||||
@@ -173,7 +173,7 @@ Alice's group state has now advanced to epoch 1.
|
|||||||
In **Terminal 3** (Bob):
|
In **Terminal 3** (Bob):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- join \
|
cargo run -p quicprochat-client -- join \
|
||||||
--state bob.bin \
|
--state bob.bin \
|
||||||
--server 127.0.0.1:7000
|
--server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
@@ -195,7 +195,7 @@ Bob is now a member of the group at epoch 1, sharing the same group secret as Al
|
|||||||
In **Terminal 2** (Alice):
|
In **Terminal 2** (Alice):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- send \
|
cargo run -p quicprochat-client -- send \
|
||||||
--state alice.bin \
|
--state alice.bin \
|
||||||
--peer-key <BOB_KEY> \
|
--peer-key <BOB_KEY> \
|
||||||
--msg "Hello Bob, this is encrypted with MLS!" \
|
--msg "Hello Bob, this is encrypted with MLS!" \
|
||||||
@@ -215,7 +215,7 @@ message sent
|
|||||||
In **Terminal 3** (Bob):
|
In **Terminal 3** (Bob):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- recv \
|
cargo run -p quicprochat-client -- recv \
|
||||||
--state bob.bin \
|
--state bob.bin \
|
||||||
--server 127.0.0.1:7000
|
--server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
@@ -233,7 +233,7 @@ This command:
|
|||||||
In **Terminal 3** (Bob):
|
In **Terminal 3** (Bob):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- send \
|
cargo run -p quicprochat-client -- send \
|
||||||
--state bob.bin \
|
--state bob.bin \
|
||||||
--peer-key <ALICE_KEY> \
|
--peer-key <ALICE_KEY> \
|
||||||
--msg "Hi Alice, received loud and clear!" \
|
--msg "Hi Alice, received loud and clear!" \
|
||||||
@@ -249,7 +249,7 @@ message sent
|
|||||||
In **Terminal 2** (Alice):
|
In **Terminal 2** (Alice):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- recv \
|
cargo run -p quicprochat-client -- recv \
|
||||||
--state alice.bin \
|
--state alice.bin \
|
||||||
--server 127.0.0.1:7000
|
--server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
@@ -266,7 +266,7 @@ If you want to see the entire flow in a single command without managing three te
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Ensure the server is running, then:
|
# Ensure the server is running, then:
|
||||||
cargo run -p quicproquo-client -- demo-group --server 127.0.0.1:7000
|
cargo run -p quicprochat-client -- demo-group --server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -327,7 +327,7 @@ Ensure the client has access to the server's TLS certificate. By default, both s
|
|||||||
- [REPL Command Reference](repl-reference.md) -- complete list of 40+ slash commands
|
- [REPL Command Reference](repl-reference.md) -- complete list of 40+ slash commands
|
||||||
- [Rich Messaging](rich-messaging.md) -- reactions, typing indicators, edit/delete
|
- [Rich Messaging](rich-messaging.md) -- reactions, typing indicators, edit/delete
|
||||||
- [File Transfer](file-transfer.md) -- chunked upload/download with SHA-256 verification
|
- [File Transfer](file-transfer.md) -- chunked upload/download with SHA-256 verification
|
||||||
- [Go SDK](go-sdk.md) -- build Go applications against the qpq server
|
- [Go SDK](go-sdk.md) -- build Go applications against the qpc server
|
||||||
- [TypeScript SDK & Browser Demo](typescript-sdk.md) -- WASM crypto in the browser
|
- [TypeScript SDK & Browser Demo](typescript-sdk.md) -- WASM crypto in the browser
|
||||||
- [Mesh Networking](mesh-networking.md) -- P2P, broadcast channels, store-and-forward
|
- [Mesh Networking](mesh-networking.md) -- P2P, broadcast channels, store-and-forward
|
||||||
- [MLS (RFC 9420)](../protocol-layers/mls.md) -- how the MLS group operations work
|
- [MLS (RFC 9420)](../protocol-layers/mls.md) -- how the MLS group operations work
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Docker Deployment
|
# Docker Deployment
|
||||||
|
|
||||||
quicproquo includes a multi-stage Dockerfile and a Docker Compose configuration for building and running the server in containers.
|
quicprochat includes a multi-stage Dockerfile and a Docker Compose configuration for building and running the server in containers.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -40,11 +40,11 @@ services:
|
|||||||
- "7000:7000/udp"
|
- "7000:7000/udp"
|
||||||
environment:
|
environment:
|
||||||
RUST_LOG: "info"
|
RUST_LOG: "info"
|
||||||
QPQ_LISTEN: "0.0.0.0:7000"
|
QPC_LISTEN: "0.0.0.0:7000"
|
||||||
QPQ_DATA_DIR: "/var/lib/quicproquo"
|
QPC_DATA_DIR: "/var/lib/quicprochat"
|
||||||
QPQ_PRODUCTION: "true"
|
QPC_PRODUCTION: "true"
|
||||||
volumes:
|
volumes:
|
||||||
- server-data:/var/lib/quicproquo
|
- server-data:/var/lib/quicprochat
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
@@ -74,7 +74,7 @@ WORKDIR /build
|
|||||||
|
|
||||||
# Copy manifests first so dependency layers are cached independently of source.
|
# Copy manifests first so dependency layers are cached independently of source.
|
||||||
COPY Cargo.toml Cargo.lock ./
|
COPY Cargo.toml Cargo.lock ./
|
||||||
COPY crates/quicproquo-core/Cargo.toml crates/quicproquo-core/Cargo.toml
|
COPY crates/quicprochat-core/Cargo.toml crates/quicprochat-core/Cargo.toml
|
||||||
# ... (all 9 crate manifests)
|
# ... (all 9 crate manifests)
|
||||||
|
|
||||||
# Create dummy source files for dependency caching.
|
# Create dummy source files for dependency caching.
|
||||||
@@ -84,11 +84,11 @@ RUN mkdir -p ... && echo 'fn main() {}' > ...
|
|||||||
COPY schemas/ schemas/
|
COPY schemas/ schemas/
|
||||||
|
|
||||||
# Build dependencies only (cache layer).
|
# Build dependencies only (cache layer).
|
||||||
RUN cargo build --release --bin qpq-server 2>/dev/null || true
|
RUN cargo build --release --bin qpc-server 2>/dev/null || true
|
||||||
|
|
||||||
# Copy real source and build for real.
|
# Copy real source and build for real.
|
||||||
COPY crates/ crates/
|
COPY crates/ crates/
|
||||||
RUN cargo build --release --bin qpq-server
|
RUN cargo build --release --bin qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
Key steps:
|
Key steps:
|
||||||
@@ -96,7 +96,7 @@ Key steps:
|
|||||||
1. **Base image**: `rust:bookworm` (Debian Bookworm with the Rust toolchain pre-installed).
|
1. **Base image**: `rust:bookworm` (Debian Bookworm with the Rust toolchain pre-installed).
|
||||||
2. **No system compiler required**: Unlike v1, the builder stage does not install `capnproto`. The v2 Protobuf compiler is vendored by `protobuf-src` and compiled automatically as part of `cargo build`.
|
2. **No system compiler required**: Unlike v1, the builder stage does not install `capnproto`. The v2 Protobuf compiler is vendored by `protobuf-src` and compiled automatically as part of `cargo build`.
|
||||||
3. **Copy manifests first**: `Cargo.toml` and `Cargo.lock` are copied before source code with dummy stubs so that dependency compilation is cached in a separate Docker layer.
|
3. **Copy manifests first**: `Cargo.toml` and `Cargo.lock` are copied before source code with dummy stubs so that dependency compilation is cached in a separate Docker layer.
|
||||||
4. **Copy schemas**: The `schemas/` directory is copied before the dependency build because `quicproquo-proto/build.rs` references it.
|
4. **Copy schemas**: The `schemas/` directory is copied before the dependency build because `quicprochat-proto/build.rs` references it.
|
||||||
5. **Copy real source and build**: After the dependency cache layer, real source files are copied in and `cargo build --release` produces the final binary.
|
5. **Copy real source and build**: After the dependency cache layer, real source files are copied in and `cargo build --release` produces the final binary.
|
||||||
|
|
||||||
### Stage 2: Runtime (`debian:bookworm-slim`)
|
### Stage 2: Runtime (`debian:bookworm-slim`)
|
||||||
@@ -108,39 +108,39 @@ RUN apt-get update \
|
|||||||
&& apt-get install -y --no-install-recommends ca-certificates \
|
&& apt-get install -y --no-install-recommends ca-certificates \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY --from=builder /build/target/release/qpq-server /usr/local/bin/qpq-server
|
COPY --from=builder /build/target/release/qpc-server /usr/local/bin/qpc-server
|
||||||
|
|
||||||
RUN groupadd --system qpq \
|
RUN groupadd --system qpc \
|
||||||
&& useradd --system --gid qpq --no-create-home --shell /usr/sbin/nologin qpq \
|
&& useradd --system --gid qpc --no-create-home --shell /usr/sbin/nologin qpc \
|
||||||
&& mkdir -p /var/lib/quicproquo \
|
&& mkdir -p /var/lib/quicprochat \
|
||||||
&& chown qpq:qpq /var/lib/quicproquo
|
&& chown qpc:qpc /var/lib/quicprochat
|
||||||
|
|
||||||
EXPOSE 7000
|
EXPOSE 7000
|
||||||
|
|
||||||
VOLUME ["/var/lib/quicproquo"]
|
VOLUME ["/var/lib/quicprochat"]
|
||||||
|
|
||||||
ENV RUST_LOG=info \
|
ENV RUST_LOG=info \
|
||||||
QPQ_LISTEN=0.0.0.0:7000 \
|
QPC_LISTEN=0.0.0.0:7000 \
|
||||||
QPQ_DATA_DIR=/var/lib/quicproquo \
|
QPC_DATA_DIR=/var/lib/quicprochat \
|
||||||
QPQ_TLS_CERT=/var/lib/quicproquo/server-cert.der \
|
QPC_TLS_CERT=/var/lib/quicprochat/server-cert.der \
|
||||||
QPQ_TLS_KEY=/var/lib/quicproquo/server-key.der \
|
QPC_TLS_KEY=/var/lib/quicprochat/server-key.der \
|
||||||
QPQ_PRODUCTION=true
|
QPC_PRODUCTION=true
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
|
||||||
CMD test -f /var/lib/quicproquo/server-cert.der || exit 1
|
CMD test -f /var/lib/quicprochat/server-cert.der || exit 1
|
||||||
|
|
||||||
USER qpq
|
USER qpc
|
||||||
|
|
||||||
CMD ["qpq-server"]
|
CMD ["qpc-server"]
|
||||||
```
|
```
|
||||||
|
|
||||||
Key characteristics:
|
Key characteristics:
|
||||||
|
|
||||||
- **Minimal image**: No Rust toolchain, no build tools, no `protoc` binary.
|
- **Minimal image**: No Rust toolchain, no build tools, no `protoc` binary.
|
||||||
- **`ca-certificates`**: Included for future HTTPS calls (e.g., ACME certificate provisioning).
|
- **`ca-certificates`**: Included for future HTTPS calls (e.g., ACME certificate provisioning).
|
||||||
- **Dedicated user**: The container runs as the `qpq` system user (not `root`) for defense in depth.
|
- **Dedicated user**: The container runs as the `qpc` system user (not `root`) for defense in depth.
|
||||||
- **Named volume**: `/var/lib/quicproquo` is declared as a `VOLUME` for data persistence.
|
- **Named volume**: `/var/lib/quicprochat` is declared as a `VOLUME` for data persistence.
|
||||||
- **`QPQ_PRODUCTION=true`**: The runtime image defaults to production mode, requiring pre-existing TLS certificates and a strong auth token.
|
- **`QPC_PRODUCTION=true`**: The runtime image defaults to production mode, requiring pre-existing TLS certificates and a strong auth token.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ services:
|
|||||||
server:
|
server:
|
||||||
# ... existing config ...
|
# ... existing config ...
|
||||||
volumes:
|
volumes:
|
||||||
- server-data:/var/lib/quicproquo
|
- server-data:/var/lib/quicprochat
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
server-data:
|
server-data:
|
||||||
@@ -165,13 +165,13 @@ Or use a bind mount for easier inspection:
|
|||||||
mkdir -p ./server-data
|
mkdir -p ./server-data
|
||||||
|
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name quicproquo \
|
--name quicprochat \
|
||||||
-p 7000:7000/udp \
|
-p 7000:7000/udp \
|
||||||
-v "$(pwd)/server-data:/var/lib/quicproquo" \
|
-v "$(pwd)/server-data:/var/lib/quicprochat" \
|
||||||
-e QPQ_ALLOW_INSECURE_AUTH=true \
|
-e QPC_ALLOW_INSECURE_AUTH=true \
|
||||||
-e QPQ_PRODUCTION=false \
|
-e QPC_PRODUCTION=false \
|
||||||
-e RUST_LOG=info \
|
-e RUST_LOG=info \
|
||||||
qpq-server
|
qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
Without a volume, all server state (including TLS certificates and message queues) is lost when the container is removed. The server will generate a new self-signed certificate on each fresh start, which means clients will need the new certificate to connect.
|
Without a volume, all server state (including TLS certificates and message queues) is lost when the container is removed. The server will generate a new self-signed certificate on each fresh start, which means clients will need the new certificate to connect.
|
||||||
@@ -183,19 +183,19 @@ Without a volume, all server state (including TLS certificates and message queue
|
|||||||
To build the Docker image without starting a container:
|
To build the Docker image without starting a container:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker build -t qpq-server -f docker/Dockerfile .
|
docker build -t qpc-server -f docker/Dockerfile .
|
||||||
```
|
```
|
||||||
|
|
||||||
To run it in development mode (without production validation):
|
To run it in development mode (without production validation):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name quicproquo \
|
--name quicprochat \
|
||||||
-p 7000:7000/udp \
|
-p 7000:7000/udp \
|
||||||
-e QPQ_ALLOW_INSECURE_AUTH=true \
|
-e QPC_ALLOW_INSECURE_AUTH=true \
|
||||||
-e QPQ_PRODUCTION=false \
|
-e QPC_PRODUCTION=false \
|
||||||
-e RUST_LOG=info \
|
-e RUST_LOG=info \
|
||||||
qpq-server
|
qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -206,15 +206,15 @@ When the server runs in Docker with `docker compose up`, the client can connect
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Extract the server's TLS cert from the container volume
|
# Extract the server's TLS cert from the container volume
|
||||||
docker compose cp server:/var/lib/quicproquo/server-cert.der ./data/server-cert.der
|
docker compose cp server:/var/lib/quicprochat/server-cert.der ./data/server-cert.der
|
||||||
|
|
||||||
# Connect
|
# Connect
|
||||||
cargo run -p quicproquo-client -- ping \
|
cargo run -p quicprochat-client -- ping \
|
||||||
--ca-cert ./data/server-cert.der \
|
--ca-cert ./data/server-cert.der \
|
||||||
--server-name localhost
|
--server-name localhost
|
||||||
```
|
```
|
||||||
|
|
||||||
If you mounted a bind volume (e.g., `./server-data:/var/lib/quicproquo`), the certificate is directly accessible at `./server-data/server-cert.der`.
|
If you mounted a bind volume (e.g., `./server-data:/var/lib/quicprochat`), the certificate is directly accessible at `./server-data/server-cert.der`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# C FFI Bindings
|
# C FFI Bindings
|
||||||
|
|
||||||
The `quicproquo-ffi` crate provides a synchronous C API for the quicproquo
|
The `quicprochat-ffi` crate provides a synchronous C API for the quicprochat
|
||||||
messaging client. It wraps the async `quicproquo-client` library behind an
|
messaging client. It wraps the async `quicprochat-client` library behind an
|
||||||
opaque handle, so C, Python, Swift, or any language with C FFI support can
|
opaque handle, so C, Python, Swift, or any language with C FFI support can
|
||||||
connect, authenticate, send messages, and receive messages.
|
connect, authenticate, send messages, and receive messages.
|
||||||
|
|
||||||
@@ -13,26 +13,26 @@ internals.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Shared library (.so / .dylib / .dll) + static archive (.a)
|
# Shared library (.so / .dylib / .dll) + static archive (.a)
|
||||||
cargo build --release -p quicproquo-ffi
|
cargo build --release -p quicprochat-ffi
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
| Platform | Shared library | Static library |
|
| Platform | Shared library | Static library |
|
||||||
|----------|---------------------------------------------|-----------------------------------------|
|
|----------|---------------------------------------------|-----------------------------------------|
|
||||||
| Linux | `target/release/libquicproquo_ffi.so` | `target/release/libquicproquo_ffi.a` |
|
| Linux | `target/release/libquicprochat_ffi.so` | `target/release/libquicprochat_ffi.a` |
|
||||||
| macOS | `target/release/libquicproquo_ffi.dylib` | `target/release/libquicproquo_ffi.a` |
|
| macOS | `target/release/libquicprochat_ffi.dylib` | `target/release/libquicprochat_ffi.a` |
|
||||||
| Windows | `target/release/quicproquo_ffi.dll` | `target/release/quicproquo_ffi.lib` |
|
| Windows | `target/release/quicprochat_ffi.dll` | `target/release/quicprochat_ffi.lib` |
|
||||||
|
|
||||||
## Status Codes
|
## Status Codes
|
||||||
|
|
||||||
| Code | Constant | Meaning |
|
| Code | Constant | Meaning |
|
||||||
|------|--------------------|------------------------------------------|
|
|------|--------------------|------------------------------------------|
|
||||||
| 0 | `QPQ_OK` | Success |
|
| 0 | `QPC_OK` | Success |
|
||||||
| 1 | `QPQ_ERROR` | Generic error (check `qpq_last_error`) |
|
| 1 | `QPC_ERROR` | Generic error (check `qpc_last_error`) |
|
||||||
| 2 | `QPQ_AUTH_FAILED` | OPAQUE authentication failed |
|
| 2 | `QPC_AUTH_FAILED` | OPAQUE authentication failed |
|
||||||
| 3 | `QPQ_TIMEOUT` | Receive timed out with no messages |
|
| 3 | `QPC_TIMEOUT` | Receive timed out with no messages |
|
||||||
| 4 | `QPQ_NOT_CONNECTED`| Handle is null or not logged in |
|
| 4 | `QPC_NOT_CONNECTED`| Handle is null or not logged in |
|
||||||
|
|
||||||
## C API Reference
|
## C API Reference
|
||||||
|
|
||||||
@@ -40,10 +40,10 @@ All functions use the `extern "C"` calling convention. All string parameters
|
|||||||
must be valid, non-null, null-terminated UTF-8. The opaque handle type is
|
must be valid, non-null, null-terminated UTF-8. The opaque handle type is
|
||||||
`QpqHandle *`.
|
`QpqHandle *`.
|
||||||
|
|
||||||
### `qpq_connect`
|
### `qpc_connect`
|
||||||
|
|
||||||
```c
|
```c
|
||||||
QpqHandle *qpq_connect(
|
QpqHandle *qpc_connect(
|
||||||
const char *server, /* "host:port", e.g. "127.0.0.1:7000" */
|
const char *server, /* "host:port", e.g. "127.0.0.1:7000" */
|
||||||
const char *ca_cert, /* path to CA certificate file (DER) */
|
const char *ca_cert, /* path to CA certificate file (DER) */
|
||||||
const char *server_name /* TLS server name, e.g. "localhost" */
|
const char *server_name /* TLS server name, e.g. "localhost" */
|
||||||
@@ -54,11 +54,11 @@ Creates a Tokio runtime, performs a health check against the server, and
|
|||||||
returns a heap-allocated opaque handle. Returns `NULL` on failure (invalid
|
returns a heap-allocated opaque handle. Returns `NULL` on failure (invalid
|
||||||
arguments, server unreachable, or runtime creation failed).
|
arguments, server unreachable, or runtime creation failed).
|
||||||
|
|
||||||
### `qpq_login`
|
### `qpc_login`
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int32_t qpq_login(
|
int32_t qpc_login(
|
||||||
QpqHandle *handle, /* handle from qpq_connect */
|
QpqHandle *handle, /* handle from qpc_connect */
|
||||||
const char *username, /* OPAQUE username */
|
const char *username, /* OPAQUE username */
|
||||||
const char *password /* OPAQUE password */
|
const char *password /* OPAQUE password */
|
||||||
);
|
);
|
||||||
@@ -66,16 +66,16 @@ int32_t qpq_login(
|
|||||||
|
|
||||||
Authenticates with the server using OPAQUE (password-authenticated key
|
Authenticates with the server using OPAQUE (password-authenticated key
|
||||||
exchange). On success the handle is marked as logged-in and subsequent
|
exchange). On success the handle is marked as logged-in and subsequent
|
||||||
`qpq_send`/`qpq_receive` calls use the authenticated session.
|
`qpc_send`/`qpc_receive` calls use the authenticated session.
|
||||||
|
|
||||||
**Returns:** `QPQ_OK` on success, `QPQ_AUTH_FAILED` on bad credentials,
|
**Returns:** `QPC_OK` on success, `QPC_AUTH_FAILED` on bad credentials,
|
||||||
`QPQ_NOT_CONNECTED` if the handle is null, or `QPQ_ERROR` on other failures.
|
`QPC_NOT_CONNECTED` if the handle is null, or `QPC_ERROR` on other failures.
|
||||||
|
|
||||||
### `qpq_send`
|
### `qpc_send`
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int32_t qpq_send(
|
int32_t qpc_send(
|
||||||
QpqHandle *handle, /* handle from qpq_connect */
|
QpqHandle *handle, /* handle from qpc_connect */
|
||||||
const char *recipient, /* recipient username (null-terminated) */
|
const char *recipient, /* recipient username (null-terminated) */
|
||||||
const uint8_t *message, /* message bytes (UTF-8, not null-terminated) */
|
const uint8_t *message, /* message bytes (UTF-8, not null-terminated) */
|
||||||
size_t message_len /* length of message in bytes */
|
size_t message_len /* length of message in bytes */
|
||||||
@@ -86,14 +86,14 @@ Resolves the recipient by username, then sends an MLS-encrypted message
|
|||||||
through the server. The `message` buffer must contain valid UTF-8 of at least
|
through the server. The `message` buffer must contain valid UTF-8 of at least
|
||||||
`message_len` bytes. The handle must be logged in.
|
`message_len` bytes. The handle must be logged in.
|
||||||
|
|
||||||
**Returns:** `QPQ_OK` on success, `QPQ_NOT_CONNECTED` if not logged in, or
|
**Returns:** `QPC_OK` on success, `QPC_NOT_CONNECTED` if not logged in, or
|
||||||
`QPQ_ERROR` on failure (recipient not found, network error, etc.).
|
`QPC_ERROR` on failure (recipient not found, network error, etc.).
|
||||||
|
|
||||||
### `qpq_receive`
|
### `qpc_receive`
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int32_t qpq_receive(
|
int32_t qpc_receive(
|
||||||
QpqHandle *handle, /* handle from qpq_connect */
|
QpqHandle *handle, /* handle from qpc_connect */
|
||||||
uint32_t timeout_ms, /* maximum wait time in milliseconds */
|
uint32_t timeout_ms, /* maximum wait time in milliseconds */
|
||||||
char **out_json /* output: heap-allocated JSON string */
|
char **out_json /* output: heap-allocated JSON string */
|
||||||
);
|
);
|
||||||
@@ -102,48 +102,48 @@ int32_t qpq_receive(
|
|||||||
Blocks up to `timeout_ms` milliseconds waiting for pending messages. On
|
Blocks up to `timeout_ms` milliseconds waiting for pending messages. On
|
||||||
success, `*out_json` points to a null-terminated JSON string containing an
|
success, `*out_json` points to a null-terminated JSON string containing an
|
||||||
array of decrypted message strings (e.g., `["hello","world"]`). The caller
|
array of decrypted message strings (e.g., `["hello","world"]`). The caller
|
||||||
**must** free this string with `qpq_free_string`.
|
**must** free this string with `qpc_free_string`.
|
||||||
|
|
||||||
**Returns:** `QPQ_OK` on success (even if the array is empty),
|
**Returns:** `QPC_OK` on success (even if the array is empty),
|
||||||
`QPQ_TIMEOUT` if the wait expires with no messages, `QPQ_NOT_CONNECTED` if
|
`QPC_TIMEOUT` if the wait expires with no messages, `QPC_NOT_CONNECTED` if
|
||||||
not logged in, or `QPQ_ERROR` on failure.
|
not logged in, or `QPC_ERROR` on failure.
|
||||||
|
|
||||||
### `qpq_disconnect`
|
### `qpc_disconnect`
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void qpq_disconnect(QpqHandle *handle);
|
void qpc_disconnect(QpqHandle *handle);
|
||||||
```
|
```
|
||||||
|
|
||||||
Shuts down the Tokio runtime and frees the handle. After this call, the
|
Shuts down the Tokio runtime and frees the handle. After this call, the
|
||||||
handle must not be used again. Passing `NULL` is a safe no-op.
|
handle must not be used again. Passing `NULL` is a safe no-op.
|
||||||
|
|
||||||
### `qpq_last_error`
|
### `qpc_last_error`
|
||||||
|
|
||||||
```c
|
```c
|
||||||
const char *qpq_last_error(const QpqHandle *handle);
|
const char *qpc_last_error(const QpqHandle *handle);
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns the last error message recorded on the handle, or `NULL` if no error
|
Returns the last error message recorded on the handle, or `NULL` if no error
|
||||||
has occurred. The returned pointer is valid **only** until the next FFI call
|
has occurred. The returned pointer is valid **only** until the next FFI call
|
||||||
on the same handle. Do **not** free this pointer -- it is owned by the handle.
|
on the same handle. Do **not** free this pointer -- it is owned by the handle.
|
||||||
|
|
||||||
### `qpq_free_string`
|
### `qpc_free_string`
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void qpq_free_string(char *ptr);
|
void qpc_free_string(char *ptr);
|
||||||
```
|
```
|
||||||
|
|
||||||
Frees a string previously returned by `qpq_receive` via the `out_json`
|
Frees a string previously returned by `qpc_receive` via the `out_json`
|
||||||
output parameter. Passing `NULL` is a safe no-op. Do **not** use this to
|
output parameter. Passing `NULL` is a safe no-op. Do **not** use this to
|
||||||
free strings from `qpq_last_error`.
|
free strings from `qpc_last_error`.
|
||||||
|
|
||||||
## Memory Management Rules
|
## Memory Management Rules
|
||||||
|
|
||||||
1. **`QpqHandle`** is heap-allocated by `qpq_connect` and freed by
|
1. **`QpqHandle`** is heap-allocated by `qpc_connect` and freed by
|
||||||
`qpq_disconnect`. Do not use the handle after disconnecting.
|
`qpc_disconnect`. Do not use the handle after disconnecting.
|
||||||
2. **`out_json` from `qpq_receive`** is heap-allocated. Free it with
|
2. **`out_json` from `qpc_receive`** is heap-allocated. Free it with
|
||||||
`qpq_free_string`.
|
`qpc_free_string`.
|
||||||
3. **`qpq_last_error`** returns a pointer owned by the handle. Do not free
|
3. **`qpc_last_error`** returns a pointer owned by the handle. Do not free
|
||||||
it; it is valid until the next FFI call on the same handle.
|
it; it is valid until the next FFI call on the same handle.
|
||||||
4. All `const char *` input parameters are borrowed for the duration of the
|
4. All `const char *` input parameters are borrowed for the duration of the
|
||||||
call and not stored beyond it.
|
call and not stored beyond it.
|
||||||
@@ -154,11 +154,11 @@ Every function that returns `int32_t` uses the status codes above. The
|
|||||||
recommended pattern is:
|
recommended pattern is:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int rc = qpq_login(handle, "alice", "password123");
|
int rc = qpc_login(handle, "alice", "password123");
|
||||||
if (rc != QPQ_OK) {
|
if (rc != QPC_OK) {
|
||||||
const char *err = qpq_last_error(handle);
|
const char *err = qpc_last_error(handle);
|
||||||
fprintf(stderr, "login failed (code %d): %s\n", rc, err ? err : "unknown");
|
fprintf(stderr, "login failed (code %d): %s\n", rc, err ? err : "unknown");
|
||||||
qpq_disconnect(handle);
|
qpc_disconnect(handle);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -169,48 +169,48 @@ if (rc != QPQ_OK) {
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* Link with: -lquicproquo_ffi -lpthread -ldl -lm */
|
/* Link with: -lquicprochat_ffi -lpthread -ldl -lm */
|
||||||
|
|
||||||
typedef struct QpqHandle QpqHandle;
|
typedef struct QpqHandle QpqHandle;
|
||||||
|
|
||||||
extern QpqHandle *qpq_connect(const char *, const char *, const char *);
|
extern QpqHandle *qpc_connect(const char *, const char *, const char *);
|
||||||
extern int qpq_login(QpqHandle *, const char *, const char *);
|
extern int qpc_login(QpqHandle *, const char *, const char *);
|
||||||
extern int qpq_send(QpqHandle *, const char *, const unsigned char *, unsigned long);
|
extern int qpc_send(QpqHandle *, const char *, const unsigned char *, unsigned long);
|
||||||
extern int qpq_receive(QpqHandle *, unsigned int, char **);
|
extern int qpc_receive(QpqHandle *, unsigned int, char **);
|
||||||
extern void qpq_disconnect(QpqHandle *);
|
extern void qpc_disconnect(QpqHandle *);
|
||||||
extern const char *qpq_last_error(const QpqHandle *);
|
extern const char *qpc_last_error(const QpqHandle *);
|
||||||
extern void qpq_free_string(char *);
|
extern void qpc_free_string(char *);
|
||||||
|
|
||||||
#define QPQ_OK 0
|
#define QPC_OK 0
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
QpqHandle *h = qpq_connect("127.0.0.1:7000", "server-cert.der", "localhost");
|
QpqHandle *h = qpc_connect("127.0.0.1:7000", "server-cert.der", "localhost");
|
||||||
if (!h) {
|
if (!h) {
|
||||||
fprintf(stderr, "connection failed\n");
|
fprintf(stderr, "connection failed\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qpq_login(h, "alice", "secret") != QPQ_OK) {
|
if (qpc_login(h, "alice", "secret") != QPC_OK) {
|
||||||
fprintf(stderr, "login failed: %s\n", qpq_last_error(h));
|
fprintf(stderr, "login failed: %s\n", qpc_last_error(h));
|
||||||
qpq_disconnect(h);
|
qpc_disconnect(h);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send a message */
|
/* Send a message */
|
||||||
const char *msg = "hello from C";
|
const char *msg = "hello from C";
|
||||||
if (qpq_send(h, "bob", (const unsigned char *)msg, strlen(msg)) != QPQ_OK) {
|
if (qpc_send(h, "bob", (const unsigned char *)msg, strlen(msg)) != QPC_OK) {
|
||||||
fprintf(stderr, "send failed: %s\n", qpq_last_error(h));
|
fprintf(stderr, "send failed: %s\n", qpc_last_error(h));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive messages (5 second timeout) */
|
/* Receive messages (5 second timeout) */
|
||||||
char *json = NULL;
|
char *json = NULL;
|
||||||
int rc = qpq_receive(h, 5000, &json);
|
int rc = qpc_receive(h, 5000, &json);
|
||||||
if (rc == QPQ_OK && json) {
|
if (rc == QPC_OK && json) {
|
||||||
printf("received: %s\n", json);
|
printf("received: %s\n", json);
|
||||||
qpq_free_string(json);
|
qpc_free_string(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
qpq_disconnect(h);
|
qpc_disconnect(h);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -218,26 +218,26 @@ int main(void) {
|
|||||||
Compile and link:
|
Compile and link:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gcc -o qpq_demo qpq_demo.c -L target/release -lquicproquo_ffi -lpthread -ldl -lm
|
gcc -o qpc_demo qpc_demo.c -L target/release -lquicprochat_ffi -lpthread -ldl -lm
|
||||||
LD_LIBRARY_PATH=target/release ./qpq_demo
|
LD_LIBRARY_PATH=target/release ./qpc_demo
|
||||||
```
|
```
|
||||||
|
|
||||||
## Python Bindings
|
## Python Bindings
|
||||||
|
|
||||||
A ready-made Python `ctypes` wrapper is provided in
|
A ready-made Python `ctypes` wrapper is provided in
|
||||||
[`examples/python/qpq_client.py`](https://github.com/nickvidal/quicproquo/tree/main/examples/python).
|
[`examples/python/qpc_client.py`](https://github.com/nickvidal/quicprochat/tree/main/examples/python).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build the FFI library first
|
# Build the FFI library first
|
||||||
cargo build --release -p quicproquo-ffi
|
cargo build --release -p quicprochat-ffi
|
||||||
|
|
||||||
# Run the Python client
|
# Run the Python client
|
||||||
python examples/python/qpq_client.py \
|
python examples/python/qpc_client.py \
|
||||||
--server 127.0.0.1:7000 \
|
--server 127.0.0.1:7000 \
|
||||||
--ca-cert server-cert.der \
|
--ca-cert server-cert.der \
|
||||||
--username alice --password secret \
|
--username alice --password secret \
|
||||||
--receive --timeout 5000
|
--receive --timeout 5000
|
||||||
```
|
```
|
||||||
|
|
||||||
Set `QPQ_FFI_LIB=/path/to/libquicproquo_ffi.so` to override automatic
|
Set `QPC_FFI_LIB=/path/to/libquicprochat_ffi.so` to override automatic
|
||||||
library discovery.
|
library discovery.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# File Transfer
|
# File Transfer
|
||||||
|
|
||||||
quicproquo supports encrypted file transfer with chunked upload/download,
|
quicprochat supports encrypted file transfer with chunked upload/download,
|
||||||
SHA-256 content addressing, and automatic MIME type detection. Files up to
|
SHA-256 content addressing, and automatic MIME type detection. Files up to
|
||||||
50 MB are supported.
|
50 MB are supported.
|
||||||
|
|
||||||
@@ -109,6 +109,6 @@ file extension. For example:
|
|||||||
|
|
||||||
## Implementation
|
## Implementation
|
||||||
|
|
||||||
- **Server:** `crates/quicproquo-server/src/node_service/blob_ops.rs`
|
- **Server:** `crates/quicprochat-server/src/node_service/blob_ops.rs`
|
||||||
- **Client REPL:** `/send-file` and `/download` in `crates/quicproquo-client/src/client/repl.rs`
|
- **Client REPL:** `/send-file` and `/download` in `crates/quicprochat-client/src/client/repl.rs`
|
||||||
- **Message type:** `FileRef` variant in `crates/quicproquo-core/src/app_message.rs`
|
- **Message type:** `FileRef` variant in `crates/quicprochat-core/src/app_message.rs`
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
# Go SDK
|
# Go SDK
|
||||||
|
|
||||||
The Go SDK (`sdks/go/`) provides a native QUIC + Cap'n Proto client for
|
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
|
quicprochat, giving Go applications full access to the messaging API without
|
||||||
any HTTP translation layer.
|
any HTTP translation layer.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- Go 1.21+
|
- Go 1.21+
|
||||||
- A running qpq server
|
- A running qpc server
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "quicproquo.dev/sdk/go/qpq"
|
import "quicprochat.dev/sdk/go/qpc"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
@@ -25,14 +25,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"quicproquo.dev/sdk/go/qpq"
|
"quicprochat.dev/sdk/go/qpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// Connect to the server
|
// Connect to the server
|
||||||
client, err := qpq.Connect(ctx, qpq.Options{
|
client, err := qpc.Connect(ctx, qpc.Options{
|
||||||
Addr: "127.0.0.1:7000",
|
Addr: "127.0.0.1:7000",
|
||||||
InsecureSkipVerify: true, // development only
|
InsecureSkipVerify: true, // development only
|
||||||
})
|
})
|
||||||
@@ -130,7 +130,7 @@ type Options struct {
|
|||||||
sdks/go/
|
sdks/go/
|
||||||
├── proto/ # Generated Cap'n Proto types from node.capnp
|
├── proto/ # Generated Cap'n Proto types from node.capnp
|
||||||
├── transport/ # QUIC transport layer (quic-go + TLS 1.3)
|
├── transport/ # QUIC transport layer (quic-go + TLS 1.3)
|
||||||
├── qpq/ # High-level client API (QpqClient)
|
├── qpc/ # High-level client API (QpqClient)
|
||||||
├── cmd/example/ # Example CLI program
|
├── cmd/example/ # Example CLI program
|
||||||
├── go.mod
|
├── go.mod
|
||||||
└── README.md
|
└── README.md
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Mesh Networking
|
# Mesh Networking
|
||||||
|
|
||||||
quicproquo includes a mesh networking layer for decentralised, peer-to-peer
|
quicprochat includes a mesh networking layer for decentralised, peer-to-peer
|
||||||
messaging without central infrastructure. It is designed for community
|
messaging without central infrastructure. It is designed for community
|
||||||
networks (Freifunk, BATMAN-adv, Babel routing) and offline-capable
|
networks (Freifunk, BATMAN-adv, Babel routing) and offline-capable
|
||||||
environments.
|
environments.
|
||||||
@@ -8,17 +8,17 @@ environments.
|
|||||||
Mesh features are **feature-gated** — build the client with:
|
Mesh features are **feature-gated** — build the client with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo build -p quicproquo-client --features mesh
|
cargo build -p quicprochat-client --features mesh
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
Client A ── mDNS discovery ──► nearby qpq node (LAN / mesh)
|
Client A ── mDNS discovery ──► nearby qpc node (LAN / mesh)
|
||||||
│
|
│
|
||||||
Cap'n Proto federation
|
Cap'n Proto federation
|
||||||
│
|
│
|
||||||
remote qpq node (across mesh)
|
remote qpc node (across mesh)
|
||||||
|
|
||||||
Client B ── iroh P2P ──────► Client C (direct, NAT-traversed)
|
Client B ── iroh P2P ──────► Client C (direct, NAT-traversed)
|
||||||
```
|
```
|
||||||
@@ -44,7 +44,7 @@ persisted in a local JSON file.
|
|||||||
|
|
||||||
## mDNS discovery
|
## mDNS discovery
|
||||||
|
|
||||||
Servers announce `_quicproquo._udp.local.` via mDNS on startup with TXT
|
Servers announce `_quicprochat._udp.local.` via mDNS on startup with TXT
|
||||||
records:
|
records:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -126,14 +126,14 @@ Servers relay messages for recipients on remote nodes:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Environment variables
|
# Environment variables
|
||||||
QPQ_FEDERATION_LISTEN=0.0.0.0:7001
|
QPC_FEDERATION_LISTEN=0.0.0.0:7001
|
||||||
QPQ_LOCAL_DOMAIN=node1.mesh.local
|
QPC_LOCAL_DOMAIN=node1.mesh.local
|
||||||
QPQ_FEDERATION_CERT=/path/to/cert.der
|
QPC_FEDERATION_CERT=/path/to/cert.der
|
||||||
QPQ_FEDERATION_KEY=/path/to/key.der
|
QPC_FEDERATION_KEY=/path/to/key.der
|
||||||
QPQ_FEDERATION_CA=/path/to/ca.der
|
QPC_FEDERATION_CA=/path/to/ca.der
|
||||||
```
|
```
|
||||||
|
|
||||||
Or in `qpq-server.toml`:
|
Or in `qpc-server.toml`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
federation_enabled = true
|
federation_enabled = true
|
||||||
@@ -146,8 +146,8 @@ federation_listen = "0.0.0.0:7001"
|
|||||||
| Protocol | ALPN |
|
| Protocol | ALPN |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Client ↔ Server | `b"capnp"` |
|
| Client ↔ Server | `b"capnp"` |
|
||||||
| P2P transport | `b"quicproquo/p2p/1"` |
|
| P2P transport | `b"quicprochat/p2p/1"` |
|
||||||
| Federation | `b"quicproquo/federation/1"` |
|
| Federation | `b"quicprochat/federation/1"` |
|
||||||
|
|
||||||
## REPL command summary
|
## REPL command summary
|
||||||
|
|
||||||
@@ -164,12 +164,12 @@ federation_listen = "0.0.0.0:7001"
|
|||||||
|
|
||||||
## Implementation
|
## Implementation
|
||||||
|
|
||||||
- **P2P node:** `crates/quicproquo-p2p/src/lib.rs` — `P2pNode` with iroh
|
- **P2P node:** `crates/quicprochat-p2p/src/lib.rs` — `P2pNode` with iroh
|
||||||
transport
|
transport
|
||||||
- **Mesh identity:** `crates/quicproquo-p2p/src/identity.rs`
|
- **Mesh identity:** `crates/quicprochat-p2p/src/identity.rs`
|
||||||
- **Store-and-forward:** `crates/quicproquo-p2p/src/store.rs` +
|
- **Store-and-forward:** `crates/quicprochat-p2p/src/store.rs` +
|
||||||
`envelope.rs`
|
`envelope.rs`
|
||||||
- **Broadcast:** `crates/quicproquo-p2p/src/broadcast.rs`
|
- **Broadcast:** `crates/quicprochat-p2p/src/broadcast.rs`
|
||||||
- **mDNS discovery:** `crates/quicproquo-client/src/client/mesh_discovery.rs`
|
- **mDNS discovery:** `crates/quicprochat-client/src/client/mesh_discovery.rs`
|
||||||
- **Federation routing:** `crates/quicproquo-server/src/node_service/delivery.rs`
|
- **Federation routing:** `crates/quicprochat-server/src/node_service/delivery.rs`
|
||||||
- **REPL commands:** mesh handlers in `crates/quicproquo-client/src/client/repl.rs`
|
- **REPL commands:** mesh handlers in `crates/quicprochat-client/src/client/repl.rs`
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Prerequisites
|
# Prerequisites
|
||||||
|
|
||||||
Before building quicproquo you need a Rust toolchain. No other system tools are required — Protobuf compilation is handled automatically at build time by the `protobuf-src` crate, which vendors the `protoc` compiler. Docker is optional and useful for reproducible builds and deployment.
|
Before building quicprochat you need a Rust toolchain. No other system tools are required — Protobuf compilation is handled automatically at build time by the `protobuf-src` crate, which vendors the `protoc` compiler. Docker is optional and useful for reproducible builds and deployment.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ Before building quicproquo you need a Rust toolchain. No other system tools are
|
|||||||
|
|
||||||
**Minimum supported Rust version: 1.77+ (stable)**
|
**Minimum supported Rust version: 1.77+ (stable)**
|
||||||
|
|
||||||
quicproquo uses the 2021 edition and workspace resolver v2. Any stable Rust release from 1.77 onward should work. Install or update via [rustup](https://rustup.rs/):
|
quicprochat uses the 2021 edition and workspace resolver v2. Any stable Rust release from 1.77 onward should work. Install or update via [rustup](https://rustup.rs/):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install rustup (if not already present)
|
# Install rustup (if not already present)
|
||||||
@@ -29,7 +29,7 @@ The workspace depends on several crates that use procedural macros (`serde_deriv
|
|||||||
|
|
||||||
## No external compiler dependencies
|
## No external compiler dependencies
|
||||||
|
|
||||||
In v2, all wire-format serialisation uses [Protobuf](https://protobuf.dev/) via the `prost` crate. The `quicproquo-proto` crate's `build.rs` script drives code generation through `prost-build`, which in turn uses the `protobuf-src` crate to compile and use a vendored copy of `protoc`. **You do not need to install `protoc` or any other system compiler.**
|
In v2, all wire-format serialisation uses [Protobuf](https://protobuf.dev/) via the `prost` crate. The `quicprochat-proto` crate's `build.rs` script drives code generation through `prost-build`, which in turn uses the `protobuf-src` crate to compile and use a vendored copy of `protoc`. **You do not need to install `protoc` or any other system compiler.**
|
||||||
|
|
||||||
The legacy Cap'n Proto schemas (`schemas/`) are still present for reference, but the v2 runtime and RPC framework use Protobuf exclusively.
|
The legacy Cap'n Proto schemas (`schemas/`) are still present for reference, but the v2 runtime and RPC framework use Protobuf exclusively.
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ The legacy Cap'n Proto schemas (`schemas/`) are still present for reference, but
|
|||||||
|
|
||||||
## Optional: Docker and Docker Compose
|
## Optional: Docker and Docker Compose
|
||||||
|
|
||||||
If you prefer to build and run quicproquo in containers, you will need:
|
If you prefer to build and run quicprochat in containers, you will need:
|
||||||
|
|
||||||
- **Docker Engine** 20.10+ (or Docker Desktop)
|
- **Docker Engine** 20.10+ (or Docker Desktop)
|
||||||
- **Docker Compose** v2+ (the `docker compose` plugin, not the legacy `docker-compose` binary)
|
- **Docker Compose** v2+ (the `docker compose` plugin, not the legacy `docker-compose` binary)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# REPL Command Reference
|
# REPL Command Reference
|
||||||
|
|
||||||
The qpq interactive REPL provides 40+ slash commands for messaging, group
|
The qpc interactive REPL provides 40+ slash commands for messaging, group
|
||||||
management, file transfer, privacy controls, and mesh networking. Launch it
|
management, file transfer, privacy controls, and mesh networking. Launch it
|
||||||
with:
|
with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run --bin qpq -- repl --username alice --password mypass
|
cargo run --bin qpc -- repl --username alice --password mypass
|
||||||
```
|
```
|
||||||
|
|
||||||
Type any text without a leading `/` to send a message in the active
|
Type any text without a leading `/` to send a message in the active
|
||||||
@@ -79,12 +79,12 @@ conversation.
|
|||||||
These commands require the client to be built with mesh support:
|
These commands require the client to be built with mesh support:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo build -p quicproquo-client --features mesh
|
cargo build -p quicprochat-client --features mesh
|
||||||
```
|
```
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `/mesh peers` | Scan for nearby qpq nodes via mDNS (`_quicproquo._udp.local.`) |
|
| `/mesh peers` | Scan for nearby qpc nodes via mDNS (`_quicprochat._udp.local.`) |
|
||||||
| `/mesh server <host:port>` | Note a discovered server address for connection |
|
| `/mesh server <host:port>` | Note a discovered server address for connection |
|
||||||
| `/mesh send <peer_id> <msg>` | Send a direct P2P message via iroh transport |
|
| `/mesh send <peer_id> <msg>` | Send a direct P2P message via iroh transport |
|
||||||
| `/mesh broadcast <topic> <msg>` | Publish a message to a broadcast channel |
|
| `/mesh broadcast <topic> <msg>` | Publish a message to a broadcast channel |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Rich Messaging
|
# Rich Messaging
|
||||||
|
|
||||||
quicproquo supports rich messaging features beyond basic text: reactions, read
|
quicprochat supports rich messaging features beyond basic text: reactions, read
|
||||||
receipts, typing indicators, message editing, and message deletion. All
|
receipts, typing indicators, message editing, and message deletion. All
|
||||||
message types are end-to-end encrypted inside MLS ciphertext — the server
|
message types are end-to-end encrypted inside MLS ciphertext — the server
|
||||||
only sees opaque bytes.
|
only sees opaque bytes.
|
||||||
@@ -86,9 +86,9 @@ All rich message types use the same binary envelope inside the MLS ciphertext:
|
|||||||
|
|
||||||
## Implementation
|
## Implementation
|
||||||
|
|
||||||
- **Core serialisation:** `crates/quicproquo-core/src/app_message.rs` — the
|
- **Core serialisation:** `crates/quicprochat-core/src/app_message.rs` — the
|
||||||
`AppMessage` enum with `serialize()` / `deserialize()` methods
|
`AppMessage` enum with `serialize()` / `deserialize()` methods
|
||||||
- **REPL commands:** `crates/quicproquo-client/src/client/repl.rs` — slash
|
- **REPL commands:** `crates/quicprochat-client/src/client/repl.rs` — slash
|
||||||
command handlers
|
command handlers
|
||||||
- **Display:** `crates/quicproquo-client/src/client/display.rs` — typing
|
- **Display:** `crates/quicprochat-client/src/client/display.rs` — typing
|
||||||
indicator rendering
|
indicator rendering
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Running the Client
|
# Running the Client
|
||||||
|
|
||||||
The quicproquo CLI client provides subcommands for connectivity testing, identity registration, KeyPackage exchange, and persistent group messaging. All commands connect to the server over QUIC + TLS 1.3 and issue Cap'n Proto RPC calls against the `NodeService` endpoint.
|
The quicprochat CLI client provides subcommands for connectivity testing, identity registration, KeyPackage exchange, and persistent group messaging. All commands connect to the server over QUIC + TLS 1.3 and issue Cap'n Proto RPC calls against the `NodeService` endpoint.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -10,8 +10,8 @@ These flags apply to every subcommand:
|
|||||||
|
|
||||||
| Flag | Env var | Default | Purpose |
|
| Flag | Env var | Default | Purpose |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `--ca-cert` | `QPQ_CA_CERT` | `data/server-cert.der` | Path to the server's TLS certificate (DER format). The client uses this to verify the server's identity during the TLS handshake. |
|
| `--ca-cert` | `QPC_CA_CERT` | `data/server-cert.der` | Path to the server's TLS certificate (DER format). The client uses this to verify the server's identity during the TLS handshake. |
|
||||||
| `--server-name` | `QPQ_SERVER_NAME` | `localhost` | Expected TLS server name. Must match a SAN in the server's certificate. |
|
| `--server-name` | `QPC_SERVER_NAME` | `localhost` | Expected TLS server name. Must match a SAN in the server's certificate. |
|
||||||
|
|
||||||
Most subcommands also accept `--server` (default `127.0.0.1:7000`) to specify the server address.
|
Most subcommands also accept `--server` (default `127.0.0.1:7000`) to specify the server address.
|
||||||
|
|
||||||
@@ -24,11 +24,11 @@ Most subcommands also accept `--server` (default `127.0.0.1:7000`) to specify th
|
|||||||
Send a health probe to the server and print the round-trip time.
|
Send a health probe to the server and print the round-trip time.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- ping
|
cargo run -p quicprochat-client -- ping
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- ping --server 192.168.1.10:7000
|
cargo run -p quicprochat-client -- ping --server 192.168.1.10:7000
|
||||||
```
|
```
|
||||||
|
|
||||||
**Output:**
|
**Output:**
|
||||||
@@ -49,7 +49,7 @@ These commands generate a fresh identity keypair in memory each time they run. T
|
|||||||
Generate a fresh Ed25519 identity, create an MLS KeyPackage, and upload it to the Authentication Service.
|
Generate a fresh Ed25519 identity, create an MLS KeyPackage, and upload it to the Authentication Service.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- register
|
cargo run -p quicprochat-client -- register
|
||||||
```
|
```
|
||||||
|
|
||||||
**Output:**
|
**Output:**
|
||||||
@@ -66,7 +66,7 @@ Share the `identity_key` value with peers who want to add you to a group. They w
|
|||||||
Fetch a peer's KeyPackage from the Authentication Service by their Ed25519 public key.
|
Fetch a peer's KeyPackage from the Authentication Service by their Ed25519 public key.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- fetch-key a1b2c3d4e5f6...
|
cargo run -p quicprochat-client -- fetch-key a1b2c3d4e5f6...
|
||||||
```
|
```
|
||||||
|
|
||||||
The `identity_key` argument must be exactly 64 lowercase hex characters (32 bytes).
|
The `identity_key` argument must be exactly 64 lowercase hex characters (32 bytes).
|
||||||
@@ -90,7 +90,7 @@ KeyPackages are single-use: fetching a KeyPackage atomically removes it from the
|
|||||||
Run a complete Alice-and-Bob MLS round-trip against a live server. Both identities are created in-process; both communicate through the server's AS and DS.
|
Run a complete Alice-and-Bob MLS round-trip against a live server. Both identities are created in-process; both communicate through the server's AS and DS.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- demo-group --server 127.0.0.1:7000
|
cargo run -p quicprochat-client -- demo-group --server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
|
|
||||||
**Output:**
|
**Output:**
|
||||||
@@ -106,21 +106,21 @@ This is the fastest way to verify that the entire stack (QUIC + TLS + Cap'n Prot
|
|||||||
|
|
||||||
## Persistent group commands
|
## Persistent group commands
|
||||||
|
|
||||||
These commands use a state file (`--state`, default `qpq-state.bin`) to persist the Ed25519 identity seed and MLS group state between invocations. A companion key store file (same path with `.ks` extension) holds HPKE init private keys.
|
These commands use a state file (`--state`, default `qpc-state.bin`) to persist the Ed25519 identity seed and MLS group state between invocations. A companion key store file (same path with `.ks` extension) holds HPKE init private keys.
|
||||||
|
|
||||||
All persistent commands share the `--state` flag:
|
All persistent commands share the `--state` flag:
|
||||||
|
|
||||||
| Flag | Env var | Default |
|
| Flag | Env var | Default |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `--state` | `QPQ_STATE` | `qpq-state.bin` |
|
| `--state` | `QPC_STATE` | `qpc-state.bin` |
|
||||||
| `--server` | `QPQ_SERVER` | `127.0.0.1:7000` |
|
| `--server` | `QPC_SERVER` | `127.0.0.1:7000` |
|
||||||
|
|
||||||
### `register-state`
|
### `register-state`
|
||||||
|
|
||||||
Create or load a persistent identity, generate a KeyPackage, and upload it to the AS.
|
Create or load a persistent identity, generate a KeyPackage, and upload it to the AS.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- register-state \
|
cargo run -p quicprochat-client -- register-state \
|
||||||
--state alice.bin \
|
--state alice.bin \
|
||||||
--server 127.0.0.1:7000
|
--server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
@@ -141,10 +141,10 @@ Refresh the KeyPackage on the server using your **existing** state file. Does no
|
|||||||
- Your KeyPackage has expired (server TTL, e.g. 24h).
|
- Your KeyPackage has expired (server TTL, e.g. 24h).
|
||||||
- Your KeyPackage was consumed (someone invited you) and you want to be invitable again.
|
- Your KeyPackage was consumed (someone invited you) and you want to be invitable again.
|
||||||
|
|
||||||
Run with the same `--access-token` (or `QPQ_ACCESS_TOKEN`) as for other commands.
|
Run with the same `--access-token` (or `QPC_ACCESS_TOKEN`) as for other commands.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- refresh-keypackage \
|
cargo run -p quicprochat-client -- refresh-keypackage \
|
||||||
--state alice.bin \
|
--state alice.bin \
|
||||||
--server 127.0.0.1:7000
|
--server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
@@ -163,7 +163,7 @@ If you are told "no key" when someone tries to invite you, have them wait and ru
|
|||||||
Create a new MLS group. The caller becomes the sole member at epoch 0.
|
Create a new MLS group. The caller becomes the sole member at epoch 0.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- create-group \
|
cargo run -p quicprochat-client -- create-group \
|
||||||
--state alice.bin \
|
--state alice.bin \
|
||||||
--group-id "project-chat"
|
--group-id "project-chat"
|
||||||
```
|
```
|
||||||
@@ -180,7 +180,7 @@ The group state is saved to the state file. You can now invite peers with `invit
|
|||||||
Fetch a peer's KeyPackage from the AS, add them to the group, and deliver the Welcome message via the DS.
|
Fetch a peer's KeyPackage from the AS, add them to the group, and deliver the Welcome message via the DS.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- invite \
|
cargo run -p quicprochat-client -- invite \
|
||||||
--state alice.bin \
|
--state alice.bin \
|
||||||
--peer-key b9a8c7d6e5f4... \
|
--peer-key b9a8c7d6e5f4... \
|
||||||
--server 127.0.0.1:7000
|
--server 127.0.0.1:7000
|
||||||
@@ -202,7 +202,7 @@ invited peer (welcome queued)
|
|||||||
Join a group by consuming a Welcome message from the DS.
|
Join a group by consuming a Welcome message from the DS.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- join \
|
cargo run -p quicprochat-client -- join \
|
||||||
--state bob.bin \
|
--state bob.bin \
|
||||||
--server 127.0.0.1:7000
|
--server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
@@ -219,7 +219,7 @@ joined group successfully
|
|||||||
Encrypt and send an application message to a peer via the DS.
|
Encrypt and send an application message to a peer via the DS.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- send \
|
cargo run -p quicprochat-client -- send \
|
||||||
--state alice.bin \
|
--state alice.bin \
|
||||||
--peer-key b9a8c7d6e5f4... \
|
--peer-key b9a8c7d6e5f4... \
|
||||||
--msg "hello from alice" \
|
--msg "hello from alice" \
|
||||||
@@ -238,7 +238,7 @@ message sent
|
|||||||
Receive and decrypt all pending messages from the DS.
|
Receive and decrypt all pending messages from the DS.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-client -- recv \
|
cargo run -p quicprochat-client -- recv \
|
||||||
--state bob.bin \
|
--state bob.bin \
|
||||||
--server 127.0.0.1:7000
|
--server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
@@ -257,12 +257,12 @@ Additional flags:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Wait up to 5 seconds for messages
|
# Wait up to 5 seconds for messages
|
||||||
cargo run -p quicproquo-client -- recv \
|
cargo run -p quicprochat-client -- recv \
|
||||||
--state bob.bin \
|
--state bob.bin \
|
||||||
--wait-ms 5000
|
--wait-ms 5000
|
||||||
|
|
||||||
# Stream messages continuously
|
# Stream messages continuously
|
||||||
cargo run -p quicproquo-client -- recv \
|
cargo run -p quicprochat-client -- recv \
|
||||||
--state bob.bin \
|
--state bob.bin \
|
||||||
--stream --wait-ms 10000
|
--stream --wait-ms 10000
|
||||||
```
|
```
|
||||||
@@ -271,7 +271,7 @@ cargo run -p quicproquo-client -- recv \
|
|||||||
|
|
||||||
## HPKE init key lifecycle warning
|
## HPKE init key lifecycle warning
|
||||||
|
|
||||||
The MLS protocol requires that the HPKE init private key generated during KeyPackage creation is available when processing the corresponding Welcome message. In quicproquo, this private key is stored in the key store file (`.ks` extension alongside the state file).
|
The MLS protocol requires that the HPKE init private key generated during KeyPackage creation is available when processing the corresponding Welcome message. In quicprochat, this private key is stored in the key store file (`.ks` extension alongside the state file).
|
||||||
|
|
||||||
**The same state file and key store must be used for both `register-state` and `join`.** If you:
|
**The same state file and key store must be used for both `register-state` and `join`.** If you:
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# Running the Server
|
# Running the Server
|
||||||
|
|
||||||
The quicproquo server is a single binary (`qpq-server`) that exposes a unified **NodeService** endpoint combining Authentication Service (OPAQUE registration/login, KeyPackage management) and Delivery Service (message relay) operations over a single QUIC + TLS 1.3 connection.
|
The quicprochat server is a single binary (`qpc-server`) that exposes a unified **NodeService** endpoint combining Authentication Service (OPAQUE registration/login, KeyPackage management) and Delivery Service (message relay) operations over a single QUIC + TLS 1.3 connection.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-server -- --allow-insecure-auth
|
cargo run -p quicprochat-server -- --allow-insecure-auth
|
||||||
```
|
```
|
||||||
|
|
||||||
On first launch the server will:
|
On first launch the server will:
|
||||||
@@ -21,8 +21,8 @@ On first launch the server will:
|
|||||||
You should see output similar to:
|
You should see output similar to:
|
||||||
|
|
||||||
```
|
```
|
||||||
2026-01-01T00:00:00.000000Z INFO qpq_server: generated self-signed TLS certificate cert="data/server-cert.der" key="data/server-key.der"
|
2026-01-01T00:00:00.000000Z INFO qpc_server: generated self-signed TLS certificate cert="data/server-cert.der" key="data/server-key.der"
|
||||||
2026-01-01T00:00:00.000000Z INFO qpq_server: accepting QUIC connections addr="0.0.0.0:7000"
|
2026-01-01T00:00:00.000000Z INFO qpc_server: accepting QUIC connections addr="0.0.0.0:7000"
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Development note:** `--allow-insecure-auth` bypasses the requirement for a static bearer token. Do not use this flag in production.
|
> **Development note:** `--allow-insecure-auth` bypasses the requirement for a static bearer token. Do not use this flag in production.
|
||||||
@@ -31,108 +31,108 @@ You should see output similar to:
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
All configuration is available via CLI flags, environment variables, or a TOML config file (`qpq-server.toml` by default, overridden with `--config`). CLI flags take precedence over the config file.
|
All configuration is available via CLI flags, environment variables, or a TOML config file (`qpc-server.toml` by default, overridden with `--config`). CLI flags take precedence over the config file.
|
||||||
|
|
||||||
### Core flags
|
### Core flags
|
||||||
|
|
||||||
| Purpose | CLI flag | Env var | Default |
|
| Purpose | CLI flag | Env var | Default |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| QUIC listen address | `--listen` | `QPQ_LISTEN` | `0.0.0.0:7000` |
|
| QUIC listen address | `--listen` | `QPC_LISTEN` | `0.0.0.0:7000` |
|
||||||
| TLS certificate (DER) | `--tls-cert` | `QPQ_TLS_CERT` | `data/server-cert.der` |
|
| TLS certificate (DER) | `--tls-cert` | `QPC_TLS_CERT` | `data/server-cert.der` |
|
||||||
| TLS private key (DER) | `--tls-key` | `QPQ_TLS_KEY` | `data/server-key.der` |
|
| TLS private key (DER) | `--tls-key` | `QPC_TLS_KEY` | `data/server-key.der` |
|
||||||
| Data directory | `--data-dir` | `QPQ_DATA_DIR` | `data` |
|
| Data directory | `--data-dir` | `QPC_DATA_DIR` | `data` |
|
||||||
| TOML config file | `--config` | `QPQ_CONFIG` | `qpq-server.toml` |
|
| TOML config file | `--config` | `QPC_CONFIG` | `qpc-server.toml` |
|
||||||
| Log level | -- | `RUST_LOG` | `info` |
|
| Log level | -- | `RUST_LOG` | `info` |
|
||||||
|
|
||||||
### Authentication flags
|
### Authentication flags
|
||||||
|
|
||||||
| Purpose | CLI flag | Env var | Default |
|
| Purpose | CLI flag | Env var | Default |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Static bearer token | `--auth-token` | `QPQ_AUTH_TOKEN` | (none) |
|
| Static bearer token | `--auth-token` | `QPC_AUTH_TOKEN` | (none) |
|
||||||
| Skip token requirement (dev only) | `--allow-insecure-auth` | `QPQ_ALLOW_INSECURE_AUTH` | `false` |
|
| Skip token requirement (dev only) | `--allow-insecure-auth` | `QPC_ALLOW_INSECURE_AUTH` | `false` |
|
||||||
| Sealed sender mode | `--sealed-sender` | `QPQ_SEALED_SENDER` | `false` |
|
| Sealed sender mode | `--sealed-sender` | `QPC_SEALED_SENDER` | `false` |
|
||||||
|
|
||||||
### Storage flags
|
### Storage flags
|
||||||
|
|
||||||
| Purpose | CLI flag | Env var | Default |
|
| Purpose | CLI flag | Env var | Default |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Storage backend | `--store-backend` | `QPQ_STORE_BACKEND` | `file` |
|
| Storage backend | `--store-backend` | `QPC_STORE_BACKEND` | `file` |
|
||||||
| SQLCipher DB path | `--db-path` | `QPQ_DB_PATH` | `data/qpq.db` |
|
| SQLCipher DB path | `--db-path` | `QPC_DB_PATH` | `data/qpc.db` |
|
||||||
| SQLCipher encryption key | `--db-key` | `QPQ_DB_KEY` | (empty = plaintext) |
|
| SQLCipher encryption key | `--db-key` | `QPC_DB_KEY` | (empty = plaintext) |
|
||||||
|
|
||||||
### Transport and timeout flags
|
### Transport and timeout flags
|
||||||
|
|
||||||
| Purpose | CLI flag | Env var | Default |
|
| Purpose | CLI flag | Env var | Default |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Drain timeout (graceful shutdown) | `--drain-timeout` | `QPQ_DRAIN_TIMEOUT` | `30` s |
|
| Drain timeout (graceful shutdown) | `--drain-timeout` | `QPC_DRAIN_TIMEOUT` | `30` s |
|
||||||
| Per-RPC timeout | `--rpc-timeout` | `QPQ_RPC_TIMEOUT` | `30` s |
|
| Per-RPC timeout | `--rpc-timeout` | `QPC_RPC_TIMEOUT` | `30` s |
|
||||||
| Storage operation timeout | `--storage-timeout` | `QPQ_STORAGE_TIMEOUT` | `10` s |
|
| Storage operation timeout | `--storage-timeout` | `QPC_STORAGE_TIMEOUT` | `10` s |
|
||||||
|
|
||||||
### Extension flags
|
### Extension flags
|
||||||
|
|
||||||
| Purpose | CLI flag | Env var | Default |
|
| Purpose | CLI flag | Env var | Default |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Plugin directory | `--plugin-dir` | `QPQ_PLUGIN_DIR` | (none) |
|
| Plugin directory | `--plugin-dir` | `QPC_PLUGIN_DIR` | (none) |
|
||||||
| WebSocket bridge address | `--ws-listen` | `QPQ_WS_LISTEN` | (none) |
|
| WebSocket bridge address | `--ws-listen` | `QPC_WS_LISTEN` | (none) |
|
||||||
| WebTransport address | `--webtransport-listen` | `QPQ_WEBTRANSPORT_LISTEN` | (none) |
|
| WebTransport address | `--webtransport-listen` | `QPC_WEBTRANSPORT_LISTEN` | (none) |
|
||||||
| Federation | `--federation-enabled` | `QPQ_FEDERATION_ENABLED` | `false` |
|
| Federation | `--federation-enabled` | `QPC_FEDERATION_ENABLED` | `false` |
|
||||||
| Federation domain | `--federation-domain` | `QPQ_FEDERATION_DOMAIN` | (none) |
|
| Federation domain | `--federation-domain` | `QPC_FEDERATION_DOMAIN` | (none) |
|
||||||
| Federation listen address | `--federation-listen` | `QPQ_FEDERATION_LISTEN` | `0.0.0.0:7001` |
|
| Federation listen address | `--federation-listen` | `QPC_FEDERATION_LISTEN` | `0.0.0.0:7001` |
|
||||||
| Redact audit logs | `--redact-logs` | `QPQ_REDACT_LOGS` | `false` |
|
| Redact audit logs | `--redact-logs` | `QPC_REDACT_LOGS` | `false` |
|
||||||
| Metrics listen address | `--metrics-listen` | `QPQ_METRICS_LISTEN` | (none) |
|
| Metrics listen address | `--metrics-listen` | `QPC_METRICS_LISTEN` | (none) |
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Development: no auth token required
|
# Development: no auth token required
|
||||||
cargo run -p quicproquo-server -- --allow-insecure-auth
|
cargo run -p quicprochat-server -- --allow-insecure-auth
|
||||||
|
|
||||||
# Listen on a custom port
|
# Listen on a custom port
|
||||||
cargo run -p quicproquo-server -- --allow-insecure-auth --listen 0.0.0.0:5001
|
cargo run -p quicprochat-server -- --allow-insecure-auth --listen 0.0.0.0:5001
|
||||||
|
|
||||||
# Use SQLCipher storage backend
|
# Use SQLCipher storage backend
|
||||||
cargo run -p quicproquo-server -- \
|
cargo run -p quicprochat-server -- \
|
||||||
--allow-insecure-auth \
|
--allow-insecure-auth \
|
||||||
--store-backend sql \
|
--store-backend sql \
|
||||||
--db-path data/qpq.db \
|
--db-path data/qpc.db \
|
||||||
--db-key mysecretkey
|
--db-key mysecretkey
|
||||||
|
|
||||||
# Load server plugins from a directory
|
# Load server plugins from a directory
|
||||||
cargo run -p quicproquo-server -- \
|
cargo run -p quicprochat-server -- \
|
||||||
--allow-insecure-auth \
|
--allow-insecure-auth \
|
||||||
--plugin-dir /path/to/plugins
|
--plugin-dir /path/to/plugins
|
||||||
|
|
||||||
# Enable WebSocket bridge for browser clients
|
# Enable WebSocket bridge for browser clients
|
||||||
cargo run -p quicproquo-server -- \
|
cargo run -p quicprochat-server -- \
|
||||||
--allow-insecure-auth \
|
--allow-insecure-auth \
|
||||||
--ws-listen 0.0.0.0:9000
|
--ws-listen 0.0.0.0:9000
|
||||||
|
|
||||||
# Via environment variables
|
# Via environment variables
|
||||||
QPQ_LISTEN=0.0.0.0:5001 \
|
QPC_LISTEN=0.0.0.0:5001 \
|
||||||
QPQ_ALLOW_INSECURE_AUTH=true \
|
QPC_ALLOW_INSECURE_AUTH=true \
|
||||||
RUST_LOG=debug \
|
RUST_LOG=debug \
|
||||||
cargo run -p quicproquo-server
|
cargo run -p quicprochat-server
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Production deployment
|
## Production deployment
|
||||||
|
|
||||||
Set `QPQ_PRODUCTION=true` to enable production validation. The server enforces:
|
Set `QPC_PRODUCTION=true` to enable production validation. The server enforces:
|
||||||
|
|
||||||
- `--allow-insecure-auth` is **prohibited**.
|
- `--allow-insecure-auth` is **prohibited**.
|
||||||
- `QPQ_AUTH_TOKEN` must be set, non-empty, at least 16 characters, and not equal to `devtoken`.
|
- `QPC_AUTH_TOKEN` must be set, non-empty, at least 16 characters, and not equal to `devtoken`.
|
||||||
- TLS cert and key files must already exist (auto-generation is disabled).
|
- TLS cert and key files must already exist (auto-generation is disabled).
|
||||||
- When `--store-backend=sql`, `QPQ_DB_KEY` must be non-empty.
|
- When `--store-backend=sql`, `QPC_DB_KEY` must be non-empty.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
QPQ_PRODUCTION=true \
|
QPC_PRODUCTION=true \
|
||||||
QPQ_AUTH_TOKEN=<strong-token> \
|
QPC_AUTH_TOKEN=<strong-token> \
|
||||||
QPQ_TLS_CERT=/etc/quicproquo/cert.der \
|
QPC_TLS_CERT=/etc/quicprochat/cert.der \
|
||||||
QPQ_TLS_KEY=/etc/quicproquo/key.der \
|
QPC_TLS_KEY=/etc/quicprochat/key.der \
|
||||||
QPQ_STORE_BACKEND=sql \
|
QPC_STORE_BACKEND=sql \
|
||||||
QPQ_DB_KEY=<strong-db-key> \
|
QPC_DB_KEY=<strong-db-key> \
|
||||||
qpq-server
|
qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -160,7 +160,7 @@ To use a certificate issued by a CA or a custom self-signed certificate:
|
|||||||
```
|
```
|
||||||
2. Point the server at them:
|
2. Point the server at them:
|
||||||
```bash
|
```bash
|
||||||
cargo run -p quicproquo-server -- \
|
cargo run -p quicprochat-server -- \
|
||||||
--allow-insecure-auth \
|
--allow-insecure-auth \
|
||||||
--tls-cert cert.der \
|
--tls-cert cert.der \
|
||||||
--tls-key key.der
|
--tls-key key.der
|
||||||
@@ -171,7 +171,7 @@ To use a certificate issued by a CA or a custom self-signed certificate:
|
|||||||
|
|
||||||
- **Protocol versions**: TLS 1.3 only. TLS 1.2 and below are rejected.
|
- **Protocol versions**: TLS 1.3 only. TLS 1.2 and below are rejected.
|
||||||
- **Client authentication**: Disabled. Client identity is established at the MLS/OPAQUE layer, not at the TLS layer.
|
- **Client authentication**: Disabled. Client identity is established at the MLS/OPAQUE layer, not at the TLS layer.
|
||||||
- **ALPN**: The server advertises `b"qpq/1"` as the application-layer protocol.
|
- **ALPN**: The server advertises `b"qpc/1"` as the application-layer protocol.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -201,13 +201,13 @@ The server uses `tracing` with `tracing-subscriber` and respects the `RUST_LOG`
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Default: info level
|
# Default: info level
|
||||||
RUST_LOG=info cargo run -p quicproquo-server -- --allow-insecure-auth
|
RUST_LOG=info cargo run -p quicprochat-server -- --allow-insecure-auth
|
||||||
|
|
||||||
# Debug level for detailed RPC tracing
|
# Debug level for detailed RPC tracing
|
||||||
RUST_LOG=debug cargo run -p quicproquo-server -- --allow-insecure-auth
|
RUST_LOG=debug cargo run -p quicprochat-server -- --allow-insecure-auth
|
||||||
|
|
||||||
# Filter to specific crates
|
# Filter to specific crates
|
||||||
RUST_LOG=quicproquo_server=debug,quinn=warn cargo run -p quicproquo-server -- --allow-insecure-auth
|
RUST_LOG=quicprochat_server=debug,quinn=warn cargo run -p quicprochat-server -- --allow-insecure-auth
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# TLS in quicproquo
|
# TLS in quicprochat
|
||||||
|
|
||||||
quicproquo uses QUIC (RFC 9000) for all client-server communication. QUIC mandates TLS 1.3, so every connection is encrypted and authenticated at the transport layer — there is no plaintext mode.
|
quicprochat uses QUIC (RFC 9000) for all client-server communication. QUIC mandates TLS 1.3, so every connection is encrypted and authenticated at the transport layer — there is no plaintext mode.
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
The server holds a TLS certificate and private key (DER format). On startup it either loads existing files or, in development mode, generates a self-signed certificate automatically. The client authenticates the server by verifying its certificate against a trusted root provided via `--ca-cert` (or `QPQ_CA_CERT`).
|
The server holds a TLS certificate and private key (DER format). On startup it either loads existing files or, in development mode, generates a self-signed certificate automatically. The client authenticates the server by verifying its certificate against a trusted root provided via `--ca-cert` (or `QPC_CA_CERT`).
|
||||||
|
|
||||||
The TLS handshake negotiates the ALPN protocol `capnp`, after which the QUIC bi-directional stream carries Cap'n Proto RPC traffic.
|
The TLS handshake negotiates the ALPN protocol `capnp`, after which the QUIC bi-directional stream carries Cap'n Proto RPC traffic.
|
||||||
|
|
||||||
@@ -13,29 +13,29 @@ The TLS handshake negotiates the ALPN protocol `capnp`, after which the QUIC bi-
|
|||||||
By default the client trusts exactly the certificate (or CA) in the file given by `--ca-cert`:
|
By default the client trusts exactly the certificate (or CA) in the file given by `--ca-cert`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
qpq --ca-cert data/server-cert.der --server-name localhost health --server 127.0.0.1:7000
|
qpc --ca-cert data/server-cert.der --server-name localhost health --server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
|
|
||||||
This is a form of **certificate pinning**: the client will only connect to a server whose certificate chains to the provided root. For single-server deployments, pass the server's own self-signed certificate. For CA-issued certificates, pass the CA's root certificate instead.
|
This is a form of **certificate pinning**: the client will only connect to a server whose certificate chains to the provided root. For single-server deployments, pass the server's own self-signed certificate. For CA-issued certificates, pass the CA's root certificate instead.
|
||||||
|
|
||||||
| Flag / Env var | Purpose |
|
| Flag / Env var | Purpose |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `--ca-cert` / `QPQ_CA_CERT` | Path to trusted root certificate (DER) |
|
| `--ca-cert` / `QPC_CA_CERT` | Path to trusted root certificate (DER) |
|
||||||
| `--server-name` / `QPQ_SERVER_NAME` | Expected TLS server name (must match certificate SAN) |
|
| `--server-name` / `QPC_SERVER_NAME` | Expected TLS server name (must match certificate SAN) |
|
||||||
|
|
||||||
## The `--danger-accept-invalid-certs` flag
|
## The `--danger-accept-invalid-certs` flag
|
||||||
|
|
||||||
For local development and testing you can skip certificate verification entirely:
|
For local development and testing you can skip certificate verification entirely:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
qpq --danger-accept-invalid-certs health --server 127.0.0.1:7000
|
qpc --danger-accept-invalid-certs health --server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
|
|
||||||
Or via the environment:
|
Or via the environment:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export QPQ_DANGER_ACCEPT_INVALID_CERTS=true
|
export QPC_DANGER_ACCEPT_INVALID_CERTS=true
|
||||||
qpq health --server 127.0.0.1:7000
|
qpc health --server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
|
|
||||||
When active, the client prints a warning to stderr:
|
When active, the client prints a warning to stderr:
|
||||||
@@ -50,7 +50,7 @@ WARNING: TLS verification disabled — insecure mode
|
|||||||
|
|
||||||
### Using rcgen (Rust)
|
### Using rcgen (Rust)
|
||||||
|
|
||||||
The server generates a self-signed certificate automatically when the cert/key files are missing (unless `QPQ_PRODUCTION=1` is set). The generated files are written to:
|
The server generates a self-signed certificate automatically when the cert/key files are missing (unless `QPC_PRODUCTION=1` is set). The generated files are written to:
|
||||||
|
|
||||||
- `data/server-cert.der` — DER-encoded certificate
|
- `data/server-cert.der` — DER-encoded certificate
|
||||||
- `data/server-key.der` — DER-encoded PKCS#8 private key
|
- `data/server-key.der` — DER-encoded PKCS#8 private key
|
||||||
@@ -66,7 +66,7 @@ openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
|
|||||||
-subj "/CN=localhost" \
|
-subj "/CN=localhost" \
|
||||||
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
|
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
|
||||||
|
|
||||||
# Convert to DER format (required by quicproquo)
|
# Convert to DER format (required by quicprochat)
|
||||||
openssl x509 -in cert.pem -outform DER -out data/server-cert.der
|
openssl x509 -in cert.pem -outform DER -out data/server-cert.der
|
||||||
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -out data/server-key.der -nocrypt
|
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -out data/server-key.der -nocrypt
|
||||||
```
|
```
|
||||||
@@ -74,15 +74,15 @@ openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -out data/server-key.d
|
|||||||
Point the server at the DER files:
|
Point the server at the DER files:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export QPQ_TLS_CERT=data/server-cert.der
|
export QPC_TLS_CERT=data/server-cert.der
|
||||||
export QPQ_TLS_KEY=data/server-key.der
|
export QPC_TLS_KEY=data/server-key.der
|
||||||
cargo run -p quicproquo-server
|
cargo run -p quicprochat-server
|
||||||
```
|
```
|
||||||
|
|
||||||
And the client at the certificate:
|
And the client at the certificate:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
qpq --ca-cert data/server-cert.der --server-name localhost repl
|
qpc --ca-cert data/server-cert.der --server-name localhost repl
|
||||||
```
|
```
|
||||||
|
|
||||||
## CA-issued certificates
|
## CA-issued certificates
|
||||||
@@ -94,7 +94,7 @@ For production deployments with a public CA (e.g. Let's Encrypt):
|
|||||||
3. Configure the client to trust the CA root rather than the server certificate directly:
|
3. Configure the client to trust the CA root rather than the server certificate directly:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
qpq --ca-cert /etc/ssl/certs/isrg-root-x1.der --server-name chat.example.com repl
|
qpc --ca-cert /etc/ssl/certs/isrg-root-x1.der --server-name chat.example.com repl
|
||||||
```
|
```
|
||||||
|
|
||||||
See [Certificate Lifecycle and CA-Signed TLS](certificate-lifecycle.md) for rotation, OCSP, and operational details.
|
See [Certificate Lifecycle and CA-Signed TLS](certificate-lifecycle.md) for rotation, OCSP, and operational details.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# TypeScript SDK and Browser Demo
|
# TypeScript SDK and Browser Demo
|
||||||
|
|
||||||
The TypeScript SDK (`sdks/typescript/`) provides `@quicproquo/client` — a
|
The TypeScript SDK (`sdks/typescript/`) provides `@quicprochat/client` — a
|
||||||
browser-ready client with WASM-powered crypto operations and WebSocket
|
browser-ready client with WASM-powered crypto operations and WebSocket
|
||||||
transport.
|
transport.
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ transport.
|
|||||||
|
|
||||||
- **WASM crypto bundle** (175 KB) — Ed25519 signatures, X25519 + ML-KEM-768
|
- **WASM crypto bundle** (175 KB) — Ed25519 signatures, X25519 + ML-KEM-768
|
||||||
hybrid encryption, safety numbers, sealed sender, and message padding,
|
hybrid encryption, safety numbers, sealed sender, and message padding,
|
||||||
compiled from the Rust `quicproquo-core` crate
|
compiled from the Rust `quicprochat-core` crate
|
||||||
- **`QpqClient` class** — high-level API for server connectivity and crypto
|
- **`QpqClient` class** — high-level API for server connectivity and crypto
|
||||||
- **Offline mode** — all crypto operations work without a server connection
|
- **Offline mode** — all crypto operations work without a server connection
|
||||||
- **Browser demo** — interactive HTML page for trying every crypto operation
|
- **Browser demo** — interactive HTML page for trying every crypto operation
|
||||||
@@ -33,7 +33,7 @@ cd sdks/typescript/wasm-crypto
|
|||||||
wasm-pack build --target web --out-dir ../pkg
|
wasm-pack build --target web --out-dir ../pkg
|
||||||
```
|
```
|
||||||
|
|
||||||
This compiles `quicproquo-core`'s crypto modules to WebAssembly and produces
|
This compiles `quicprochat-core`'s crypto modules to WebAssembly and produces
|
||||||
JavaScript + TypeScript bindings in `sdks/typescript/pkg/`.
|
JavaScript + TypeScript bindings in `sdks/typescript/pkg/`.
|
||||||
|
|
||||||
### 2. Build the TypeScript SDK
|
### 2. Build the TypeScript SDK
|
||||||
@@ -75,7 +75,7 @@ The crypto operations work entirely offline — no server connection needed.
|
|||||||
|
|
||||||
### Server connectivity
|
### Server connectivity
|
||||||
|
|
||||||
The Chat section of the demo connects via WebSocket. Since the native qpq
|
The Chat section of the demo connects via WebSocket. Since the native qpc
|
||||||
server speaks Cap'n Proto RPC over QUIC/TCP + Noise_XX, a WebSocket bridge
|
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
|
proxy is required for browser connectivity. The demo sends JSON-framed
|
||||||
requests over WebSocket.
|
requests over WebSocket.
|
||||||
@@ -85,7 +85,7 @@ requests over WebSocket.
|
|||||||
### Offline crypto (no server)
|
### Offline crypto (no server)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { QpqClient } from "@quicproquo/client";
|
import { QpqClient } from "@quicprochat/client";
|
||||||
|
|
||||||
const client = await QpqClient.offline();
|
const client = await QpqClient.offline();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# WASM Integration
|
# WASM Integration
|
||||||
|
|
||||||
The `quicproquo-core` crate supports compilation to `wasm32-unknown-unknown`
|
The `quicprochat-core` crate supports compilation to `wasm32-unknown-unknown`
|
||||||
when the `native` feature is disabled. This exposes the pure-crypto subset of
|
when the `native` feature is disabled. This exposes the pure-crypto subset of
|
||||||
the library for use in browsers or other WASM runtimes.
|
the library for use in browsers or other WASM runtimes.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ the library for use in browsers or other WASM runtimes.
|
|||||||
```bash
|
```bash
|
||||||
rustup target add wasm32-unknown-unknown
|
rustup target add wasm32-unknown-unknown
|
||||||
|
|
||||||
cargo build -p quicproquo-core \
|
cargo build -p quicprochat-core \
|
||||||
--target wasm32-unknown-unknown \
|
--target wasm32-unknown-unknown \
|
||||||
--no-default-features
|
--no-default-features
|
||||||
```
|
```
|
||||||
@@ -43,13 +43,13 @@ The following require the `native` feature and will not compile to WASM:
|
|||||||
- `keystore` -- OpenMLS key store with disk persistence
|
- `keystore` -- OpenMLS key store with disk persistence
|
||||||
- `opaque_auth` -- OPAQUE cipher suite configuration
|
- `opaque_auth` -- OPAQUE cipher suite configuration
|
||||||
|
|
||||||
Networking (`quicproquo-client`, `quicproquo-server`) is not available in WASM.
|
Networking (`quicprochat-client`, `quicprochat-server`) is not available in WASM.
|
||||||
|
|
||||||
## Random number generation
|
## Random number generation
|
||||||
|
|
||||||
On `wasm32`, the `getrandom` crate is configured with the `js` feature to
|
On `wasm32`, the `getrandom` crate is configured with the `js` feature to
|
||||||
use the browser's `crypto.getRandomValues()` API. This is set automatically
|
use the browser's `crypto.getRandomValues()` API. This is set automatically
|
||||||
in `quicproquo-core/Cargo.toml`:
|
in `quicprochat-core/Cargo.toml`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
@@ -71,7 +71,7 @@ cd sdks/typescript/wasm-crypto
|
|||||||
wasm-pack build --target web --out-dir ../pkg
|
wasm-pack build --target web --out-dir ../pkg
|
||||||
```
|
```
|
||||||
|
|
||||||
The resulting 175 KB WASM bundle is used by the `@quicproquo/client`
|
The resulting 175 KB WASM bundle is used by the `@quicprochat/client`
|
||||||
TypeScript SDK and the interactive browser demo.
|
TypeScript SDK and the interactive browser demo.
|
||||||
|
|
||||||
See the [TypeScript SDK and Browser Demo](typescript-sdk.md) guide for
|
See the [TypeScript SDK and Browser Demo](typescript-sdk.md) guide for
|
||||||
|
|||||||
@@ -5,15 +5,15 @@ The Authentication Service handles user registration and login via the OPAQUE as
|
|||||||
This page covers the server-side OPAQUE flow, session token lifecycle, KeyPackage storage, and hybrid key endpoints.
|
This page covers the server-side OPAQUE flow, session token lifecycle, KeyPackage storage, and hybrid key endpoints.
|
||||||
|
|
||||||
**Sources:**
|
**Sources:**
|
||||||
- `crates/quicproquo-server/src/domain/` (OPAQUE handlers, session management)
|
- `crates/quicprochat-server/src/domain/` (OPAQUE handlers, session management)
|
||||||
- `crates/quicproquo-server/src/sql_store.rs` (SqlStore persistence)
|
- `crates/quicprochat-server/src/sql_store.rs` (SqlStore persistence)
|
||||||
- `proto/qpq/v1/auth.proto` (wire schema)
|
- `proto/qpc/v1/auth.proto` (wire schema)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## OPAQUE Protocol
|
## OPAQUE Protocol
|
||||||
|
|
||||||
quicproquo uses the OPAQUE asymmetric PAKE (RFC 9497) for user authentication. The password never leaves the client and is never known to the server. The server stores an OPAQUE registration record derived from the password, but this record cannot be used to recover the password even if the server is fully compromised.
|
quicprochat uses the OPAQUE asymmetric PAKE (RFC 9497) for user authentication. The password never leaves the client and is never known to the server. The server stores an OPAQUE registration record derived from the password, but this record cannot be used to recover the password even if the server is fully compromised.
|
||||||
|
|
||||||
### Registration (IDs 100-101)
|
### Registration (IDs 100-101)
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ Returns a range of entries from the append-only log for client-side Merkle verif
|
|||||||
## Server implementation structure
|
## Server implementation structure
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Domain handler (quicproquo-server/src/domain/)
|
// Domain handler (quicprochat-server/src/domain/)
|
||||||
struct AuthHandler {
|
struct AuthHandler {
|
||||||
store: Arc<SqlStore>, // SQLCipher persistence
|
store: Arc<SqlStore>, // SQLCipher persistence
|
||||||
opaque_server: OpaqueServer, // opaque-ke server state
|
opaque_server: OpaqueServer, // opaque-ke server state
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ recipient identity key and channel identifier. The DS exposes three operations
|
|||||||
through the `NodeService` RPC interface: `enqueue`, `fetch`, and `fetchWait`.
|
through the `NodeService` RPC interface: `enqueue`, `fetch`, and `fetchWait`.
|
||||||
|
|
||||||
**Sources:**
|
**Sources:**
|
||||||
- `crates/quicproquo-server/src/main.rs` (RPC handlers)
|
- `crates/quicprochat-server/src/main.rs` (RPC handlers)
|
||||||
- `crates/quicproquo-server/src/storage.rs` (queue storage)
|
- `crates/quicprochat-server/src/storage.rs` (queue storage)
|
||||||
- `schemas/node.capnp` (wire schema)
|
- `schemas/node.capnp` (wire schema)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# GroupMember Lifecycle
|
# GroupMember Lifecycle
|
||||||
|
|
||||||
The `GroupMember` struct in `quicproquo-core` is the core MLS state machine
|
The `GroupMember` struct in `quicprochat-core` is the core MLS state machine
|
||||||
that manages a single client's membership in an MLS group. It wraps an openmls
|
that manages a single client's membership in an MLS group. It wraps an openmls
|
||||||
`MlsGroup`, a persistent crypto backend, and the long-term Ed25519 identity
|
`MlsGroup`, a persistent crypto backend, and the long-term Ed25519 identity
|
||||||
keypair. Every MLS operation -- key package generation, group creation, member
|
keypair. Every MLS operation -- key package generation, group creation, member
|
||||||
addition, joining, sending, and receiving -- flows through this struct.
|
addition, joining, sending, and receiving -- flows through this struct.
|
||||||
|
|
||||||
**Source:** `crates/quicproquo-core/src/group.rs`
|
**Source:** `crates/quicprochat-core/src/group.rs`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
MLS KeyPackages are single-use tokens that enable a group creator to add a new
|
MLS KeyPackages are single-use tokens that enable a group creator to add a new
|
||||||
member. The KeyPackage contains the member's HPKE init public key, their MLS
|
member. The KeyPackage contains the member's HPKE init public key, their MLS
|
||||||
credential (Ed25519 public key), and a signature proving ownership. The
|
credential (Ed25519 public key), and a signature proving ownership. The
|
||||||
quicproquo Authentication Service (AS) provides a simple upload/fetch
|
quicprochat Authentication Service (AS) provides a simple upload/fetch
|
||||||
interface for distributing KeyPackages between clients.
|
interface for distributing KeyPackages between clients.
|
||||||
|
|
||||||
**Expiry and refresh:** KeyPackages are consumed on fetch (single-use). The server may also enforce a TTL (e.g. 24h). Clients should upload a fresh KeyPackage periodically or on demand so they remain invitable. The CLI provides `refresh-keypackage`: load existing state, generate a new KeyPackage, upload to the AS. See [Running the Client](../getting-started/running-the-client.md#refresh-keypackage).
|
**Expiry and refresh:** KeyPackages are consumed on fetch (single-use). The server may also enforce a TTL (e.g. 24h). Clients should upload a fresh KeyPackage periodically or on demand so they remain invitable. The CLI provides `refresh-keypackage`: load existing state, generate a new KeyPackage, upload to the AS. See [Running the Client](../getting-started/running-the-client.md#refresh-keypackage).
|
||||||
@@ -12,10 +12,10 @@ This page describes the end-to-end flow: from client-side generation through
|
|||||||
server-side storage to peer-side retrieval and consumption.
|
server-side storage to peer-side retrieval and consumption.
|
||||||
|
|
||||||
**Sources:**
|
**Sources:**
|
||||||
- `crates/quicproquo-core/src/group.rs` (client-side generation)
|
- `crates/quicprochat-core/src/group.rs` (client-side generation)
|
||||||
- `crates/quicproquo-server/src/main.rs` (server-side handlers)
|
- `crates/quicprochat-server/src/main.rs` (server-side handlers)
|
||||||
- `crates/quicproquo-server/src/storage.rs` (server-side persistence)
|
- `crates/quicprochat-server/src/storage.rs` (server-side persistence)
|
||||||
- `crates/quicproquo-client/src/lib.rs` (client-side RPC calls)
|
- `crates/quicprochat-client/src/lib.rs` (client-side RPC calls)
|
||||||
- `schemas/node.capnp` (wire schema)
|
- `schemas/node.capnp` (wire schema)
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -274,10 +274,10 @@ identity; `register-state` loads from (or initializes) a persistent state file.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Ephemeral registration (for testing)
|
# Ephemeral registration (for testing)
|
||||||
qpq register --server 127.0.0.1:7000
|
qpc register --server 127.0.0.1:7000
|
||||||
|
|
||||||
# Persistent registration (production)
|
# Persistent registration (production)
|
||||||
qpq register-state --state alice.bin --server 127.0.0.1:7000
|
qpc register-state --state alice.bin --server 127.0.0.1:7000
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
@@ -292,7 +292,7 @@ KeyPackage uploaded successfully.
|
|||||||
Fetches a peer's KeyPackage by their hex-encoded Ed25519 public key:
|
Fetches a peer's KeyPackage by their hex-encoded Ed25519 public key:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
qpq fetch-key --server 127.0.0.1:7000 7a3f...
|
qpc fetch-key --server 127.0.0.1:7000 7a3f...
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Server Hooks
|
# Server Hooks
|
||||||
|
|
||||||
The `ServerHooks` trait provides a plugin system for extending the quicproquo
|
The `ServerHooks` trait provides a plugin system for extending the quicprochat
|
||||||
server. Hooks fire at key points in the request lifecycle — message delivery,
|
server. Hooks fire at key points in the request lifecycle — message delivery,
|
||||||
authentication, channel creation, and message fetch — allowing you to inspect,
|
authentication, channel creation, and message fetch — allowing you to inspect,
|
||||||
log, rate-limit, or reject operations without modifying server internals.
|
log, rate-limit, or reject operations without modifying server internals.
|
||||||
@@ -169,7 +169,7 @@ impl ServerHooks for TracingHooks {
|
|||||||
### Example: payload size limiter
|
### Example: payload size limiter
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
use quicproquo_server::hooks::{ServerHooks, HookAction, MessageEvent};
|
use quicprochat_server::hooks::{ServerHooks, HookAction, MessageEvent};
|
||||||
|
|
||||||
struct PayloadLimiter {
|
struct PayloadLimiter {
|
||||||
max_bytes: usize,
|
max_bytes: usize,
|
||||||
@@ -191,7 +191,7 @@ impl ServerHooks for PayloadLimiter {
|
|||||||
### Example: login auditor
|
### Example: login auditor
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
use quicproquo_server::hooks::{ServerHooks, AuthEvent};
|
use quicprochat_server::hooks::{ServerHooks, AuthEvent};
|
||||||
|
|
||||||
struct LoginAuditor;
|
struct LoginAuditor;
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@ impl ServerHooks for LoginAuditor {
|
|||||||
### Example: composing multiple hooks
|
### Example: composing multiple hooks
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
use quicproquo_server::hooks::*;
|
use quicprochat_server::hooks::*;
|
||||||
|
|
||||||
struct CompositeHooks {
|
struct CompositeHooks {
|
||||||
hooks: Vec<Box<dyn ServerHooks>>,
|
hooks: Vec<Box<dyn ServerHooks>>,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Storage Backend
|
# Storage Backend
|
||||||
|
|
||||||
quicproquo uses two storage backends: `SqlStore` on the server side (SQLCipher-encrypted SQLite with Argon2id key derivation) and `DiskKeyStore` on the client side (bincode-serialised file for MLS cryptographic key material).
|
quicprochat uses two storage backends: `SqlStore` on the server side (SQLCipher-encrypted SQLite with Argon2id key derivation) and `DiskKeyStore` on the client side (bincode-serialised file for MLS cryptographic key material).
|
||||||
|
|
||||||
**Sources:**
|
**Sources:**
|
||||||
- `crates/quicproquo-server/src/sql_store.rs` (SqlStore)
|
- `crates/quicprochat-server/src/sql_store.rs` (SqlStore)
|
||||||
- `crates/quicproquo-server/src/storage.rs` (Store trait, FileBackedStore legacy)
|
- `crates/quicprochat-server/src/storage.rs` (Store trait, FileBackedStore legacy)
|
||||||
- `crates/quicproquo-core/src/keystore.rs` (DiskKeyStore, StoreCrypto)
|
- `crates/quicprochat-core/src/keystore.rs` (DiskKeyStore, StoreCrypto)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ Persistent mode is used for production clients. The key store path is derived fr
|
|||||||
|
|
||||||
MLS entities MUST use bincode serialisation. The `DiskKeyStore` implements this with a two-layer scheme:
|
MLS entities MUST use bincode serialisation. The `DiskKeyStore` implements this with a two-layer scheme:
|
||||||
|
|
||||||
1. **Inner layer:** Each MLS entity value (`V: MlsEntity`) is serialised using the openmls-required serialisation format. The `DiskKeyStore` in quicproquo uses bincode for MLS entity values, matching the `OpenMlsKeyStore` trait requirements.
|
1. **Inner layer:** Each MLS entity value (`V: MlsEntity`) is serialised using the openmls-required serialisation format. The `DiskKeyStore` in quicprochat uses bincode for MLS entity values, matching the `OpenMlsKeyStore` trait requirements.
|
||||||
2. **Outer layer:** The entire `HashMap<Vec<u8>, Vec<u8>>` is bincode-serialised as the file on disk.
|
2. **Outer layer:** The entire `HashMap<Vec<u8>, Vec<u8>>` is bincode-serialised as the file on disk.
|
||||||
|
|
||||||
**Important:** Do not use Protobuf or JSON for MLS entities. MLS requires bincode for the `DiskKeyStore` in this codebase. Using a different format will produce incompatible key material.
|
**Important:** Do not use Protobuf or JSON for MLS entities. MLS requires bincode for the `DiskKeyStore` in this codebase. Using a different format will produce incompatible key material.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
**quicproquo** is a research-oriented, end-to-end encrypted group messaging system written in Rust. It layers the Messaging Layer Security protocol (MLS, [RFC 9420](https://datatracker.ietf.org/doc/rfc9420/)) on top of QUIC + TLS 1.3 transport (via [quinn](https://github.com/quinn-rs/quinn) and [rustls](https://github.com/rustls/rustls)), with all service RPCs framed using a compact binary Protocol Buffers format over a custom framing layer. The project exists to explore how modern transport encryption (QUIC), a formally specified group key agreement protocol (MLS), and post-quantum hybrid key encapsulation compose in practice -- and to provide a readable, auditable reference implementation for security researchers, protocol designers, and Rust developers who want to study or extend the design.
|
**quicprochat** is a research-oriented, end-to-end encrypted group messaging system written in Rust. It layers the Messaging Layer Security protocol (MLS, [RFC 9420](https://datatracker.ietf.org/doc/rfc9420/)) on top of QUIC + TLS 1.3 transport (via [quinn](https://github.com/quinn-rs/quinn) and [rustls](https://github.com/rustls/rustls)), with all service RPCs framed using a compact binary Protocol Buffers format over a custom framing layer. The project exists to explore how modern transport encryption (QUIC), a formally specified group key agreement protocol (MLS), and post-quantum hybrid key encapsulation compose in practice -- and to provide a readable, auditable reference implementation for security researchers, protocol designers, and Rust developers who want to study or extend the design.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -18,9 +18,9 @@
|
|||||||
|
|
||||||
Each layer addresses a distinct concern:
|
Each layer addresses a distinct concern:
|
||||||
|
|
||||||
1. **QUIC + TLS 1.3** provides authenticated, confidential transport with 0-RTT connection establishment and multiplexed streams. The server presents a TLS 1.3 certificate (self-signed by default); the client verifies it against a local trust anchor. ALPN negotiation uses the token `qpq`.
|
1. **QUIC + TLS 1.3** provides authenticated, confidential transport with 0-RTT connection establishment and multiplexed streams. The server presents a TLS 1.3 certificate (self-signed by default); the client verifies it against a local trust anchor. ALPN negotiation uses the token `qpc`.
|
||||||
|
|
||||||
2. **Protobuf framing** defines the wire format for all service operations across 44 RPC methods. Each request carries a `[method_id: u16][request_id: u32][payload_len: u32]` header followed by a Protobuf-encoded payload. Server-to-client push events use a separate frame type on QUIC uni-streams. Message definitions live in `proto/qpq/v1/*.proto` and are compiled to Rust with `prost` at build time.
|
2. **Protobuf framing** defines the wire format for all service operations across 44 RPC methods. Each request carries a `[method_id: u16][request_id: u32][payload_len: u32]` header followed by a Protobuf-encoded payload. Server-to-client push events use a separate frame type on QUIC uni-streams. Message definitions live in `proto/qpc/v1/*.proto` and are compiled to Rust with `prost` at build time.
|
||||||
|
|
||||||
3. **MLS (RFC 9420)** provides the group key agreement layer. Each participant holds an Ed25519 identity keypair and generates single-use HPKE KeyPackages. The MLS epoch ratchet delivers forward secrecy and post-compromise security: compromising a member's state at epoch *n* does not reveal plaintext from epochs *< n* (forward secrecy) or *> n+1* (post-compromise security, once the compromised member updates).
|
3. **MLS (RFC 9420)** provides the group key agreement layer. Each participant holds an Ed25519 identity keypair and generates single-use HPKE KeyPackages. The MLS epoch ratchet delivers forward secrecy and post-compromise security: compromising a member's state at epoch *n* does not reveal plaintext from epochs *< n* (forward secrecy) or *> n+1* (post-compromise security, once the compromised member updates).
|
||||||
|
|
||||||
@@ -53,12 +53,12 @@ For a deeper discussion of the cryptographic guarantees, threat model, and known
|
|||||||
|
|
||||||
**Security researchers** studying how MLS composes with QUIC transport and post-quantum hybrid KEM. The codebase spans 9 workspace crates with clear cryptographic boundaries for auditability.
|
**Security researchers** studying how MLS composes with QUIC transport and post-quantum hybrid KEM. The codebase spans 9 workspace 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.
|
**Protocol designers** evaluating MLS deployment patterns. quicprochat 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 the Rust SDK:
|
**Application developers** building on the platform via the Rust SDK:
|
||||||
|
|
||||||
- **`quicproquo-sdk`** -- `QpqClient` with async event streams and a `ConversationStore`
|
- **`quicprochat-sdk`** -- `QpqClient` with async event streams and a `ConversationStore`
|
||||||
- **C FFI** -- cross-language integration via `quicproquo-plugin-api`
|
- **C FFI** -- cross-language integration via `quicprochat-plugin-api`
|
||||||
|
|
||||||
**Rust developers** looking for a working example of:
|
**Rust developers** looking for a working example of:
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ For a deeper discussion of the cryptographic guarantees, threat model, and known
|
|||||||
|
|
||||||
| Section | What you will find |
|
| Section | What you will find |
|
||||||
|---|---|
|
|---|---|
|
||||||
| **[Comparison with Classical Protocols](design-rationale/protocol-comparison.md)** | **Why quicproquo? IRC+SSL, XMPP, Telegram vs. our design** |
|
| **[Comparison with Classical Protocols](design-rationale/protocol-comparison.md)** | **Why quicprochat? IRC+SSL, XMPP, Telegram vs. our design** |
|
||||||
| [Prerequisites](getting-started/prerequisites.md) | Toolchain and system dependencies |
|
| [Prerequisites](getting-started/prerequisites.md) | Toolchain and system dependencies |
|
||||||
| [Building from Source](getting-started/building.md) | `cargo build`, Protobuf codegen, troubleshooting |
|
| [Building from Source](getting-started/building.md) | `cargo build`, Protobuf codegen, troubleshooting |
|
||||||
| [Running the Server](getting-started/running-the-server.md) | Server startup, configuration, TLS cert generation |
|
| [Running the Server](getting-started/running-the-server.md) | Server startup, configuration, TLS cert generation |
|
||||||
@@ -90,7 +90,7 @@ For a deeper discussion of the cryptographic guarantees, threat model, and known
|
|||||||
|
|
||||||
## Current status
|
## Current status
|
||||||
|
|
||||||
quicproquo is a **research project** with production-grade features. It has
|
quicprochat is a **research project** with production-grade features. It has
|
||||||
not been audited by a third party. The test suite covers 301 tests across
|
not been audited by a third party. The test suite covers 301 tests across
|
||||||
core, server, client, E2E, and P2P modules.
|
core, server, client, E2E, and P2P modules.
|
||||||
|
|
||||||
@@ -120,4 +120,4 @@ For the full milestone tracker, see [Milestones](roadmap/milestones.md).
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
quicproquo is released under the **MIT** license. See `LICENSE` in the repository root.
|
quicprochat is released under the **MIT** license. See `LICENSE` in the repository root.
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
# Backup and Restore Procedures
|
# Backup and Restore Procedures
|
||||||
|
|
||||||
This document covers backup and restore for all quicproquo server data stores.
|
This document covers backup and restore for all quicprochat server data stores.
|
||||||
|
|
||||||
## Data Inventory
|
## Data Inventory
|
||||||
|
|
||||||
| Data | Location | Backend | Contains |
|
| Data | Location | Backend | Contains |
|
||||||
|------|----------|---------|----------|
|
|------|----------|---------|----------|
|
||||||
| SQLCipher DB | `QPQ_DB_PATH` (default `data/qpq.db`) | `store_backend=sql` | Users, key packages, delivery queues, sessions, KT log, OPAQUE setup, blobs metadata, moderation |
|
| SQLCipher DB | `QPC_DB_PATH` (default `data/qpc.db`) | `store_backend=sql` | Users, key packages, delivery queues, sessions, KT log, OPAQUE setup, blobs metadata, moderation |
|
||||||
| File store | `QPQ_DATA_DIR` (default `data/`) | `store_backend=file` | Bincode-serialized key packages, delivery queues, server state |
|
| File store | `QPC_DATA_DIR` (default `data/`) | `store_backend=file` | Bincode-serialized key packages, delivery queues, server state |
|
||||||
| Blob storage | `QPQ_DATA_DIR/blobs/` | Filesystem | Uploaded file transfer blobs |
|
| Blob storage | `QPC_DATA_DIR/blobs/` | Filesystem | Uploaded file transfer blobs |
|
||||||
| TLS certificates | `QPQ_TLS_CERT`, `QPQ_TLS_KEY` | DER files | Server identity |
|
| TLS certificates | `QPC_TLS_CERT`, `QPC_TLS_KEY` | DER files | Server identity |
|
||||||
| OPAQUE ServerSetup | Inside DB or file store | Persisted | OPAQUE credential state (critical for auth) |
|
| OPAQUE ServerSetup | Inside DB or file store | Persisted | OPAQUE credential state (critical for auth) |
|
||||||
| Server signing key | Inside DB or file store | Persisted | Ed25519 key for delivery proofs |
|
| Server signing key | Inside DB or file store | Persisted | Ed25519 key for delivery proofs |
|
||||||
| KT Merkle log | Inside DB or file store | Persisted | Key transparency audit log |
|
| KT Merkle log | Inside DB or file store | Persisted | Key transparency audit log |
|
||||||
@@ -23,13 +23,13 @@ allows concurrent readers).
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Open the encrypted database with the same key
|
# 1. Open the encrypted database with the same key
|
||||||
sqlite3 data/qpq.db
|
sqlite3 data/qpc.db
|
||||||
|
|
||||||
# 2. At the sqlite3 prompt, set the encryption key
|
# 2. At the sqlite3 prompt, set the encryption key
|
||||||
PRAGMA key = 'your-db-key-here';
|
PRAGMA key = 'your-db-key-here';
|
||||||
|
|
||||||
# 3. Perform an online backup
|
# 3. Perform an online backup
|
||||||
.backup /backups/qpq-$(date +%Y%m%d-%H%M%S).db
|
.backup /backups/qpc-$(date +%Y%m%d-%H%M%S).db
|
||||||
|
|
||||||
.quit
|
.quit
|
||||||
```
|
```
|
||||||
@@ -40,11 +40,11 @@ PRAGMA key = 'your-db-key-here';
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
BACKUP_DIR="/backups/qpq"
|
BACKUP_DIR="/backups/qpc"
|
||||||
DB_PATH="${QPQ_DB_PATH:-data/qpq.db}"
|
DB_PATH="${QPC_DB_PATH:-data/qpc.db}"
|
||||||
DB_KEY="${QPQ_DB_KEY}"
|
DB_KEY="${QPC_DB_KEY}"
|
||||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||||
BACKUP_FILE="${BACKUP_DIR}/qpq-${TIMESTAMP}.db"
|
BACKUP_FILE="${BACKUP_DIR}/qpc-${TIMESTAMP}.db"
|
||||||
|
|
||||||
mkdir -p "$BACKUP_DIR"
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
@@ -59,41 +59,41 @@ sqlite3 "$BACKUP_FILE" "PRAGMA key = '${DB_KEY}'; PRAGMA integrity_check;" \
|
|||||||
|| { echo "ERROR: backup verification failed"; exit 1; }
|
|| { echo "ERROR: backup verification failed"; exit 1; }
|
||||||
|
|
||||||
# Retain last 7 daily backups
|
# Retain last 7 daily backups
|
||||||
find "$BACKUP_DIR" -name 'qpq-*.db' -mtime +7 -delete
|
find "$BACKUP_DIR" -name 'qpc-*.db' -mtime +7 -delete
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cold Backup (Offline)
|
### Cold Backup (Offline)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Stop the server
|
# 1. Stop the server
|
||||||
systemctl stop qpq-server # or docker compose stop server
|
systemctl stop qpc-server # or docker compose stop server
|
||||||
|
|
||||||
# 2. Copy the database file
|
# 2. Copy the database file
|
||||||
cp data/qpq.db /backups/qpq-$(date +%Y%m%d).db
|
cp data/qpc.db /backups/qpc-$(date +%Y%m%d).db
|
||||||
|
|
||||||
# 3. Copy the WAL and SHM files if they exist
|
# 3. Copy the WAL and SHM files if they exist
|
||||||
cp data/qpq.db-wal /backups/ 2>/dev/null || true
|
cp data/qpc.db-wal /backups/ 2>/dev/null || true
|
||||||
cp data/qpq.db-shm /backups/ 2>/dev/null || true
|
cp data/qpc.db-shm /backups/ 2>/dev/null || true
|
||||||
|
|
||||||
# 4. Restart the server
|
# 4. Restart the server
|
||||||
systemctl start qpq-server
|
systemctl start qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
## File Backend Backup
|
## File Backend Backup
|
||||||
|
|
||||||
When using `store_backend=file`, data is stored as bincode files under
|
When using `store_backend=file`, data is stored as bincode files under
|
||||||
`QPQ_DATA_DIR`.
|
`QPC_DATA_DIR`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Full directory backup
|
# Full directory backup
|
||||||
tar czf /backups/qpq-data-$(date +%Y%m%d-%H%M%S).tar.gz \
|
tar czf /backups/qpc-data-$(date +%Y%m%d-%H%M%S).tar.gz \
|
||||||
-C "$(dirname "${QPQ_DATA_DIR:-data}")" \
|
-C "$(dirname "${QPC_DATA_DIR:-data}")" \
|
||||||
"$(basename "${QPQ_DATA_DIR:-data}")"
|
"$(basename "${QPC_DATA_DIR:-data}")"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Blob Storage Backup
|
## Blob Storage Backup
|
||||||
|
|
||||||
Blobs are stored in `QPQ_DATA_DIR/blobs/`. These are immutable once written.
|
Blobs are stored in `QPC_DATA_DIR/blobs/`. These are immutable once written.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Incremental rsync (blobs are write-once, ideal for rsync)
|
# Incremental rsync (blobs are write-once, ideal for rsync)
|
||||||
@@ -119,38 +119,38 @@ cp data/federation-ca.der /backups/tls/federation-ca.der 2>/dev/null || true
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Stop the server
|
# 1. Stop the server
|
||||||
systemctl stop qpq-server
|
systemctl stop qpc-server
|
||||||
|
|
||||||
# 2. Move the current (corrupt/lost) database aside
|
# 2. Move the current (corrupt/lost) database aside
|
||||||
mv data/qpq.db data/qpq.db.broken 2>/dev/null || true
|
mv data/qpc.db data/qpc.db.broken 2>/dev/null || true
|
||||||
rm -f data/qpq.db-wal data/qpq.db-shm
|
rm -f data/qpc.db-wal data/qpc.db-shm
|
||||||
|
|
||||||
# 3. Copy the backup in place
|
# 3. Copy the backup in place
|
||||||
cp /backups/qpq-20260304.db data/qpq.db
|
cp /backups/qpc-20260304.db data/qpc.db
|
||||||
|
|
||||||
# 4. Verify integrity
|
# 4. Verify integrity
|
||||||
sqlite3 data/qpq.db "PRAGMA key = '${QPQ_DB_KEY}'; PRAGMA integrity_check;"
|
sqlite3 data/qpc.db "PRAGMA key = '${QPC_DB_KEY}'; PRAGMA integrity_check;"
|
||||||
|
|
||||||
# 5. Start the server (migrations will apply automatically if needed)
|
# 5. Start the server (migrations will apply automatically if needed)
|
||||||
systemctl start qpq-server
|
systemctl start qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
### Restore File Backend
|
### Restore File Backend
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Stop the server
|
# 1. Stop the server
|
||||||
systemctl stop qpq-server
|
systemctl stop qpc-server
|
||||||
|
|
||||||
# 2. Replace the data directory
|
# 2. Replace the data directory
|
||||||
mv data data.broken 2>/dev/null || true
|
mv data data.broken 2>/dev/null || true
|
||||||
tar xzf /backups/qpq-data-20260304.tar.gz -C .
|
tar xzf /backups/qpc-data-20260304.tar.gz -C .
|
||||||
|
|
||||||
# 3. Restore TLS certs if not included in the data backup
|
# 3. Restore TLS certs if not included in the data backup
|
||||||
cp /backups/tls/server-cert.der data/server-cert.der
|
cp /backups/tls/server-cert.der data/server-cert.der
|
||||||
cp /backups/tls/server-key.der data/server-key.der
|
cp /backups/tls/server-key.der data/server-key.der
|
||||||
|
|
||||||
# 4. Start the server
|
# 4. Start the server
|
||||||
systemctl start qpq-server
|
systemctl start qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
### Restore Blobs Only
|
### Restore Blobs Only
|
||||||
@@ -172,16 +172,16 @@ rsync -av /backups/blobs/ data/blobs/
|
|||||||
|
|
||||||
```cron
|
```cron
|
||||||
# SQLCipher hot backup every 6 hours
|
# SQLCipher hot backup every 6 hours
|
||||||
0 */6 * * * /opt/qpq/scripts/backup-db.sh >> /var/log/qpq-backup.log 2>&1
|
0 */6 * * * /opt/qpc/scripts/backup-db.sh >> /var/log/qpc-backup.log 2>&1
|
||||||
|
|
||||||
# Full data directory daily at 02:00
|
# Full data directory daily at 02:00
|
||||||
0 2 * * * tar czf /backups/qpq-data-$(date +\%Y\%m\%d).tar.gz -C /var/lib quicproquo
|
0 2 * * * tar czf /backups/qpc-data-$(date +\%Y\%m\%d).tar.gz -C /var/lib quicprochat
|
||||||
|
|
||||||
# Blob sync every hour
|
# Blob sync every hour
|
||||||
0 * * * * rsync -a /var/lib/quicproquo/blobs/ /backups/blobs/
|
0 * * * * rsync -a /var/lib/quicprochat/blobs/ /backups/blobs/
|
||||||
|
|
||||||
# Prune backups older than 30 days
|
# Prune backups older than 30 days
|
||||||
0 3 * * 0 find /backups -name 'qpq-*' -mtime +30 -delete
|
0 3 * * 0 find /backups -name 'qpc-*' -mtime +30 -delete
|
||||||
```
|
```
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
@@ -190,11 +190,11 @@ Always verify backups after creation:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# SQLCipher integrity check
|
# SQLCipher integrity check
|
||||||
sqlite3 /backups/qpq-latest.db \
|
sqlite3 /backups/qpc-latest.db \
|
||||||
"PRAGMA key = '${QPQ_DB_KEY}'; PRAGMA integrity_check; SELECT count(*) FROM users;"
|
"PRAGMA key = '${QPC_DB_KEY}'; PRAGMA integrity_check; SELECT count(*) FROM users;"
|
||||||
|
|
||||||
# File backend: check the archive is valid
|
# File backend: check the archive is valid
|
||||||
tar tzf /backups/qpq-data-latest.tar.gz > /dev/null
|
tar tzf /backups/qpc-data-latest.tar.gz > /dev/null
|
||||||
|
|
||||||
# TLS cert: check it parses and is not expired
|
# TLS cert: check it parses and is not expired
|
||||||
openssl x509 -inform DER -in /backups/tls/server-cert.der -noout -dates
|
openssl x509 -inform DER -in /backups/tls/server-cert.der -noout -dates
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Monitoring Guide
|
# Monitoring Guide
|
||||||
|
|
||||||
This document covers metrics collection, alerting, and dashboards for
|
This document covers metrics collection, alerting, and dashboards for
|
||||||
quicproquo server deployments.
|
quicprochat server deployments.
|
||||||
|
|
||||||
## Enabling Metrics
|
## Enabling Metrics
|
||||||
|
|
||||||
@@ -9,10 +9,10 @@ The server exports Prometheus metrics via HTTP when configured:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Environment variables
|
# Environment variables
|
||||||
QPQ_METRICS_LISTEN=0.0.0.0:9090
|
QPC_METRICS_LISTEN=0.0.0.0:9090
|
||||||
QPQ_METRICS_ENABLED=true
|
QPC_METRICS_ENABLED=true
|
||||||
|
|
||||||
# Or in qpq-server.toml
|
# Or in qpc-server.toml
|
||||||
metrics_listen = "0.0.0.0:9090"
|
metrics_listen = "0.0.0.0:9090"
|
||||||
metrics_enabled = true
|
metrics_enabled = true
|
||||||
```
|
```
|
||||||
@@ -50,9 +50,9 @@ global:
|
|||||||
evaluation_interval: 15s
|
evaluation_interval: 15s
|
||||||
|
|
||||||
scrape_configs:
|
scrape_configs:
|
||||||
- job_name: 'qpq-server'
|
- job_name: 'qpc-server'
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ['qpq-server:9090']
|
- targets: ['qpc-server:9090']
|
||||||
scrape_interval: 10s
|
scrape_interval: 10s
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -61,17 +61,17 @@ scrape_configs:
|
|||||||
```yaml
|
```yaml
|
||||||
# prometheus-alerts.yml
|
# prometheus-alerts.yml
|
||||||
groups:
|
groups:
|
||||||
- name: qpq-server
|
- name: qpc-server
|
||||||
rules:
|
rules:
|
||||||
# Server down
|
# Server down
|
||||||
- alert: QpqServerDown
|
- alert: QpqServerDown
|
||||||
expr: up{job="qpq-server"} == 0
|
expr: up{job="qpc-server"} == 0
|
||||||
for: 1m
|
for: 1m
|
||||||
labels:
|
labels:
|
||||||
severity: critical
|
severity: critical
|
||||||
annotations:
|
annotations:
|
||||||
summary: "qpq-server is down"
|
summary: "qpc-server is down"
|
||||||
description: "Prometheus cannot scrape qpq-server metrics for > 1 minute."
|
description: "Prometheus cannot scrape qpc-server metrics for > 1 minute."
|
||||||
|
|
||||||
# High auth failure rate (potential brute force)
|
# High auth failure rate (potential brute force)
|
||||||
- alert: QpqHighAuthFailureRate
|
- alert: QpqHighAuthFailureRate
|
||||||
@@ -138,7 +138,7 @@ groups:
|
|||||||
|
|
||||||
## Key Dashboard Panels
|
## Key Dashboard Panels
|
||||||
|
|
||||||
See `dashboards/qpq-overview.json` for the full Grafana dashboard. Key panels:
|
See `dashboards/qpc-overview.json` for the full Grafana dashboard. Key panels:
|
||||||
|
|
||||||
### Message Throughput
|
### Message Throughput
|
||||||
|
|
||||||
@@ -167,10 +167,10 @@ See `dashboards/qpq-overview.json` for the full Grafana dashboard. Key panels:
|
|||||||
|
|
||||||
## Grafana Dashboard
|
## Grafana Dashboard
|
||||||
|
|
||||||
Import the dashboard from `dashboards/qpq-overview.json`:
|
Import the dashboard from `dashboards/qpc-overview.json`:
|
||||||
|
|
||||||
1. Open Grafana -> Dashboards -> Import
|
1. Open Grafana -> Dashboards -> Import
|
||||||
2. Upload `docs/operations/dashboards/qpq-overview.json`
|
2. Upload `docs/operations/dashboards/qpc-overview.json`
|
||||||
3. Select your Prometheus data source
|
3. Select your Prometheus data source
|
||||||
4. Save
|
4. Save
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ The server uses `tracing` with `RUST_LOG` environment variable:
|
|||||||
RUST_LOG=info
|
RUST_LOG=info
|
||||||
|
|
||||||
# Debug specific modules
|
# Debug specific modules
|
||||||
RUST_LOG=info,quicproquo_server::node_service=debug
|
RUST_LOG=info,quicprochat_server::node_service=debug
|
||||||
|
|
||||||
# Verbose debugging
|
# Verbose debugging
|
||||||
RUST_LOG=debug
|
RUST_LOG=debug
|
||||||
@@ -196,7 +196,7 @@ RUST_LOG=debug
|
|||||||
| `"TLS certificate expires within 30 days"` | Cert expiring soon | Rotate certificate |
|
| `"TLS certificate expires within 30 days"` | Cert expiring soon | Rotate certificate |
|
||||||
| `"TLS certificate is self-signed"` | Self-signed cert in use | Replace with CA-signed cert in production |
|
| `"TLS certificate is self-signed"` | Self-signed cert in use | Replace with CA-signed cert in production |
|
||||||
| `"connection rate limit exceeded"` | IP being rate limited | Check for DDoS |
|
| `"connection rate limit exceeded"` | IP being rate limited | Check for DDoS |
|
||||||
| `"running without QPQ_AUTH_TOKEN"` | Insecure mode | Must not appear in production |
|
| `"running without QPC_AUTH_TOKEN"` | Insecure mode | Must not appear in production |
|
||||||
| `"db_key is empty; SQL store will be plaintext"` | Unencrypted DB | Must not appear in production |
|
| `"db_key is empty; SQL store will be plaintext"` | Unencrypted DB | Must not appear in production |
|
||||||
| `"shutdown signal received"` | Graceful shutdown started | Expected during deploys |
|
| `"shutdown signal received"` | Graceful shutdown started | Expected during deploys |
|
||||||
| `"generated and persisted new OPAQUE ServerSetup"` | Fresh OPAQUE setup | Expected on first start only |
|
| `"generated and persisted new OPAQUE ServerSetup"` | Fresh OPAQUE setup | Expected on first start only |
|
||||||
@@ -207,13 +207,13 @@ For production, pipe logs to a log aggregator:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Systemd -> journald -> Loki/Elasticsearch
|
# Systemd -> journald -> Loki/Elasticsearch
|
||||||
journalctl -u qpq-server -f --output=json | \
|
journalctl -u qpc-server -f --output=json | \
|
||||||
promtail --stdin --client.url=http://loki:3100/loki/api/v1/push
|
promtail --stdin --client.url=http://loki:3100/loki/api/v1/push
|
||||||
|
|
||||||
# Docker -> Loki driver
|
# Docker -> Loki driver
|
||||||
docker run --log-driver=loki \
|
docker run --log-driver=loki \
|
||||||
--log-opt loki-url="http://loki:3100/loki/api/v1/push" \
|
--log-opt loki-url="http://loki:3100/loki/api/v1/push" \
|
||||||
qpq-server
|
qpc-server
|
||||||
```
|
```
|
||||||
|
|
||||||
## Health Checking
|
## Health Checking
|
||||||
@@ -229,5 +229,5 @@ ss -ulnp | grep 5001
|
|||||||
curl -sf http://localhost:9090/metrics > /dev/null
|
curl -sf http://localhost:9090/metrics > /dev/null
|
||||||
|
|
||||||
# Full client connection test
|
# Full client connection test
|
||||||
qpq-client --server 127.0.0.1:5001 --ping
|
qpc-client --server 127.0.0.1:5001 --ping
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Scaling Guide
|
# Scaling Guide
|
||||||
|
|
||||||
This document covers resource sizing, scaling triggers, and capacity planning
|
This document covers resource sizing, scaling triggers, and capacity planning
|
||||||
for quicproquo deployments.
|
for quicprochat deployments.
|
||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
quicproquo runs as a single-process server handling QUIC connections. Key
|
quicprochat runs as a single-process server handling QUIC connections. Key
|
||||||
resource consumers:
|
resource consumers:
|
||||||
|
|
||||||
- **CPU**: TLS 1.3 handshakes (QUIC), OPAQUE PAKE authentication, message routing
|
- **CPU**: TLS 1.3 handshakes (QUIC), OPAQUE PAKE authentication, message routing
|
||||||
@@ -73,7 +73,7 @@ handshakes and OPAQUE computations are CPU-intensive.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check current CPU usage
|
# Check current CPU usage
|
||||||
top -bn1 -p $(pgrep qpq-server)
|
top -bn1 -p $(pgrep qpc-server)
|
||||||
|
|
||||||
# For Docker: increase CPU limits in docker-compose.prod.yml
|
# For Docker: increase CPU limits in docker-compose.prod.yml
|
||||||
# deploy:
|
# deploy:
|
||||||
@@ -109,24 +109,24 @@ SQLCipher uses WAL mode for concurrent reads. For write-heavy workloads:
|
|||||||
iostat -x 1 5
|
iostat -x 1 5
|
||||||
|
|
||||||
# Increase WAL autocheckpoint threshold for burst writes
|
# Increase WAL autocheckpoint threshold for burst writes
|
||||||
sqlite3 data/qpq.db "PRAGMA key='${QPQ_DB_KEY}'; PRAGMA wal_autocheckpoint=2000;"
|
sqlite3 data/qpc.db "PRAGMA key='${QPC_DB_KEY}'; PRAGMA wal_autocheckpoint=2000;"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Horizontal Scaling
|
## Horizontal Scaling
|
||||||
|
|
||||||
quicproquo does not yet have built-in multi-node clustering. For horizontal
|
quicprochat does not yet have built-in multi-node clustering. For horizontal
|
||||||
scaling, use these patterns:
|
scaling, use these patterns:
|
||||||
|
|
||||||
### Load Balancer (UDP/QUIC)
|
### Load Balancer (UDP/QUIC)
|
||||||
|
|
||||||
Place a UDP load balancer in front of multiple qpq-server instances. Each
|
Place a UDP load balancer in front of multiple qpc-server instances. Each
|
||||||
instance runs independently with its own database.
|
instance runs independently with its own database.
|
||||||
|
|
||||||
```
|
```
|
||||||
+-----------+
|
+-----------+
|
||||||
clients ------> | L4 LB | ----> qpq-server-1 (db-1)
|
clients ------> | L4 LB | ----> qpc-server-1 (db-1)
|
||||||
| (UDP/QUIC)| ----> qpq-server-2 (db-2)
|
| (UDP/QUIC)| ----> qpc-server-2 (db-2)
|
||||||
+-----------+ qpq-server-3 (db-3)
|
+-----------+ qpc-server-3 (db-3)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Requirements:**
|
**Requirements:**
|
||||||
@@ -140,7 +140,7 @@ instance runs independently with its own database.
|
|||||||
Enable federation to relay messages between nodes:
|
Enable federation to relay messages between nodes:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# qpq-server.toml on node-1
|
# qpc-server.toml on node-1
|
||||||
[federation]
|
[federation]
|
||||||
enabled = true
|
enabled = true
|
||||||
domain = "node1.chat.example.com"
|
domain = "node1.chat.example.com"
|
||||||
@@ -160,9 +160,9 @@ For true horizontal scaling, migrating from SQLCipher to a shared PostgreSQL
|
|||||||
instance is the planned approach. This is not yet implemented.
|
instance is the planned approach. This is not yet implemented.
|
||||||
|
|
||||||
```
|
```
|
||||||
qpq-server-1 --\
|
qpc-server-1 --\
|
||||||
qpq-server-2 ---+--> PostgreSQL (shared)
|
qpc-server-2 ---+--> PostgreSQL (shared)
|
||||||
qpq-server-3 --/
|
qpc-server-3 --/
|
||||||
```
|
```
|
||||||
|
|
||||||
## Connection Tuning
|
## Connection Tuning
|
||||||
@@ -181,7 +181,7 @@ For high connection counts:
|
|||||||
# Increase OS file descriptor limit
|
# Increase OS file descriptor limit
|
||||||
ulimit -n 65536
|
ulimit -n 65536
|
||||||
|
|
||||||
# Increase UDP buffer sizes in /etc/sysctl.d/99-qpq.conf
|
# Increase UDP buffer sizes in /etc/sysctl.d/99-qpc.conf
|
||||||
net.core.rmem_max = 26214400
|
net.core.rmem_max = 26214400
|
||||||
net.core.wmem_max = 26214400
|
net.core.wmem_max = 26214400
|
||||||
net.core.rmem_default = 1048576
|
net.core.rmem_default = 1048576
|
||||||
@@ -189,7 +189,7 @@ net.core.wmem_default = 1048576
|
|||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sysctl -p /etc/sysctl.d/99-qpq.conf
|
sysctl -p /etc/sysctl.d/99-qpc.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker Resource Limits
|
## Docker Resource Limits
|
||||||
@@ -218,11 +218,11 @@ Use the included test infrastructure to benchmark:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build the test client
|
# Build the test client
|
||||||
cargo build --release --bin qpq-client
|
cargo build --release --bin qpc-client
|
||||||
|
|
||||||
# Run concurrent connection test (example)
|
# Run concurrent connection test (example)
|
||||||
for i in $(seq 1 100); do
|
for i in $(seq 1 100); do
|
||||||
qpq-client --server 127.0.0.1:5001 &
|
qpc-client --server 127.0.0.1:5001 &
|
||||||
done
|
done
|
||||||
wait
|
wait
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Protobuf Framing
|
# Protobuf Framing
|
||||||
|
|
||||||
quicproquo v2 uses a custom binary framing protocol layered over QUIC bidirectional streams. Message payloads are serialised with Protocol Buffers (Protobuf) via the `prost` crate. The framing layer (implemented in `quicproquo-rpc`) adds a compact fixed-size header that carries the method ID, request correlation ID, and payload length -- enabling zero-copy dispatch without a separate length-delimited codec.
|
quicprochat v2 uses a custom binary framing protocol layered over QUIC bidirectional streams. Message payloads are serialised with Protocol Buffers (Protobuf) via the `prost` crate. The framing layer (implemented in `quicprochat-rpc`) adds a compact fixed-size header that carries the method ID, request correlation ID, and payload length -- enabling zero-copy dispatch without a separate length-delimited codec.
|
||||||
|
|
||||||
This page covers the three frame types, the method ID dispatch table, status codes, push event delivery, and the Protobuf schema organisation.
|
This page covers the three frame types, the method ID dispatch table, status codes, push event delivery, and the Protobuf schema organisation.
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ The `status` byte in a Response frame carries one of the following values:
|
|||||||
|
|
||||||
## Method IDs
|
## Method IDs
|
||||||
|
|
||||||
All 44 RPC method IDs are defined in `crates/quicproquo-proto/src/lib.rs` in the `method_ids` module. The numeric ranges group related methods by service category.
|
All 44 RPC method IDs are defined in `crates/quicprochat-proto/src/lib.rs` in the `method_ids` module. The numeric ranges group related methods by service category.
|
||||||
|
|
||||||
### Auth (100-103)
|
### Auth (100-103)
|
||||||
|
|
||||||
@@ -230,7 +230,7 @@ All 44 RPC method IDs are defined in `crates/quicproquo-proto/src/lib.rs` in the
|
|||||||
|
|
||||||
## Push Event Types
|
## Push Event Types
|
||||||
|
|
||||||
Server-to-client push events are delivered on QUIC uni-streams using the Push frame format. Event types are defined alongside method IDs in `quicproquo-proto/src/lib.rs`:
|
Server-to-client push events are delivered on QUIC uni-streams using the Push frame format. Event types are defined alongside method IDs in `quicprochat-proto/src/lib.rs`:
|
||||||
|
|
||||||
| Value | Event | Description |
|
| Value | Event | Description |
|
||||||
|-------|-------|-------------|
|
|-------|-------|-------------|
|
||||||
@@ -261,7 +261,7 @@ This allows many concurrent RPCs on a single QUIC connection without head-of-lin
|
|||||||
|
|
||||||
## Protobuf Schema Organisation
|
## Protobuf Schema Organisation
|
||||||
|
|
||||||
All message types are defined in `proto/qpq/v1/`:
|
All message types are defined in `proto/qpc/v1/`:
|
||||||
|
|
||||||
| File | Contents |
|
| File | Contents |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -280,22 +280,22 @@ All message types are defined in `proto/qpq/v1/`:
|
|||||||
| `p2p.proto` | P2P endpoints, health |
|
| `p2p.proto` | P2P endpoints, health |
|
||||||
| `federation.proto` | Cross-server relay |
|
| `federation.proto` | Cross-server relay |
|
||||||
|
|
||||||
All `.proto` files use `package qpq.v1;` and are compiled to Rust at build time using `prost-build` via the `quicproquo-proto` crate's `build.rs`. The `protobuf-src` crate vendors `protoc`, so no system-wide `protoc` installation is required.
|
All `.proto` files use `package qpc.v1;` and are compiled to Rust at build time using `prost-build` via the `quicprochat-proto` crate's `build.rs`. The `protobuf-src` crate vendors `protoc`, so no system-wide `protoc` installation is required.
|
||||||
|
|
||||||
Generated Rust types are accessed via:
|
Generated Rust types are accessed via:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use quicproquo_proto::qpq::v1::{EnqueueRequest, FetchResponse, /* ... */};
|
use quicprochat_proto::qpc::v1::{EnqueueRequest, FetchResponse, /* ... */};
|
||||||
use quicproquo_proto::method_ids::{ENQUEUE, FETCH, /* ... */};
|
use quicprochat_proto::method_ids::{ENQUEUE, FETCH, /* ... */};
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Design Constraints of `quicproquo-proto`
|
## Design Constraints of `quicprochat-proto`
|
||||||
|
|
||||||
The `quicproquo-proto` crate enforces three constraints:
|
The `quicprochat-proto` crate enforces three constraints:
|
||||||
|
|
||||||
1. **No crypto**: Key material never enters this crate. All encryption and signing happens in `quicproquo-core`.
|
1. **No crypto**: Key material never enters this crate. All encryption and signing happens in `quicprochat-core`.
|
||||||
2. **No I/O**: Callers own the transport. This crate only converts between bytes and types.
|
2. **No I/O**: Callers own the transport. This crate only converts between bytes and types.
|
||||||
3. **No async**: Pure synchronous data-layer code. Async is the caller's responsibility.
|
3. **No async**: Pure synchronous data-layer code. Async is the caller's responsibility.
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Hybrid KEM: X25519 + ML-KEM-768
|
# Hybrid KEM: X25519 + ML-KEM-768
|
||||||
|
|
||||||
quicproquo implements a hybrid Key Encapsulation Mechanism that combines classical X25519 Diffie-Hellman with post-quantum ML-KEM-768 (FIPS 203). The hybrid construction ensures that the system remains secure even if one of the two components is broken: X25519 protects against failures in ML-KEM, and ML-KEM protects against quantum computers breaking X25519.
|
quicprochat implements a hybrid Key Encapsulation Mechanism that combines classical X25519 Diffie-Hellman with post-quantum ML-KEM-768 (FIPS 203). The hybrid construction ensures that the system remains secure even if one of the two components is broken: X25519 protects against failures in ML-KEM, and ML-KEM protects against quantum computers breaking X25519.
|
||||||
|
|
||||||
The implementation lives in `quicproquo-core/src/hybrid_kem.rs`. It is fully implemented and tested but **not yet integrated into the MLS ciphersuite** -- integration is planned for the M5 milestone. Currently, the module can be used as a standalone envelope encryption layer to wrap MLS payloads in an outer post-quantum-resistant encryption before they transit the network.
|
The implementation lives in `quicprochat-core/src/hybrid_kem.rs`. It is fully implemented and tested but **not yet integrated into the MLS ciphersuite** -- integration is planned for the M5 milestone. Currently, the module can be used as a standalone envelope encryption layer to wrap MLS payloads in an outer post-quantum-resistant encryption before they transit the network.
|
||||||
|
|
||||||
## Design approach
|
## Design approach
|
||||||
|
|
||||||
@@ -70,8 +70,8 @@ The two shared secrets are combined via HKDF-SHA256 with domain separation:
|
|||||||
ikm = X25519_shared_secret(32 bytes) || ML-KEM_shared_secret(32 bytes)
|
ikm = X25519_shared_secret(32 bytes) || ML-KEM_shared_secret(32 bytes)
|
||||||
salt = [] (empty)
|
salt = [] (empty)
|
||||||
|
|
||||||
key = HKDF-SHA256(salt, ikm, info="quicproquo-hybrid-v1", L=32)
|
key = HKDF-SHA256(salt, ikm, info="quicprochat-hybrid-v1", L=32)
|
||||||
nonce = HKDF-SHA256(salt, ikm, info="quicproquo-hybrid-nonce-v1", L=12)
|
nonce = HKDF-SHA256(salt, ikm, info="quicprochat-hybrid-nonce-v1", L=12)
|
||||||
```
|
```
|
||||||
|
|
||||||
The implementation in `derive_aead_material()`:
|
The implementation in `derive_aead_material()`:
|
||||||
@@ -85,10 +85,10 @@ fn derive_aead_material(x25519_ss: &[u8], mlkem_ss: &[u8]) -> (Key, Nonce) {
|
|||||||
let hk = Hkdf::<Sha256>::new(None, &ikm);
|
let hk = Hkdf::<Sha256>::new(None, &ikm);
|
||||||
|
|
||||||
let mut key_bytes = Zeroizing::new([0u8; 32]);
|
let mut key_bytes = Zeroizing::new([0u8; 32]);
|
||||||
hk.expand(b"quicproquo-hybrid-v1", &mut *key_bytes).unwrap();
|
hk.expand(b"quicprochat-hybrid-v1", &mut *key_bytes).unwrap();
|
||||||
|
|
||||||
let mut nonce_bytes = [0u8; 12];
|
let mut nonce_bytes = [0u8; 12];
|
||||||
hk.expand(b"quicproquo-hybrid-nonce-v1", &mut nonce_bytes).unwrap();
|
hk.expand(b"quicprochat-hybrid-nonce-v1", &mut nonce_bytes).unwrap();
|
||||||
|
|
||||||
(*Key::from_slice(&*key_bytes), *Nonce::from_slice(&nonce_bytes))
|
(*Key::from_slice(&*key_bytes), *Nonce::from_slice(&nonce_bytes))
|
||||||
}
|
}
|
||||||
@@ -273,7 +273,7 @@ The AEAD nonce is derived deterministically from the shared secrets via HKDF. Si
|
|||||||
|
|
||||||
## Further reading
|
## Further reading
|
||||||
|
|
||||||
- [Post-Quantum Readiness](../cryptography/post-quantum-readiness.md) -- Broader discussion of quicproquo's PQ strategy.
|
- [Post-Quantum Readiness](../cryptography/post-quantum-readiness.md) -- Broader discussion of quicprochat's PQ strategy.
|
||||||
- [MLS (RFC 9420)](mls.md) -- The MLS layer that the hybrid KEM will wrap.
|
- [MLS (RFC 9420)](mls.md) -- The MLS layer that the hybrid KEM will wrap.
|
||||||
- [Key Lifecycle and Zeroization](../cryptography/key-lifecycle.md) -- How hybrid key material is managed and cleared.
|
- [Key Lifecycle and Zeroization](../cryptography/key-lifecycle.md) -- How hybrid key material is managed and cleared.
|
||||||
- [Threat Model](../cryptography/threat-model.md) -- Where hybrid KEM fits in the overall threat model.
|
- [Threat Model](../cryptography/threat-model.md) -- Where hybrid KEM fits in the overall threat model.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# MLS (RFC 9420)
|
# MLS (RFC 9420)
|
||||||
|
|
||||||
The Messaging Layer Security protocol (RFC 9420) is the core cryptographic layer in quicproquo. It provides authenticated group key agreement with forward secrecy and post-compromise security -- properties that distinguish quicproquo from a simple transport-encrypted relay. This is the most detailed page in the Protocol Deep Dives section because MLS is the most complex layer in the stack.
|
The Messaging Layer Security protocol (RFC 9420) is the core cryptographic layer in quicprochat. It provides authenticated group key agreement with forward secrecy and post-compromise security -- properties that distinguish quicprochat from a simple transport-encrypted relay. This is the most detailed page in the Protocol Deep Dives section because MLS is the most complex layer in the stack.
|
||||||
|
|
||||||
The implementation lives in `quicproquo-core/src/group.rs` and `quicproquo-core/src/keystore.rs`, using the `openmls 0.5` crate.
|
The implementation lives in `quicprochat-core/src/group.rs` and `quicprochat-core/src/keystore.rs`, using the `openmls 0.5` crate.
|
||||||
|
|
||||||
## Background: what problem MLS solves
|
## Background: what problem MLS solves
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ MLS takes a fundamentally different approach: it uses a **ratchet tree** (a bina
|
|||||||
|
|
||||||
## Ciphersuite
|
## Ciphersuite
|
||||||
|
|
||||||
quicproquo uses:
|
quicprochat uses:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
|
MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
|
||||||
@@ -38,7 +38,7 @@ This ciphersuite provides 128-bit classical security. Post-quantum protection is
|
|||||||
|
|
||||||
## The `GroupMember` state machine
|
## The `GroupMember` state machine
|
||||||
|
|
||||||
The central type is `GroupMember`, defined in `quicproquo-core/src/group.rs`. It wraps an openmls `MlsGroup`, a persistent crypto backend (`StoreCrypto`), and the user's long-term Ed25519 identity keypair.
|
The central type is `GroupMember`, defined in `quicprochat-core/src/group.rs`. It wraps an openmls `MlsGroup`, a persistent crypto backend (`StoreCrypto`), and the user's long-term Ed25519 identity keypair.
|
||||||
|
|
||||||
### Lifecycle diagram
|
### Lifecycle diagram
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ pub fn create_group(&mut self, group_id: &[u8]) -> Result<(), CoreError>
|
|||||||
Creates a new MLS group at epoch 0 with the caller as the sole member.
|
Creates a new MLS group at epoch 0 with the caller as the sole member.
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `group_id`: Any non-empty byte string. By convention, quicproquo uses the SHA-256 digest of a human-readable group name.
|
- `group_id`: Any non-empty byte string. By convention, quicprochat uses the SHA-256 digest of a human-readable group name.
|
||||||
|
|
||||||
**What happens internally:**
|
**What happens internally:**
|
||||||
|
|
||||||
@@ -259,7 +259,7 @@ Processes an incoming TLS-encoded MLS message.
|
|||||||
|
|
||||||
## The `StoreCrypto` backend
|
## The `StoreCrypto` backend
|
||||||
|
|
||||||
The `StoreCrypto` struct (in `quicproquo-core/src/keystore.rs`) implements `OpenMlsCryptoProvider`, which openmls requires for all cryptographic operations:
|
The `StoreCrypto` struct (in `quicprochat-core/src/keystore.rs`) implements `OpenMlsCryptoProvider`, which openmls requires for all cryptographic operations:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub struct StoreCrypto {
|
pub struct StoreCrypto {
|
||||||
@@ -318,11 +318,11 @@ KeyPackageIn::tls_deserialize(&mut bytes.as_ref())?
|
|||||||
|
|
||||||
### Feature-gated methods
|
### Feature-gated methods
|
||||||
|
|
||||||
Several convenient methods (`into_welcome()`, `into_protocol_message()`) are feature-gated behind openmls feature flags that quicproquo does not enable. The workaround is to use `msg_in.extract()` and pattern-match on the `MlsMessageInBody` enum variants.
|
Several convenient methods (`into_welcome()`, `into_protocol_message()`) are feature-gated behind openmls feature flags that quicprochat does not enable. The workaround is to use `msg_in.extract()` and pattern-match on the `MlsMessageInBody` enum variants.
|
||||||
|
|
||||||
### MlsGroup is not Send
|
### MlsGroup is not Send
|
||||||
|
|
||||||
`MlsGroup` holds internal state that may not be `Send` depending on the crypto backend. In quicproquo, `StoreCrypto` uses `RwLock` (which is `Send + Sync`), so `GroupMember` is `Send`. However, all MLS operations must use the same backend instance, so `GroupMember` should not be cloned across tasks.
|
`MlsGroup` holds internal state that may not be `Send` depending on the crypto backend. In quicprochat, `StoreCrypto` uses `RwLock` (which is `Send + Sync`), so `GroupMember` is `Send`. However, all MLS operations must use the same backend instance, so `GroupMember` should not be cloned across tasks.
|
||||||
|
|
||||||
## Ratchet tree embedding
|
## Ratchet tree embedding
|
||||||
|
|
||||||
@@ -335,7 +335,7 @@ The trade-off:
|
|||||||
- **Pro**: No need for a separate tree distribution service or additional round-trips.
|
- **Pro**: No need for a separate tree distribution service or additional round-trips.
|
||||||
- **Con**: Welcome messages grow with the group size (O(n log n) for a balanced tree of n members).
|
- **Con**: Welcome messages grow with the group size (O(n log n) for a balanced tree of n members).
|
||||||
|
|
||||||
For quicproquo's target group sizes (2-100 members), this trade-off is acceptable.
|
For quicprochat's target group sizes (2-100 members), this trade-off is acceptable.
|
||||||
|
|
||||||
## Wire format
|
## Wire format
|
||||||
|
|
||||||
@@ -386,7 +386,7 @@ The following sequence shows a complete Alice-and-Bob scenario, matching the `tw
|
|||||||
|
|
||||||
## Credential model
|
## Credential model
|
||||||
|
|
||||||
quicproquo uses MLS `Basic` credentials. The credential body is the raw Ed25519 public key bytes (32 bytes), and the `signature_key` is the same public key:
|
quicprochat uses MLS `Basic` credentials. The credential body is the raw Ed25519 public key bytes (32 bytes), and the `signature_key` is the same public key:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let credential = Credential::new(
|
let credential = Credential::new(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Protocol Layers Overview
|
# Protocol Layers Overview
|
||||||
|
|
||||||
quicproquo composes four distinct protocol layers into a single security stack. Each layer addresses a specific class of threat and delegates everything else to the layers above or below it. No single layer is sufficient on its own; the composition is what delivers end-to-end confidentiality, server authentication, forward secrecy, post-compromise security, and post-quantum resistance.
|
quicprochat composes four distinct protocol layers into a single security stack. Each layer addresses a specific class of threat and delegates everything else to the layers above or below it. No single layer is sufficient on its own; the composition is what delivers end-to-end confidentiality, server authentication, forward secrecy, post-compromise security, and post-quantum resistance.
|
||||||
|
|
||||||
This page provides a high-level comparison and a suggested reading order. The deep-dive pages that follow contain implementation details drawn directly from the source code.
|
This page provides a high-level comparison and a suggested reading order. The deep-dive pages that follow contain implementation details drawn directly from the source code.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ This page provides a high-level comparison and a suggested reading order. The de
|
|||||||
| Layer | Standard / Spec | Crate(s) | Security Properties |
|
| Layer | Standard / Spec | Crate(s) | Security Properties |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **QUIC + TLS 1.3** | RFC 9000, RFC 9001 | `quinn 0.11`, `rustls 0.23` | Transport confidentiality, server authentication, 0-RTT resumption |
|
| **QUIC + TLS 1.3** | RFC 9000, RFC 9001 | `quinn 0.11`, `rustls 0.23` | Transport confidentiality, server authentication, 0-RTT resumption |
|
||||||
| **Protobuf framing** | Custom binary header + [Protocol Buffers](https://protobuf.dev/) | `quicproquo-rpc`, `prost 0.13` | Typed length-prefixed frames, method dispatch, push events, status codes |
|
| **Protobuf framing** | Custom binary header + [Protocol Buffers](https://protobuf.dev/) | `quicprochat-rpc`, `prost 0.13` | Typed length-prefixed frames, method dispatch, push events, status codes |
|
||||||
| **MLS** | [RFC 9420](https://www.rfc-editor.org/rfc/rfc9420.html) | `openmls 0.5` | Group key agreement, forward secrecy, post-compromise security (PCS) |
|
| **MLS** | [RFC 9420](https://www.rfc-editor.org/rfc/rfc9420.html) | `openmls 0.5` | Group key agreement, forward secrecy, post-compromise security (PCS) |
|
||||||
| **Hybrid KEM** | [draft-ietf-tls-hybrid-design](https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/) | `ml-kem 0.2`, `x25519-dalek 2` | Post-quantum resistance via ML-KEM-768 combined with X25519 |
|
| **Hybrid KEM** | [draft-ietf-tls-hybrid-design](https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/) | `ml-kem 0.2`, `x25519-dalek 2` | Post-quantum resistance via ML-KEM-768 combined with X25519 |
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ The pages in this section are ordered to build understanding incrementally:
|
|||||||
|
|
||||||
1. **[QUIC + TLS 1.3](quic-tls.md)** -- Start here. This is the transport layer that every client-server connection uses. Understanding QUIC stream multiplexing and the TLS 1.3 handshake is prerequisite to understanding how the Protobuf framing protocol rides on top.
|
1. **[QUIC + TLS 1.3](quic-tls.md)** -- Start here. This is the transport layer that every client-server connection uses. Understanding QUIC stream multiplexing and the TLS 1.3 handshake is prerequisite to understanding how the Protobuf framing protocol rides on top.
|
||||||
|
|
||||||
2. **[MLS (RFC 9420)](mls.md)** -- The core cryptographic innovation. MLS provides the group key agreement that makes quicproquo an E2E encrypted group messenger rather than just a transport-encrypted relay. This is the longest and most detailed page.
|
2. **[MLS (RFC 9420)](mls.md)** -- The core cryptographic innovation. MLS provides the group key agreement that makes quicprochat an E2E encrypted group messenger rather than just a transport-encrypted relay. This is the longest and most detailed page.
|
||||||
|
|
||||||
3. **[Protobuf Framing](capn-proto.md)** -- The framing and RPC layer that bridges MLS application data with the transport. Understanding the three frame types (Request, Response, Push), the method ID dispatch table, and status codes is essential for reading the server and client source code.
|
3. **[Protobuf Framing](capn-proto.md)** -- The framing and RPC layer that bridges MLS application data with the transport. Understanding the three frame types (Request, Response, Push), the method ID dispatch table, and status codes is essential for reading the server and client source code.
|
||||||
|
|
||||||
@@ -71,10 +71,10 @@ Each protocol layer maps to one or more workspace crates:
|
|||||||
|
|
||||||
| Layer | Primary Crate | Source File(s) |
|
| Layer | Primary Crate | Source File(s) |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| QUIC + TLS 1.3 | `quicproquo-server`, `quicproquo-client` | Server and client entry points |
|
| QUIC + TLS 1.3 | `quicprochat-server`, `quicprochat-client` | Server and client entry points |
|
||||||
| Protobuf framing | `quicproquo-rpc` | `src/framing.rs`, `src/server.rs`, `src/client.rs` |
|
| Protobuf framing | `quicprochat-rpc` | `src/framing.rs`, `src/server.rs`, `src/client.rs` |
|
||||||
| Protobuf types + method IDs | `quicproquo-proto` | `src/lib.rs` (method_ids), `proto/qpq/v1/*.proto` |
|
| Protobuf types + method IDs | `quicprochat-proto` | `src/lib.rs` (method_ids), `proto/qpc/v1/*.proto` |
|
||||||
| MLS | `quicproquo-core` | `src/group.rs`, `src/keystore.rs` |
|
| MLS | `quicprochat-core` | `src/group.rs`, `src/keystore.rs` |
|
||||||
| Hybrid KEM | `quicproquo-core` | `src/hybrid_kem.rs` |
|
| Hybrid KEM | `quicprochat-core` | `src/hybrid_kem.rs` |
|
||||||
|
|
||||||
For a full crate responsibility breakdown, see [Crate Responsibilities](../architecture/crate-responsibilities.md).
|
For a full crate responsibility breakdown, see [Crate Responsibilities](../architecture/crate-responsibilities.md).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# QUIC + TLS 1.3
|
# QUIC + TLS 1.3
|
||||||
|
|
||||||
quicproquo uses QUIC (RFC 9000) with mandatory TLS 1.3 (RFC 9001) as its transport layer. This page explains how the `quinn` and `rustls` crates are integrated and what security properties the transport provides.
|
quicprochat uses QUIC (RFC 9000) with mandatory TLS 1.3 (RFC 9001) as its transport layer. This page explains how the `quinn` and `rustls` crates are integrated and what security properties the transport provides.
|
||||||
|
|
||||||
## Why QUIC
|
## Why QUIC
|
||||||
|
|
||||||
@@ -15,11 +15,11 @@ QUIC provides several advantages over traditional TCP-based transports:
|
|||||||
|
|
||||||
## Crate integration
|
## Crate integration
|
||||||
|
|
||||||
quicproquo uses the following crates for QUIC and TLS:
|
quicprochat uses the following crates for QUIC and TLS:
|
||||||
|
|
||||||
- **`quinn 0.11`** -- The async QUIC implementation for Tokio. Provides `Endpoint`, `Connection`, and bidirectional/uni-directional stream types.
|
- **`quinn 0.11`** -- The async QUIC implementation for Tokio. Provides `Endpoint`, `Connection`, and bidirectional/uni-directional stream types.
|
||||||
- **`quinn-proto 0.11`** -- The protocol-level types, including `QuicServerConfig` and `QuicClientConfig` wrappers that bridge `rustls` into `quinn`.
|
- **`quinn-proto 0.11`** -- The protocol-level types, including `QuicServerConfig` and `QuicClientConfig` wrappers that bridge `rustls` into `quinn`.
|
||||||
- **`rustls 0.23`** -- The TLS implementation. quicproquo uses it in strict TLS 1.3 mode with no fallback to TLS 1.2.
|
- **`rustls 0.23`** -- The TLS implementation. quicprochat uses it in strict TLS 1.3 mode with no fallback to TLS 1.2.
|
||||||
- **`rcgen 0.13`** -- Self-signed certificate generation for development and testing.
|
- **`rcgen 0.13`** -- Self-signed certificate generation for development and testing.
|
||||||
|
|
||||||
### Server configuration
|
### Server configuration
|
||||||
@@ -30,7 +30,7 @@ The server builds its QUIC endpoint configuration with:
|
|||||||
let mut tls = rustls::ServerConfig::builder_with_protocol_versions(&[&TLS13])
|
let mut tls = rustls::ServerConfig::builder_with_protocol_versions(&[&TLS13])
|
||||||
.with_no_client_auth()
|
.with_no_client_auth()
|
||||||
.with_single_cert(cert_chain, key)?;
|
.with_single_cert(cert_chain, key)?;
|
||||||
tls.alpn_protocols = vec![b"qpq".to_vec()];
|
tls.alpn_protocols = vec![b"qpc".to_vec()];
|
||||||
|
|
||||||
let crypto = QuicServerConfig::try_from(tls)?;
|
let crypto = QuicServerConfig::try_from(tls)?;
|
||||||
Ok(ServerConfig::with_crypto(Arc::new(crypto)))
|
Ok(ServerConfig::with_crypto(Arc::new(crypto)))
|
||||||
@@ -38,11 +38,11 @@ Ok(ServerConfig::with_crypto(Arc::new(crypto)))
|
|||||||
|
|
||||||
Key points:
|
Key points:
|
||||||
|
|
||||||
1. **TLS 1.3 strict mode**: `builder_with_protocol_versions(&[&TLS13])` ensures no TLS 1.2 fallback. This is a hard requirement: TLS 1.2 lacks the 0-RTT and full forward secrecy guarantees that quicproquo relies on.
|
1. **TLS 1.3 strict mode**: `builder_with_protocol_versions(&[&TLS13])` ensures no TLS 1.2 fallback. This is a hard requirement: TLS 1.2 lacks the 0-RTT and full forward secrecy guarantees that quicprochat relies on.
|
||||||
|
|
||||||
2. **No client certificate authentication**: `with_no_client_auth()` means the server does not verify client certificates at the TLS layer. Client authentication is handled at the application layer via OPAQUE password authentication and Ed25519 identity keys. This is a deliberate design choice -- OPAQUE provides stronger authentication properties than TLS client certificates without requiring PKI infrastructure.
|
2. **No client certificate authentication**: `with_no_client_auth()` means the server does not verify client certificates at the TLS layer. Client authentication is handled at the application layer via OPAQUE password authentication and Ed25519 identity keys. This is a deliberate design choice -- OPAQUE provides stronger authentication properties than TLS client certificates without requiring PKI infrastructure.
|
||||||
|
|
||||||
3. **ALPN negotiation**: The Application-Layer Protocol Negotiation extension is set to `b"qpq"`, advertising that this endpoint speaks the quicproquo v2 Protobuf framing protocol. Both client and server must agree on this protocol identifier or the TLS handshake fails.
|
3. **ALPN negotiation**: The Application-Layer Protocol Negotiation extension is set to `b"qpc"`, advertising that this endpoint speaks the quicprochat v2 Protobuf framing protocol. Both client and server must agree on this protocol identifier or the TLS handshake fails.
|
||||||
|
|
||||||
4. **`QuicServerConfig` bridge**: The `quinn-proto` crate provides `QuicServerConfig::try_from(tls)` to adapt the `rustls::ServerConfig` for use with QUIC. This handles the QUIC-specific TLS parameters (transport parameters, QUIC header protection keys) automatically.
|
4. **`QuicServerConfig` bridge**: The `quinn-proto` crate provides `QuicServerConfig::try_from(tls)` to adapt the `rustls::ServerConfig` for use with QUIC. This handles the QUIC-specific TLS parameters (transport parameters, QUIC header protection keys) automatically.
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ roots.add(CertificateDer::from(cert_bytes))?;
|
|||||||
let mut tls = rustls::ClientConfig::builder_with_protocol_versions(&[&TLS13])
|
let mut tls = rustls::ClientConfig::builder_with_protocol_versions(&[&TLS13])
|
||||||
.with_root_certificates(roots)
|
.with_root_certificates(roots)
|
||||||
.with_no_client_auth();
|
.with_no_client_auth();
|
||||||
tls.alpn_protocols = vec![b"qpq".to_vec()];
|
tls.alpn_protocols = vec![b"qpc".to_vec()];
|
||||||
|
|
||||||
let crypto = QuicClientConfig::try_from(tls)?;
|
let crypto = QuicClientConfig::try_from(tls)?;
|
||||||
```
|
```
|
||||||
@@ -89,11 +89,11 @@ Unlike the v1 Cap'n Proto RPC (which required `tokio::task::LocalSet` due to
|
|||||||
|
|
||||||
## Certificate trust model
|
## Certificate trust model
|
||||||
|
|
||||||
quicproquo currently uses a **trust-on-first-use (TOFU)** model with self-signed certificates:
|
quicprochat currently uses a **trust-on-first-use (TOFU)** model with self-signed certificates:
|
||||||
|
|
||||||
1. On first start, the server generates a self-signed certificate using `rcgen::generate_simple_self_signed` with SANs for `localhost`, `127.0.0.1`, and `::1`.
|
1. On first start, the server generates a self-signed certificate using `rcgen::generate_simple_self_signed` with SANs for `localhost`, `127.0.0.1`, and `::1`.
|
||||||
2. The certificate and private key are persisted to disk as DER files (default: `data/server-cert.der` and `data/server-key.der`).
|
2. The certificate and private key are persisted to disk as DER files (default: `data/server-cert.der` and `data/server-key.der`).
|
||||||
3. Clients must obtain the server's certificate file out-of-band and reference it via the `--ca-cert` flag or `QPQ_CA_CERT` environment variable.
|
3. Clients must obtain the server's certificate file out-of-band and reference it via the `--ca-cert` flag or `QPC_CA_CERT` environment variable.
|
||||||
|
|
||||||
This model is adequate for development and single-server deployments. The roadmap includes:
|
This model is adequate for development and single-server deployments. The roadmap includes:
|
||||||
|
|
||||||
@@ -143,18 +143,18 @@ The QUIC + TLS 1.3 layer provides:
|
|||||||
|
|
||||||
| Environment Variable | CLI Flag | Default | Description |
|
| Environment Variable | CLI Flag | Default | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `QPQ_LISTEN` | `--listen` | `0.0.0.0:5001` | QUIC listen address |
|
| `QPC_LISTEN` | `--listen` | `0.0.0.0:5001` | QUIC listen address |
|
||||||
| `QPQ_TLS_CERT` | `--tls-cert` | `data/server-cert.der` | TLS certificate path |
|
| `QPC_TLS_CERT` | `--tls-cert` | `data/server-cert.der` | TLS certificate path |
|
||||||
| `QPQ_TLS_KEY` | `--tls-key` | `data/server-key.der` | TLS private key path |
|
| `QPC_TLS_KEY` | `--tls-key` | `data/server-key.der` | TLS private key path |
|
||||||
| `QPQ_DATA_DIR` | `--data-dir` | `data` | Persistent storage directory |
|
| `QPC_DATA_DIR` | `--data-dir` | `data` | Persistent storage directory |
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
|
|
||||||
| Environment Variable | CLI Flag | Default | Description |
|
| Environment Variable | CLI Flag | Default | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `QPQ_CA_CERT` | `--ca-cert` | `data/server-cert.der` | Server certificate to trust |
|
| `QPC_CA_CERT` | `--ca-cert` | `data/server-cert.der` | Server certificate to trust |
|
||||||
| `QPQ_SERVER_NAME` | `--server-name` | `localhost` | Expected TLS server name (must match certificate SAN) |
|
| `QPC_SERVER_NAME` | `--server-name` | `localhost` | Expected TLS server name (must match certificate SAN) |
|
||||||
| `QPQ_SERVER` | `--server` | `127.0.0.1:5001` | Server address (per-subcommand) |
|
| `QPC_SERVER` | `--server` | `127.0.0.1:5001` | Server address (per-subcommand) |
|
||||||
|
|
||||||
## Further reading
|
## Further reading
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Auth, Devices, and Tokens
|
# Auth, Devices, and Tokens
|
||||||
|
|
||||||
This page describes the authentication, device management, and authorisation
|
This page describes the authentication, device management, and authorisation
|
||||||
design for quicproquo. It introduces account and device identities, gates
|
design for quicprochat. It introduces account and device identities, gates
|
||||||
server operations by authenticated identity, enforces rate and size limits, and
|
server operations by authenticated identity, enforces rate and size limits, and
|
||||||
binds MLS identity keys to accounts.
|
binds MLS identity keys to accounts.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# 1:1 Channel Design
|
# 1:1 Channel Design
|
||||||
|
|
||||||
This page describes the design for first-class 1:1 (direct message) channels in
|
This page describes the design for first-class 1:1 (direct message) channels in
|
||||||
quicproquo. Channels provide per-conversation authorisation, MLS-encrypted
|
quicprochat. Channels provide per-conversation authorisation, MLS-encrypted
|
||||||
payloads, message retention with TTL eviction, and backward compatibility with
|
payloads, message retention with TTL eviction, and backward compatibility with
|
||||||
the legacy delivery model.
|
the legacy delivery model.
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ Not strictly required for “operational” but expected for production deployme
|
|||||||
## 3. Roadmap and Documentation Updates
|
## 3. Roadmap and Documentation Updates
|
||||||
|
|
||||||
- **Milestones doc:** Mark M4 as **Complete** (CLI subcommands exist). Mark M6 as **Complete** (migrations + runner; server and client persistence in place). Leave M5 as **Next** and M7 as **Planned**.
|
- **Milestones doc:** Mark M4 as **Complete** (CLI subcommands exist). Mark M6 as **Complete** (migrations + runner; server and client persistence in place). Leave M5 as **Next** and M7 as **Planned**.
|
||||||
- **README:** Update milestone table to reflect M4 and M6 complete; add one line on migrations (e.g. “Server supports SQL migrations under `quicproquo-server/migrations/`”).
|
- **README:** Update milestone table to reflect M4 and M6 complete; add one line on migrations (e.g. “Server supports SQL migrations under `quicprochat-server/migrations/`”).
|
||||||
- **Migration convention:** Document in README or a dev doc: add new migrations as `NNN_name.sql`, add to `MIGRATIONS` in `sql_store.rs`, bump `SCHEMA_VERSION`.
|
- **Migration convention:** Document in README or a dev doc: add new migrations as `NNN_name.sql`, add to `MIGRATIONS` in `sql_store.rs`, bump `SCHEMA_VERSION`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Future Research Directions
|
# Future Research Directions
|
||||||
|
|
||||||
This page catalogues technologies and research directions that could strengthen
|
This page catalogues technologies and research directions that could strengthen
|
||||||
quicproquo beyond the current [milestone plan](milestones.md). Each entry
|
quicprochat beyond the current [milestone plan](milestones.md). Each entry
|
||||||
includes a brief description, the problem it solves, relevant crates or
|
includes a brief description, the problem it solves, relevant crates or
|
||||||
specifications, and how it maps to the project architecture.
|
specifications, and how it maps to the project architecture.
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ vulnerable to harvest-now-decrypt-later attacks.
|
|||||||
hybrid Ed25519 + ML-DSA-65 for credential signatures. The `ml-kem` crate is
|
hybrid Ed25519 + ML-DSA-65 for credential signatures. The `ml-kem` crate is
|
||||||
already vendored in the workspace.
|
already vendored in the workspace.
|
||||||
|
|
||||||
**Architecture impact:** Custom `OpenMlsCryptoProvider` in `quicproquo-core`
|
**Architecture impact:** Custom `OpenMlsCryptoProvider` in `quicprochat-core`
|
||||||
implementing the hybrid combiner. This is the M7 milestone -- see
|
implementing the hybrid combiner. This is the M7 milestone -- see
|
||||||
[Milestones](milestones.md#m7----post-quantum-planned) and
|
[Milestones](milestones.md#m7----post-quantum-planned) and
|
||||||
[Hybrid KEM](../protocol-layers/hybrid-kem.md).
|
[Hybrid KEM](../protocol-layers/hybrid-kem.md).
|
||||||
@@ -186,7 +186,7 @@ admin could require proof of organization membership before allowing join.
|
|||||||
**Problem:** A single server is a single point of failure and a single point of
|
**Problem:** A single server is a single point of failure and a single point of
|
||||||
trust. Users on different servers cannot communicate.
|
trust. Users on different servers cannot communicate.
|
||||||
|
|
||||||
**Solution:** Federation allows multiple quicproquo servers to exchange
|
**Solution:** Federation allows multiple quicprochat servers to exchange
|
||||||
messages, similar to [Matrix](https://matrix.org/) homeserver federation. Each
|
messages, similar to [Matrix](https://matrix.org/) homeserver federation. Each
|
||||||
server manages its own users and relays messages to peer servers.
|
server manages its own users and relays messages to peer servers.
|
||||||
|
|
||||||
@@ -278,10 +278,10 @@ the user base for testing and demonstration.
|
|||||||
|
|
||||||
**Solution:** [Tauri](https://tauri.app/) or [Dioxus](https://dioxuslabs.com/)
|
**Solution:** [Tauri](https://tauri.app/) or [Dioxus](https://dioxuslabs.com/)
|
||||||
provide native cross-platform GUI frameworks in Rust. The
|
provide native cross-platform GUI frameworks in Rust. The
|
||||||
`quicproquo-core` crate can be shared directly with the GUI client.
|
`quicprochat-core` crate can be shared directly with the GUI client.
|
||||||
|
|
||||||
**Architecture impact:** Add a `quicproquo-gui` crate that depends on
|
**Architecture impact:** Add a `quicprochat-gui` crate that depends on
|
||||||
`quicproquo-core` and `quicproquo-proto`. The GUI drives the same
|
`quicprochat-core` and `quicprochat-proto`. The GUI drives the same
|
||||||
`GroupMember` and RPC logic as the CLI client.
|
`GroupMember` and RPC logic as the CLI client.
|
||||||
|
|
||||||
**Crates:** `tauri`, `dioxus`
|
**Crates:** `tauri`, `dioxus`
|
||||||
@@ -294,7 +294,7 @@ provide native cross-platform GUI frameworks in Rust. The
|
|||||||
[diplomat](https://github.com/nickelc/diplomat) generate idiomatic Swift and
|
[diplomat](https://github.com/nickelc/diplomat) generate idiomatic Swift and
|
||||||
Kotlin bindings from Rust definitions.
|
Kotlin bindings from Rust definitions.
|
||||||
|
|
||||||
**Architecture impact:** Expose `quicproquo-core` through a C-compatible FFI
|
**Architecture impact:** Expose `quicprochat-core` through a C-compatible FFI
|
||||||
layer. Mobile apps call into the Rust crypto and protocol logic.
|
layer. Mobile apps call into the Rust crypto and protocol logic.
|
||||||
|
|
||||||
**Crates:** `uniffi`, `diplomat`
|
**Crates:** `uniffi`, `diplomat`
|
||||||
@@ -325,7 +325,7 @@ Items marked **Implemented** are already part of the v2 codebase.
|
|||||||
| -- | **Post-quantum hybrid KEM** | `ml-kem` vendored; custom `OpenMlsCryptoProvider` with X25519 + ML-KEM-768. | **Implemented** |
|
| -- | **Post-quantum hybrid KEM** | `ml-kem` vendored; custom `OpenMlsCryptoProvider` with X25519 + ML-KEM-768. | **Implemented** |
|
||||||
| -- | **SQLCipher persistence** | Encrypted-at-rest storage via rusqlite + bundled-sqlcipher + Argon2id key derivation. | **Implemented** |
|
| -- | **SQLCipher persistence** | Encrypted-at-rest storage via rusqlite + bundled-sqlcipher + Argon2id key derivation. | **Implemented** |
|
||||||
| -- | **OPAQUE auth** | Zero-knowledge password authentication via `opaque-ke`. Server never stores passwords. | **Implemented** |
|
| -- | **OPAQUE auth** | Zero-knowledge password authentication via `opaque-ke`. Server never stores passwords. | **Implemented** |
|
||||||
| -- | **iroh P2P** | NAT traversal and optional P2P mesh via the `quicproquo-p2p` crate (feature-flagged). | **Implemented** |
|
| -- | **iroh P2P** | NAT traversal and optional P2P mesh via the `quicprochat-p2p` crate (feature-flagged). | **Implemented** |
|
||||||
| -- | **Sealed Sender** | `--sealed-sender` flag encrypts sender identity inside MLS ciphertext. | **Implemented** |
|
| -- | **Sealed Sender** | `--sealed-sender` flag encrypts sender identity inside MLS ciphertext. | **Implemented** |
|
||||||
| 1 | **PIR (Private Information Retrieval)** | Fetch messages without revealing the recipient's identity to the server. | Future |
|
| 1 | **PIR (Private Information Retrieval)** | Fetch messages without revealing the recipient's identity to the server. | Future |
|
||||||
| 2 | **Key Transparency** | Verifiable, append-only log of public key bindings. Detects key substitution attacks. | Future |
|
| 2 | **Key Transparency** | Verifiable, append-only log of public key bindings. Detects key substitution attacks. | Future |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Milestone Tracker
|
# Milestone Tracker
|
||||||
|
|
||||||
This page tracks the project milestones for quicproquo, from initial transport
|
This page tracks the project milestones for quicprochat, from initial transport
|
||||||
layer through post-quantum cryptography. Each milestone produces production-ready,
|
layer through post-quantum cryptography. Each milestone produces production-ready,
|
||||||
tested, deployable code -- see [Coding Standards](../contributing/coding-standards.md)
|
tested, deployable code -- see [Coding Standards](../contributing/coding-standards.md)
|
||||||
for what that means in practice.
|
for what that means in practice.
|
||||||
@@ -29,13 +29,13 @@ typed Cap'n Proto frames.
|
|||||||
**Deliverables:**
|
**Deliverables:**
|
||||||
|
|
||||||
- `schemas/envelope.capnp`: `Envelope` struct with `MsgType` enum (Ping/Pong at this stage)
|
- `schemas/envelope.capnp`: `Envelope` struct with `MsgType` enum (Ping/Pong at this stage)
|
||||||
- `quicproquo-proto`: `build.rs` invoking `capnpc`, generated type re-exports,
|
- `quicprochat-proto`: `build.rs` invoking `capnpc`, generated type re-exports,
|
||||||
canonical serialisation helpers
|
canonical serialisation helpers
|
||||||
- `quicproquo-core`: Ed25519 identity keypair generation,
|
- `quicprochat-core`: Ed25519 identity keypair generation,
|
||||||
Cap'n Proto frame codec (Tokio `Encoder`/`Decoder`)
|
Cap'n Proto frame codec (Tokio `Encoder`/`Decoder`)
|
||||||
- `quicproquo-server`: QUIC listener with TLS 1.3 (quinn/rustls), Ping to Pong
|
- `quicprochat-server`: QUIC listener with TLS 1.3 (quinn/rustls), Ping to Pong
|
||||||
handler, one tokio task per connection
|
handler, one tokio task per connection
|
||||||
- `quicproquo-client`: connects over QUIC, sends Ping, receives Pong, exits 0
|
- `quicprochat-client`: connects over QUIC, sends Ping, receives Pong, exits 0
|
||||||
- Integration test: server and client in same test binary using `tokio::spawn`
|
- Integration test: server and client in same test binary using `tokio::spawn`
|
||||||
- `docker-compose.yml` running the server
|
- `docker-compose.yml` running the server
|
||||||
|
|
||||||
@@ -54,10 +54,10 @@ via Cap'n Proto RPC.
|
|||||||
|
|
||||||
- `schemas/auth.capnp`: `AuthenticationService` interface (`uploadKeyPackage`,
|
- `schemas/auth.capnp`: `AuthenticationService` interface (`uploadKeyPackage`,
|
||||||
`fetchKeyPackage`)
|
`fetchKeyPackage`)
|
||||||
- `quicproquo-core`: Ed25519 identity keypair generation, MLS KeyPackage
|
- `quicprochat-core`: Ed25519 identity keypair generation, MLS KeyPackage
|
||||||
generation via `openmls`
|
generation via `openmls`
|
||||||
- `quicproquo-server`: AS RPC server with `DashMap` store, atomic consume-on-fetch
|
- `quicprochat-server`: AS RPC server with `DashMap` store, atomic consume-on-fetch
|
||||||
- `quicproquo-client`: `register-state` and `fetch-key` CLI subcommands
|
- `quicprochat-client`: `register-state` and `fetch-key` CLI subcommands
|
||||||
- Integration test: Alice uploads KeyPackage, Bob fetches it, fingerprints match
|
- Integration test: Alice uploads KeyPackage, Bob fetches it, fingerprints match
|
||||||
|
|
||||||
**Tests:** auth\_service.rs integration tests (upload, fetch, consume semantics).
|
**Tests:** auth\_service.rs integration tests (upload, fetch, consume semantics).
|
||||||
@@ -97,7 +97,7 @@ group\_id lifecycle, MLS integration.
|
|||||||
3. **openmls 0.5 API gotchas.** Several `openmls` methods changed signatures
|
3. **openmls 0.5 API gotchas.** Several `openmls` methods changed signatures
|
||||||
between 0.4 and 0.5 (e.g., `MlsGroup::new` vs `MlsGroup::new_with_group_id`,
|
between 0.4 and 0.5 (e.g., `MlsGroup::new` vs `MlsGroup::new_with_group_id`,
|
||||||
`BasicCredential::new` taking `Vec<u8>` directly). These differences are
|
`BasicCredential::new` taking `Vec<u8>` directly). These differences are
|
||||||
documented inline in `quicproquo-core/src/group.rs`.
|
documented inline in `quicprochat-core/src/group.rs`.
|
||||||
|
|
||||||
**Branch:** `feat/m1-noise-transport`
|
**Branch:** `feat/m1-noise-transport`
|
||||||
|
|
||||||
@@ -142,13 +142,13 @@ providing post-quantum confidentiality for all group key material.
|
|||||||
|
|
||||||
**Deliverables:**
|
**Deliverables:**
|
||||||
|
|
||||||
- Custom `OpenMlsCryptoProvider` with hybrid KEM in `quicproquo-core`
|
- Custom `OpenMlsCryptoProvider` with hybrid KEM in `quicprochat-core`
|
||||||
- Hybrid shared secret derivation:
|
- Hybrid shared secret derivation:
|
||||||
|
|
||||||
```
|
```
|
||||||
SharedSecret = HKDF-SHA256(
|
SharedSecret = HKDF-SHA256(
|
||||||
ikm = X25519_ss || ML-KEM-768_ss,
|
ikm = X25519_ss || ML-KEM-768_ss,
|
||||||
info = "quicproquo-hybrid-v1",
|
info = "quicprochat-hybrid-v1",
|
||||||
len = 32
|
len = 32
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ The following legacy behaviour has been removed; only current behaviour is suppo
|
|||||||
|
|
||||||
| Task | Status | Notes |
|
| Task | Status | Notes |
|
||||||
|------|--------|-------|
|
|------|--------|-------|
|
||||||
| **Ciphersuite allowlist** | **Done** | Server rejects KeyPackages whose ciphersuite is not `MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519`. See `quicproquo_core::validate_keypackage_ciphersuite` and `upload_key_package` (E021). |
|
| **Ciphersuite allowlist** | **Done** | Server rejects KeyPackages whose ciphersuite is not `MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519`. See `quicprochat_core::validate_keypackage_ciphersuite` and `upload_key_package` (E021). |
|
||||||
| **ALPN enforcement** | **Done** | Server TLS config sets `alpn_protocols = [b"capnp"]`; handshake completes only if client offers `capnp`. |
|
| **ALPN enforcement** | **Done** | Server TLS config sets `alpn_protocols = [b"capnp"]`; handshake completes only if client offers `capnp`. |
|
||||||
| **Connection draining** | **Done** | On `Ctrl+C`, server calls `endpoint.close(0, b"server shutdown")` and exits the accept loop. |
|
| **Connection draining** | **Done** | On `Ctrl+C`, server calls `endpoint.close(0, b"server shutdown")` and exits the accept loop. |
|
||||||
| **Wire versioning** | **Done** | `enqueue`, `fetch`, `fetchWait` require `version == CURRENT_WIRE_VERSION` (1). Other RPCs use auth version. |
|
| **Wire versioning** | **Done** | `enqueue`, `fetch`, `fetchWait` require `version == CURRENT_WIRE_VERSION` (1). Other RPCs use auth version. |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Production Readiness WBS
|
# Production Readiness WBS
|
||||||
|
|
||||||
This page defines the work breakdown structure (WBS) for taking quicproquo
|
This page defines the work breakdown structure (WBS) for taking quicprochat
|
||||||
from a proof-of-concept to a production-hardened system. It covers feature scope,
|
from a proof-of-concept to a production-hardened system. It covers feature scope,
|
||||||
security policy, phased delivery, and a planning checklist.
|
security policy, phased delivery, and a planning checklist.
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ document focuses on the cross-cutting concerns that span multiple milestones.
|
|||||||
|
|
||||||
## Feature Scope (Must-Have)
|
## Feature Scope (Must-Have)
|
||||||
|
|
||||||
These are the feature areas that must be addressed before quicproquo can be
|
These are the feature areas that must be addressed before quicprochat can be
|
||||||
considered production-ready. Each area maps to one or more milestones or phases
|
considered production-ready. Each area maps to one or more milestones or phases
|
||||||
in the WBS below.
|
in the WBS below.
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ in the WBS below.
|
|||||||
|
|
||||||
## Security Plan (By Design)
|
## Security Plan (By Design)
|
||||||
|
|
||||||
quicproquo follows a security-by-design philosophy. The standards below are
|
quicprochat follows a security-by-design philosophy. The standards below are
|
||||||
non-negotiable -- see [Coding Standards](../contributing/coding-standards.md) for
|
non-negotiable -- see [Coding Standards](../contributing/coding-standards.md) for
|
||||||
how they are enforced in code.
|
how they are enforced in code.
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ how they are enforced in code.
|
|||||||
### Transport Policy
|
### Transport Policy
|
||||||
|
|
||||||
- TLS 1.3 only (`rustls` configured with `TLS13` cipher suites exclusively).
|
- TLS 1.3 only (`rustls` configured with `TLS13` cipher suites exclusively).
|
||||||
- ALPN token `b"qpq"` required; reject connections with mismatched ALPN.
|
- ALPN token `b"qpc"` required; reject connections with mismatched ALPN.
|
||||||
- Self-signed certificates acceptable for development; production deployments
|
- Self-signed certificates acceptable for development; production deployments
|
||||||
must use a CA-signed certificate or certificate pinning.
|
must use a CA-signed certificate or certificate pinning.
|
||||||
- Connection draining on shutdown (QUIC `CONNECTION_CLOSE`).
|
- Connection draining on shutdown (QUIC `CONNECTION_CLOSE`).
|
||||||
@@ -128,7 +128,7 @@ how they are enforced in code.
|
|||||||
| Wire versioning | Version field in all Protobuf frames; reject unknown versions |
|
| Wire versioning | Version field in all Protobuf frames; reject unknown versions |
|
||||||
| Ciphersuite allowlist | Server rejects KeyPackages outside the allowed set |
|
| Ciphersuite allowlist | Server rejects KeyPackages outside the allowed set |
|
||||||
| Downgrade guards | Prevent epoch rollback; reject Commits with weaker ciphersuites |
|
| Downgrade guards | Prevent epoch rollback; reject Commits with weaker ciphersuites |
|
||||||
| ALPN enforcement | Reject connections without `b"qpq"` ALPN token |
|
| ALPN enforcement | Reject connections without `b"qpc"` ALPN token |
|
||||||
| Connection draining | Graceful QUIC `CONNECTION_CLOSE` on server shutdown |
|
| Connection draining | Graceful QUIC `CONNECTION_CLOSE` on server shutdown |
|
||||||
| KeyPackage rotation | Client-side timer to upload fresh KeyPackages before TTL expiry |
|
| KeyPackage rotation | Client-side timer to upload fresh KeyPackages before TTL expiry |
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
# Client SDKs
|
# Client SDKs
|
||||||
|
|
||||||
This guide covers how to build clients for the quicproquo E2E encrypted messenger
|
This guide covers how to build clients for the quicprochat E2E encrypted messenger
|
||||||
using the official SDKs or by implementing a new one.
|
using the official SDKs or by implementing a new one.
|
||||||
|
|
||||||
## Official SDKs
|
## Official SDKs
|
||||||
|
|
||||||
| Language | Location | Transport | Status |
|
| Language | Location | Transport | Status |
|
||||||
|----------|----------|-----------|--------|
|
|----------|----------|-----------|--------|
|
||||||
| **Rust** | `crates/quicproquo-client` | QUIC + Protobuf (v2) | Production |
|
| **Rust** | `crates/quicprochat-client` | QUIC + Protobuf (v2) | Production |
|
||||||
| **Go** | `sdks/go/` | QUIC + Protobuf (v2) | Production |
|
| **Go** | `sdks/go/` | QUIC + Protobuf (v2) | Production |
|
||||||
| **TypeScript** | `sdks/typescript/` | WebSocket bridge + WASM crypto | Production |
|
| **TypeScript** | `sdks/typescript/` | WebSocket bridge + WASM crypto | Production |
|
||||||
| **Python** | `sdks/python/` | QUIC + Protobuf (v2) / Rust FFI | Production |
|
| **Python** | `sdks/python/` | QUIC + Protobuf (v2) / Rust FFI | Production |
|
||||||
| **C** | `crates/quicproquo-ffi/` | Rust FFI (synchronous) | Production |
|
| **C** | `crates/quicprochat-ffi/` | Rust FFI (synchronous) | Production |
|
||||||
| **Swift** | `sdks/swift/` | C FFI wrapper | In progress |
|
| **Swift** | `sdks/swift/` | C FFI wrapper | In progress |
|
||||||
| **Kotlin** | `sdks/kotlin/` | JNI + C FFI | In progress |
|
| **Kotlin** | `sdks/kotlin/` | JNI + C FFI | In progress |
|
||||||
| **Java** | `sdks/java/` | JNI + C FFI | In progress |
|
| **Java** | `sdks/java/` | JNI + C FFI | In progress |
|
||||||
@@ -49,15 +49,15 @@ use the same 10-byte framing header followed by a protobuf payload.
|
|||||||
|
|
||||||
## Canonical Schemas
|
## Canonical Schemas
|
||||||
|
|
||||||
- **Protobuf** (v2): `proto/qpq/v1/*.proto` -- 14 service definitions
|
- **Protobuf** (v2): `proto/qpc/v1/*.proto` -- 14 service definitions
|
||||||
|
|
||||||
The protobuf schemas in `proto/qpq/v1/` are the canonical API contract for
|
The protobuf schemas in `proto/qpc/v1/` are the canonical API contract for
|
||||||
the v2 protocol. New SDKs should implement against these definitions.
|
the v2 protocol. New SDKs should implement against these definitions.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [Wire Format Reference](wire-format.md) -- v2 QUIC + Protobuf framing and method IDs
|
- [Wire Format Reference](wire-format.md) -- v2 QUIC + Protobuf framing and method IDs
|
||||||
- [Rust SDK](rust.md) -- native Rust client using `quicproquo-sdk`
|
- [Rust SDK](rust.md) -- native Rust client using `quicprochat-sdk`
|
||||||
- [Go SDK](../getting-started/go-sdk.md) -- Go client with QUIC transport
|
- [Go SDK](../getting-started/go-sdk.md) -- Go client with QUIC transport
|
||||||
- [TypeScript SDK](../getting-started/typescript-sdk.md) -- browser and Node.js client
|
- [TypeScript SDK](../getting-started/typescript-sdk.md) -- browser and Node.js client
|
||||||
- [C FFI Bindings](../getting-started/ffi.md) -- C bindings for language integrations
|
- [C FFI Bindings](../getting-started/ffi.md) -- C bindings for language integrations
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Rust SDK
|
# Rust SDK
|
||||||
|
|
||||||
The Rust client is the reference implementation, located in
|
The Rust client is the reference implementation, located in
|
||||||
`crates/quicproquo-client/`. It is built on top of the `quicproquo-sdk` crate,
|
`crates/quicprochat-client/`. It is built on top of the `quicprochat-sdk` crate,
|
||||||
which provides the high-level v2 API over QUIC + Protobuf.
|
which provides the high-level v2 API over QUIC + Protobuf.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@@ -10,13 +10,13 @@ Add to your `Cargo.toml`:
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quicproquo-sdk = { path = "crates/quicproquo-sdk" }
|
quicprochat-sdk = { path = "crates/quicprochat-sdk" }
|
||||||
```
|
```
|
||||||
|
|
||||||
## Connection
|
## Connection
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use quicproquo_sdk::QpqClient;
|
use quicprochat_sdk::QpqClient;
|
||||||
|
|
||||||
let client = QpqClient::connect("127.0.0.1:5001", &tls_config).await?;
|
let client = QpqClient::connect("127.0.0.1:5001", &tls_config).await?;
|
||||||
let health = client.health().await?;
|
let health = client.health().await?;
|
||||||
@@ -24,10 +24,10 @@ let health = client.health().await?;
|
|||||||
|
|
||||||
## CLI Client Usage
|
## CLI Client Usage
|
||||||
|
|
||||||
The `quicproquo-client` binary provides a CLI/TUI interface:
|
The `quicprochat-client` binary provides a CLI/TUI interface:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use quicproquo_client::{cmd_health, cmd_login, cmd_send};
|
use quicprochat_client::{cmd_health, cmd_login, cmd_send};
|
||||||
|
|
||||||
// Health check
|
// Health check
|
||||||
cmd_health("127.0.0.1:5001", &ca_cert_path, "localhost").await?;
|
cmd_health("127.0.0.1:5001", &ca_cert_path, "localhost").await?;
|
||||||
@@ -49,17 +49,17 @@ cmd_login(
|
|||||||
- OPAQUE authentication with zeroizing credential storage
|
- OPAQUE authentication with zeroizing credential storage
|
||||||
- SQLCipher local state with Argon2id key derivation
|
- SQLCipher local state with Argon2id key derivation
|
||||||
- Sealed sender metadata protection (`--sealed-sender` flag)
|
- Sealed sender metadata protection (`--sealed-sender` flag)
|
||||||
- v2 QUIC + Protobuf transport via the `quicproquo-sdk` crate
|
- v2 QUIC + Protobuf transport via the `quicprochat-sdk` crate
|
||||||
|
|
||||||
## Crate Structure
|
## Crate Structure
|
||||||
|
|
||||||
| Crate | Purpose |
|
| Crate | Purpose |
|
||||||
|-------|---------|
|
|-------|---------|
|
||||||
| `quicproquo-core` | Crypto primitives, MLS, hybrid KEM |
|
| `quicprochat-core` | Crypto primitives, MLS, hybrid KEM |
|
||||||
| `quicproquo-proto` | Protobuf generated types |
|
| `quicprochat-proto` | Protobuf generated types |
|
||||||
| `quicproquo-rpc` | QUIC RPC framework (framing, dispatch) |
|
| `quicprochat-rpc` | QUIC RPC framework (framing, dispatch) |
|
||||||
| `quicproquo-sdk` | High-level client SDK (`QpqClient`) |
|
| `quicprochat-sdk` | High-level client SDK (`QpqClient`) |
|
||||||
| `quicproquo-client` | CLI/TUI client application |
|
| `quicprochat-client` | CLI/TUI client application |
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# Wire Format Reference
|
# Wire Format Reference
|
||||||
|
|
||||||
The quicproquo v2 protocol uses QUIC (RFC 9000) with TLS 1.3 as the transport
|
The quicprochat v2 protocol uses QUIC (RFC 9000) with TLS 1.3 as the transport
|
||||||
layer and Protocol Buffers for message serialization.
|
layer and Protocol Buffers for message serialization.
|
||||||
|
|
||||||
## Connection
|
## Connection
|
||||||
|
|
||||||
- **Protocol**: QUIC with TLS 1.3
|
- **Protocol**: QUIC with TLS 1.3
|
||||||
- **ALPN**: `qpq`
|
- **ALPN**: `qpc`
|
||||||
- **Port**: 5001 (default)
|
- **Port**: 5001 (default)
|
||||||
- **Certificate**: Server presents a TLS certificate; clients verify against a CA cert
|
- **Certificate**: Server presents a TLS certificate; clients verify against a CA cert
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ This allows concurrent RPCs without head-of-line blocking.
|
|||||||
|
|
||||||
## Protobuf Definitions
|
## Protobuf Definitions
|
||||||
|
|
||||||
All message types are defined in `proto/qpq/v1/*.proto`:
|
All message types are defined in `proto/qpc/v1/*.proto`:
|
||||||
|
|
||||||
| File | Services |
|
| File | Services |
|
||||||
|------|----------|
|
|------|----------|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Auth Schema
|
# Auth Schema
|
||||||
|
|
||||||
**Proto file:** `proto/qpq/v1/auth.proto`
|
**Proto file:** `proto/qpc/v1/auth.proto`
|
||||||
**Package:** `qpq.v1`
|
**Package:** `qpc.v1`
|
||||||
**Method IDs:** 100-103
|
**Method IDs:** 100-103
|
||||||
|
|
||||||
The auth proto defines the OPAQUE asymmetric password-authenticated key exchange (PAKE) messages used for user registration and login. OPAQUE never transmits the password to the server; the server learns only a random value derived from the password.
|
The auth proto defines the OPAQUE asymmetric password-authenticated key exchange (PAKE) messages used for user registration and login. OPAQUE never transmits the password to the server; the server learns only a random value derived from the password.
|
||||||
@@ -16,7 +16,7 @@ See [Authentication Service Internals](../internals/authentication-service.md) f
|
|||||||
|
|
||||||
```protobuf
|
```protobuf
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
package qpq.v1;
|
package qpc.v1;
|
||||||
|
|
||||||
// OPAQUE registration + login (4 methods).
|
// OPAQUE registration + login (4 methods).
|
||||||
// Method IDs: 100-103.
|
// Method IDs: 100-103.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user