docs: update getting-started and contributing docs for v2

Remove the capnp compiler requirement from prerequisites (protobuf-src
vendors protoc automatically). Update building.md for 9 crates and the
justfile commands. Rewrite running-the-server.md with accurate v2 flags
(--allow-insecure-auth, --sealed-sender, --plugin-dir, --ws-listen,
--webtransport-listen, --federation-enabled, QPQ_PRODUCTION). Update
docker.md to remove capnproto install from builder stage description.
Delete bot-sdk.md and generators.md (removed crates). Update testing.md
with the accurate 301-test breakdown across 9 crates and the AUTH_LOCK
note for E2E tests. Update coding-standards.md dependency table to list
prost as primary serialisation, capnp as legacy-only, and add opaque-ke.
This commit is contained in:
2026-03-04 22:00:23 +01:00
parent 189534c511
commit f7a7f672b4
8 changed files with 405 additions and 675 deletions

View File

@@ -37,26 +37,23 @@ services:
context: .
dockerfile: docker/Dockerfile
ports:
- "7000:7000"
- "7000:7000/udp"
environment:
RUST_LOG: "info"
QPQ_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
QPQ_DATA_DIR: "/var/lib/quicproquo"
QPQ_PRODUCTION: "true"
volumes:
- server-data:/var/lib/quicproquo
restart: unless-stopped
volumes:
server-data:
```
### 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.
The container exposes port `7000` (QUIC/UDP). Note that QUIC uses UDP, so ensure your firewall allows UDP traffic on this port.
### Restart policy
@@ -73,18 +70,34 @@ The Dockerfile at `docker/Dockerfile` uses a two-stage build to produce a minima
```dockerfile
FROM rust:bookworm AS builder
RUN apt-get update \
&& apt-get install -y --no-install-recommends capnproto \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
# Copy manifests first so dependency layers are cached independently of source.
COPY Cargo.toml Cargo.lock ./
COPY crates/quicproquo-core/Cargo.toml crates/quicproquo-core/Cargo.toml
# ... (all 9 crate manifests)
# Create dummy source files for dependency caching.
RUN mkdir -p ... && echo 'fn main() {}' > ...
# Schemas must exist before the proto crate's build.rs runs.
COPY schemas/ schemas/
# Build dependencies only (cache layer).
RUN cargo build --release --bin qpq-server 2>/dev/null || true
# Copy real source and build for real.
COPY crates/ crates/
RUN cargo build --release --bin qpq-server
```
Key steps:
1. **Base image**: `rust:bookworm` (Debian Bookworm with the Rust toolchain pre-installed).
2. **Install `capnproto`**: Required by `quicproquo-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 `quicproquo-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.
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.
4. **Copy schemas**: The `schemas/` directory is copied before the dependency build because `quicproquo-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.
### Stage 2: Runtime (`debian:bookworm-slim`)
@@ -97,39 +110,50 @@ RUN apt-get update \
COPY --from=builder /build/target/release/qpq-server /usr/local/bin/qpq-server
RUN groupadd --system qpq \
&& useradd --system --gid qpq --no-create-home --shell /usr/sbin/nologin qpq \
&& mkdir -p /var/lib/quicproquo \
&& chown qpq:qpq /var/lib/quicproquo
EXPOSE 7000
ENV RUST_LOG=info \
QPQ_LISTEN=0.0.0.0:7000
VOLUME ["/var/lib/quicproquo"]
USER nobody
ENV RUST_LOG=info \
QPQ_LISTEN=0.0.0.0:7000 \
QPQ_DATA_DIR=/var/lib/quicproquo \
QPQ_TLS_CERT=/var/lib/quicproquo/server-cert.der \
QPQ_TLS_KEY=/var/lib/quicproquo/server-key.der \
QPQ_PRODUCTION=true
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD test -f /var/lib/quicproquo/server-cert.der || exit 1
USER qpq
CMD ["qpq-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 `QPQ_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 `QPQ_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.
- **Minimal image**: No Rust toolchain, no build tools, no `protoc` binary.
- **`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.
- **Named volume**: `/var/lib/quicproquo` 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.
---
## 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:
The server stores its state (TLS certificates, KeyPackages, delivery queues, OPAQUE setup, KT log) in the data directory. Mount a volume to persist this data across container restarts:
```yaml
services:
server:
# ... existing config ...
volumes:
- server-data:/data
environment:
QPQ_DATA_DIR: "/data"
- server-data:/var/lib/quicproquo
volumes:
server-data:
@@ -138,10 +162,16 @@ volumes:
Or use a bind mount for easier inspection:
```bash
docker compose run \
-v $(pwd)/server-data:/data \
-e QPQ_DATA_DIR=/data \
server
mkdir -p ./server-data
docker run -d \
--name quicproquo \
-p 7000:7000/udp \
-v "$(pwd)/server-data:/var/lib/quicproquo" \
-e QPQ_ALLOW_INSECURE_AUTH=true \
-e QPQ_PRODUCTION=false \
-e RUST_LOG=info \
qpq-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.
@@ -156,19 +186,18 @@ To build the Docker image without starting a container:
docker build -t qpq-server -f docker/Dockerfile .
```
To run it manually:
To run it in development mode (without production validation):
```bash
docker run -d \
--name quicproquo \
-p 7000:7000/udp \
-e QPQ_LISTEN=0.0.0.0:7000 \
-e QPQ_ALLOW_INSECURE_AUTH=true \
-e QPQ_PRODUCTION=false \
-e RUST_LOG=info \
qpq-server
```
Note the `/udp` suffix on the port mapping -- QUIC runs over UDP.
---
## Connecting the client to a containerised server
@@ -176,8 +205,8 @@ Note the `/udp` suffix on the port mapping -- QUIC runs over UDP.
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
# Extract the server's TLS cert from the container volume
docker compose cp server:/var/lib/quicproquo/server-cert.der ./data/server-cert.der
# Connect
cargo run -p quicproquo-client -- ping \
@@ -185,7 +214,7 @@ cargo run -p quicproquo-client -- ping \
--server-name localhost
```
If you mounted a volume (e.g., `./server-data:/data`), the certificate is directly accessible at `./server-data/server-cert.der`.
If you mounted a bind volume (e.g., `./server-data:/var/lib/quicproquo`), the certificate is directly accessible at `./server-data/server-cert.der`.
---