feat: add post-quantum hybrid KEM + SQLCipher persistence
Feature 1 — Post-Quantum Hybrid KEM (X25519 + ML-KEM-768): - Create hybrid_kem.rs with keygen, encrypt, decrypt + 11 unit tests - Wire format: version(1) | x25519_eph_pk(32) | mlkem_ct(1088) | nonce(12) | ct - Add uploadHybridKey/fetchHybridKey RPCs to node.capnp schema - Server: hybrid key storage in FileBackedStore + RPC handlers - Client: hybrid keypair in StoredState, auto-wrap/unwrap in send/recv/invite/join - demo-group runs full hybrid PQ envelope round-trip Feature 2 — SQLCipher Persistence: - Extract Store trait from FileBackedStore API - Create SqlStore (rusqlite + bundled-sqlcipher) with encrypted-at-rest SQLite - Schema: key_packages, deliveries, hybrid_keys tables with indexes - Server CLI: --store-backend=sql, --db-path, --db-key flags - 5 unit tests for SqlStore (FIFO, round-trip, upsert, channel isolation) Also includes: client lib.rs refactor, auth config, TOML config file support, mdBook documentation, and various cleanups by user. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
196
docs/src/getting-started/docker.md
Normal file
196
docs/src/getting-started/docker.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# Docker Deployment
|
||||
|
||||
quicnprotochat includes a multi-stage Dockerfile and a Docker Compose configuration for building and running the server in containers.
|
||||
|
||||
---
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
docker compose up
|
||||
```
|
||||
|
||||
This builds the server image (if not already built) and starts a single `server` service listening on port `7000`. The server will generate a self-signed TLS certificate on first launch and begin accepting QUIC connections.
|
||||
|
||||
To rebuild after code changes:
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
To run in the background:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Docker Compose configuration
|
||||
|
||||
The `docker-compose.yml` at the repository root defines a single service:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
server:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
ports:
|
||||
- "7000:7000"
|
||||
environment:
|
||||
RUST_LOG: "info"
|
||||
QUICNPROTOCHAT_LISTEN: "0.0.0.0:7000"
|
||||
healthcheck:
|
||||
test: ["CMD", "bash", "-c", "echo '' > /dev/tcp/localhost/7000"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
start_period: 10s
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Port mapping
|
||||
|
||||
The container exposes port `7000` (QUIC/UDP). The `ports` directive maps host port `7000` to the container's `7000`. Note that QUIC uses UDP, so ensure your firewall allows UDP traffic on this port.
|
||||
|
||||
### Health check
|
||||
|
||||
The health check uses a TCP connection probe (`/dev/tcp/localhost/7000`). While QUIC is a UDP protocol, the TCP probe verifies that the process is running and the port is bound. A QUIC-aware health check (e.g., using the client's `ping` command) would be more precise but requires the client binary in the runtime image.
|
||||
|
||||
### Restart policy
|
||||
|
||||
`restart: unless-stopped` ensures the server restarts automatically after crashes but stays stopped if you explicitly `docker compose stop` or `docker compose down`.
|
||||
|
||||
---
|
||||
|
||||
## Multi-stage Docker build
|
||||
|
||||
The Dockerfile at `docker/Dockerfile` uses a two-stage build to produce a minimal runtime image.
|
||||
|
||||
### Stage 1: Builder (`rust:bookworm`)
|
||||
|
||||
```dockerfile
|
||||
FROM rust:bookworm AS builder
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends capnproto \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
Key steps:
|
||||
|
||||
1. **Base image**: `rust:bookworm` (Debian Bookworm with the Rust toolchain pre-installed).
|
||||
2. **Install `capnproto`**: Required by `quicnprotochat-proto/build.rs` to compile `.capnp` schemas at build time.
|
||||
3. **Copy manifests first**: `Cargo.toml` and `Cargo.lock` are copied before source code. Dummy `main.rs` / `lib.rs` stubs are created so that `cargo build` can resolve and cache the dependency graph. This ensures that dependency compilation is cached in a separate Docker layer -- subsequent builds that only change source code skip the dependency compilation step entirely.
|
||||
4. **Copy schemas**: The `schemas/` directory is copied before the dependency build because `quicnprotochat-proto/build.rs` requires the `.capnp` files during compilation.
|
||||
5. **Copy real source and build**: After the dependency cache layer, real source files are copied in and `cargo build --release` is run.
|
||||
|
||||
### Stage 2: Runtime (`debian:bookworm-slim`)
|
||||
|
||||
```dockerfile
|
||||
FROM debian:bookworm-slim AS runtime
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /build/target/release/quicnprotochat-server /usr/local/bin/quicnprotochat-server
|
||||
|
||||
EXPOSE 7000
|
||||
|
||||
ENV RUST_LOG=info \
|
||||
QUICNPROTOCHAT_LISTEN=0.0.0.0:7000
|
||||
|
||||
USER nobody
|
||||
|
||||
CMD ["quicnprotochat-server"]
|
||||
```
|
||||
|
||||
Key characteristics:
|
||||
|
||||
- **Minimal image**: No Rust toolchain, no `capnp` compiler, no build artifacts.
|
||||
- **`ca-certificates`**: Included for future HTTPS calls (e.g., ACME certificate provisioning or key sync endpoints).
|
||||
- **Non-root execution**: The container runs as `nobody` for defense in depth.
|
||||
- **Default port**: The Dockerfile defaults to port `7000` via `QUICNPROTOCHAT_LISTEN`, but the `docker-compose.yml` overrides this to `7000` for consistency with the development workflow.
|
||||
|
||||
> **Note**: The `EXPOSE 7000` directive in the Dockerfile and the `QUICNPROTOCHAT_LISTEN=0.0.0.0:7000` override in `docker-compose.yml` mean the effective listen port is `7000` when using Compose. If you run the Docker image directly without Compose, the server will listen on `7000` by default.
|
||||
|
||||
---
|
||||
|
||||
## Volume persistence
|
||||
|
||||
The server stores its state (TLS certificates, KeyPackages, delivery queues, hybrid keys) in the data directory (default `data/`). To persist this data across container restarts, mount a volume:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
server:
|
||||
# ... existing config ...
|
||||
volumes:
|
||||
- server-data:/data
|
||||
environment:
|
||||
QUICNPROTOCHAT_DATA_DIR: "/data"
|
||||
|
||||
volumes:
|
||||
server-data:
|
||||
```
|
||||
|
||||
Or use a bind mount for easier inspection:
|
||||
|
||||
```bash
|
||||
docker compose run \
|
||||
-v $(pwd)/server-data:/data \
|
||||
-e QUICNPROTOCHAT_DATA_DIR=/data \
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## Building just the image
|
||||
|
||||
To build the Docker image without starting a container:
|
||||
|
||||
```bash
|
||||
docker build -t quicnprotochat-server -f docker/Dockerfile .
|
||||
```
|
||||
|
||||
To run it manually:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name quicnprotochat \
|
||||
-p 7000:7000/udp \
|
||||
-e QUICNPROTOCHAT_LISTEN=0.0.0.0:7000 \
|
||||
-e RUST_LOG=info \
|
||||
quicnprotochat-server
|
||||
```
|
||||
|
||||
Note the `/udp` suffix on the port mapping -- QUIC runs over UDP.
|
||||
|
||||
---
|
||||
|
||||
## Connecting the client to a containerised server
|
||||
|
||||
When the server runs in Docker with `docker compose up`, the client can connect from the host:
|
||||
|
||||
```bash
|
||||
# Extract the server's TLS cert from the container
|
||||
docker compose cp server:/data/server-cert.der ./data/server-cert.der
|
||||
|
||||
# Connect
|
||||
cargo run -p quicnprotochat-client -- ping \
|
||||
--ca-cert ./data/server-cert.der \
|
||||
--server-name localhost
|
||||
```
|
||||
|
||||
If you mounted a volume (e.g., `./server-data:/data`), the certificate is directly accessible at `./server-data/server-cert.der`.
|
||||
|
||||
---
|
||||
|
||||
## Next steps
|
||||
|
||||
- [Running the Server](running-the-server.md) -- server configuration without Docker
|
||||
- [Running the Client](running-the-client.md) -- CLI subcommands
|
||||
- [Demo Walkthrough](demo-walkthrough.md) -- step-by-step messaging scenario
|
||||
Reference in New Issue
Block a user