Files
quicproquo/docs/src/getting-started/running-the-server.md
Chris Nennemann 853ca4fec0 chore: rename project quicnprotochat -> quicproquo (binaries: qpq)
Rename the entire workspace:
- Crate packages: quicnprotochat-{core,proto,server,client,gui,p2p,mobile} -> quicproquo-*
- Binary names: quicnprotochat -> qpq, quicnprotochat-server -> qpq-server,
  quicnprotochat-gui -> qpq-gui
- Default files: *-state.bin -> qpq-state.bin, *-server.toml -> qpq-server.toml,
  *.db -> qpq.db
- Environment variable prefix: QUICNPROTOCHAT_* -> QPQ_*
- App identifier: chat.quicnproto.gui -> chat.quicproquo.gui
- Proto package: quicnprotochat.bench -> quicproquo.bench
- All documentation, Docker, CI, and script references updated

HKDF domain-separation strings and P2P ALPN remain unchanged for
backward compatibility with existing encrypted state and wire protocol.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 20:11:51 +01:00

6.3 KiB

Running the Server

The quicproquo server is a single binary (qpq-server) that exposes a unified NodeService endpoint combining Authentication Service (KeyPackage management) and Delivery Service (message relay) operations over a single QUIC + TLS 1.3 connection.


Quick start

cargo run -p quicproquo-server

On first launch the server will:

  1. Create the data/ directory if it does not exist.
  2. Generate a self-signed TLS certificate and private key (data/server-cert.der, data/server-key.der) with SANs localhost, 127.0.0.1, and ::1.
  3. Open a QUIC endpoint on 0.0.0.0:7000.
  4. Begin accepting connections.

You should see output similar to:

2025-01-01T00:00:00.000000Z  INFO quicproquo_server: generated self-signed TLS certificate cert="data/server-cert.der" key="data/server-key.der"
2025-01-01T00:00:00.000000Z  INFO quicproquo_server: accepting QUIC connections addr="0.0.0.0:7000"

Configuration

All configuration is available via CLI flags and environment variables. Environment variables take precedence when both are specified.

Purpose CLI flag Env var Default
QUIC listen address --listen QPQ_LISTEN 0.0.0.0:7000
TLS certificate (DER) --tls-cert QPQ_TLS_CERT data/server-cert.der
TLS private key (DER) --tls-key QPQ_TLS_KEY data/server-key.der
Data directory --data-dir QPQ_DATA_DIR data
Log level -- RUST_LOG info

Examples

# Listen on a custom port
cargo run -p quicproquo-server -- --listen 0.0.0.0:9000

# Use pre-existing TLS credentials
cargo run -p quicproquo-server -- \
  --tls-cert /etc/quicproquo/cert.der \
  --tls-key  /etc/quicproquo/key.der

# Via environment variables
QPQ_LISTEN=0.0.0.0:9000 \
RUST_LOG=debug \
cargo run -p quicproquo-server

Production deployment

Set QPQ_PRODUCTION=1 (or true / yes) so the server enforces production checks:

  • Auth: A non-empty QPQ_AUTH_TOKEN is required; the value devtoken is rejected.
  • TLS: Existing cert and key files are required (auto-generation is disabled).
  • SQL store: When --store-backend=sql, a non-empty QPQ_DB_KEY is required. An empty key leaves the database unencrypted on disk and is not acceptable for production.

TLS certificate handling

Self-signed certificate auto-generation

If the files at --tls-cert and --tls-key do not exist when the server starts, it generates a self-signed certificate using the rcgen crate. The generated certificate includes three Subject Alternative Names:

  • localhost
  • 127.0.0.1
  • ::1

The certificate and key are written in DER format. Parent directories are created automatically.

Using your own certificate

To use a certificate issued by a CA or a custom self-signed certificate:

  1. Convert your certificate and key to DER format if they are in PEM:
    openssl x509 -in cert.pem -outform DER -out cert.der
    openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -out key.der -nocrypt
    
  2. Point the server at them:
    cargo run -p quicproquo-server -- \
      --tls-cert cert.der \
      --tls-key  key.der
    
  3. Distribute the certificate (or its CA root) to clients so they can verify the server. The client's --ca-cert flag accepts a DER file.

TLS configuration details

The server's TLS stack is configured as follows:

  • Protocol versions: TLS 1.3 only (rustls::version::TLS13). TLS 1.2 and below are rejected.
  • Client authentication: Disabled (with_no_client_auth()). The server does not request a client certificate. Client identity is established at the MLS layer via Ed25519 credentials, not at the TLS layer.
  • ALPN: The server advertises b"capnp" as the application-layer protocol.

ALPN negotiation

Both the server and client must agree on the ALPN token b"capnp" during the TLS handshake. This token is hardcoded in the server's TLS configuration:

tls.alpn_protocols = vec![b"capnp".to_vec()];

If a client connects with a different (or no) ALPN token, the QUIC handshake will fail with an ALPN mismatch error.


Storage

The server persists its state to the data directory (--data-dir, default data/):

File Contents
data/server-cert.der TLS certificate (DER)
data/server-key.der TLS private key (DER)
data/keypackages.bin bincode-serialised map of identity keys to KeyPackage queues
data/deliveries.bin bincode-serialised map of (channelId, recipientKey) to message queues
data/hybridkeys.bin bincode-serialised map of identity keys to hybrid (X25519 + ML-KEM-768) public keys

Storage is implemented by the FileBackedStore in crates/quicproquo-server/src/storage.rs. Every mutation (upload, enqueue, fetch) flushes the entire map to disk synchronously. This is suitable for proof-of-concept workloads but not production traffic. See Storage Backend for details.


Connection handling

Each incoming QUIC connection is handled in a tokio::task::spawn_local task on a shared LocalSet. The capnp-rpc library uses Rc<RefCell<>> internally, making it !Send, which is why all RPC tasks must run on a LocalSet rather than being spawned with tokio::spawn.

The connection lifecycle:

  1. Accept incoming QUIC connection.
  2. Complete TLS 1.3 handshake.
  3. Accept a bidirectional QUIC stream.
  4. Wrap the stream in a capnp_rpc::twoparty::VatNetwork.
  5. Bootstrap a NodeService RPC endpoint.
  6. Serve requests until the client disconnects or an error occurs.

Logging

The server uses tracing with tracing-subscriber and respects the RUST_LOG environment variable:

# Default: info level
RUST_LOG=info cargo run -p quicproquo-server

# Debug level for detailed RPC tracing
RUST_LOG=debug cargo run -p quicproquo-server

# Trace level for maximum verbosity
RUST_LOG=trace cargo run -p quicproquo-server

# Filter to specific crates
RUST_LOG=quicproquo_server=debug,quinn=warn cargo run -p quicproquo-server

Next steps