# Running the Server

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

```bash
cargo run -p quicprochat-server -- --allow-insecure-auth
```

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. Generate and persist an OPAQUE `ServerSetup` for authentication.
4. Open a QUIC endpoint on `0.0.0.0:7000`.
5. Begin accepting connections.

You should see output similar to:

```
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 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.

---

## Configuration

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

| Purpose | CLI flag | Env var | Default |
|---|---|---|---|
| QUIC listen address | `--listen` | `QPC_LISTEN` | `0.0.0.0:7000` |
| TLS certificate (DER) | `--tls-cert` | `QPC_TLS_CERT` | `data/server-cert.der` |
| TLS private key (DER) | `--tls-key` | `QPC_TLS_KEY` | `data/server-key.der` |
| Data directory | `--data-dir` | `QPC_DATA_DIR` | `data` |
| TOML config file | `--config` | `QPC_CONFIG` | `qpc-server.toml` |
| Log level | -- | `RUST_LOG` | `info` |

### Authentication flags

| Purpose | CLI flag | Env var | Default |
|---|---|---|---|
| Static bearer token | `--auth-token` | `QPC_AUTH_TOKEN` | (none) |
| Skip token requirement (dev only) | `--allow-insecure-auth` | `QPC_ALLOW_INSECURE_AUTH` | `false` |
| Sealed sender mode | `--sealed-sender` | `QPC_SEALED_SENDER` | `false` |

### Storage flags

| Purpose | CLI flag | Env var | Default |
|---|---|---|---|
| Storage backend | `--store-backend` | `QPC_STORE_BACKEND` | `file` |
| SQLCipher DB path | `--db-path` | `QPC_DB_PATH` | `data/qpc.db` |
| SQLCipher encryption key | `--db-key` | `QPC_DB_KEY` | (empty = plaintext) |

### Transport and timeout flags

| Purpose | CLI flag | Env var | Default |
|---|---|---|---|
| Drain timeout (graceful shutdown) | `--drain-timeout` | `QPC_DRAIN_TIMEOUT` | `30` s |
| Per-RPC timeout | `--rpc-timeout` | `QPC_RPC_TIMEOUT` | `30` s |
| Storage operation timeout | `--storage-timeout` | `QPC_STORAGE_TIMEOUT` | `10` s |

### Extension flags

| Purpose | CLI flag | Env var | Default |
|---|---|---|---|
| Plugin directory | `--plugin-dir` | `QPC_PLUGIN_DIR` | (none) |
| WebSocket bridge address | `--ws-listen` | `QPC_WS_LISTEN` | (none) |
| WebTransport address | `--webtransport-listen` | `QPC_WEBTRANSPORT_LISTEN` | (none) |
| Federation | `--federation-enabled` | `QPC_FEDERATION_ENABLED` | `false` |
| Federation domain | `--federation-domain` | `QPC_FEDERATION_DOMAIN` | (none) |
| Federation listen address | `--federation-listen` | `QPC_FEDERATION_LISTEN` | `0.0.0.0:7001` |
| Redact audit logs | `--redact-logs` | `QPC_REDACT_LOGS` | `false` |
| Metrics listen address | `--metrics-listen` | `QPC_METRICS_LISTEN` | (none) |

### Examples

```bash
# Development: no auth token required
cargo run -p quicprochat-server -- --allow-insecure-auth

# Listen on a custom port
cargo run -p quicprochat-server -- --allow-insecure-auth --listen 0.0.0.0:5001

# Use SQLCipher storage backend
cargo run -p quicprochat-server -- \
  --allow-insecure-auth \
  --store-backend sql \
  --db-path data/qpc.db \
  --db-key mysecretkey

# Load server plugins from a directory
cargo run -p quicprochat-server -- \
  --allow-insecure-auth \
  --plugin-dir /path/to/plugins

# Enable WebSocket bridge for browser clients
cargo run -p quicprochat-server -- \
  --allow-insecure-auth \
  --ws-listen 0.0.0.0:9000

# Via environment variables
QPC_LISTEN=0.0.0.0:5001 \
QPC_ALLOW_INSECURE_AUTH=true \
RUST_LOG=debug \
cargo run -p quicprochat-server
```

---

## Production deployment

Set `QPC_PRODUCTION=true` to enable production validation. The server enforces:

- `--allow-insecure-auth` is **prohibited**.
- `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).
- When `--store-backend=sql`, `QPC_DB_KEY` must be non-empty.

```bash
QPC_PRODUCTION=true \
QPC_AUTH_TOKEN=<strong-token> \
QPC_TLS_CERT=/etc/quicprochat/cert.der \
QPC_TLS_KEY=/etc/quicprochat/key.der \
QPC_STORE_BACKEND=sql \
QPC_DB_KEY=<strong-db-key> \
qpc-server
```

---

## TLS certificate handling

### Self-signed certificate auto-generation

If the files at `--tls-cert` and `--tls-key` do not exist when the server starts in non-production mode, 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:
   ```bash
   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:
   ```bash
   cargo run -p quicprochat-server -- \
     --allow-insecure-auth \
     --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

- **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.
- **ALPN**: The server advertises `b"qpc/1"` as the application-layer protocol.

---

## Storage

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

### File-backed store (default)

| File | Contents |
|---|---|
| `data/server-cert.der` | TLS certificate (DER) |
| `data/server-key.der` | TLS private key (DER) |
| `data/keypackages.bin` | `bincode`-serialised KeyPackage queues |
| `data/deliveries.bin` | `bincode`-serialised delivery queues |
| `data/hybridkeys.bin` | `bincode`-serialised hybrid (X25519 + ML-KEM-768) public keys |

### SQL store (recommended for production)

When `--store-backend=sql`, all data is persisted in a SQLCipher-encrypted database at `--db-path`. The SQLite driver is statically bundled (`rusqlite` with `bundled-sqlcipher`).

---

## Logging

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

```bash
# Default: info level
RUST_LOG=info cargo run -p quicprochat-server -- --allow-insecure-auth

# Debug level for detailed RPC tracing
RUST_LOG=debug cargo run -p quicprochat-server -- --allow-insecure-auth

# Filter to specific crates
RUST_LOG=quicprochat_server=debug,quinn=warn cargo run -p quicprochat-server -- --allow-insecure-auth
```

---

## Graceful shutdown

The server handles `SIGINT` (Ctrl-C) and `SIGTERM`. On receipt of a shutdown signal:

1. New connections are rejected immediately (`endpoint.close`).
2. In-flight RPC tasks are given `--drain-timeout` seconds (default: 30) to finish.
3. The process exits cleanly.

---

## Next steps

- [Running the Client](running-the-client.md) -- connect to the server and exercise the CLI
- [Demo Walkthrough](demo-walkthrough.md) -- step-by-step Alice-and-Bob group messaging scenario
- [Service Architecture](../architecture/service-architecture.md) -- how the NodeService combines AS and DS
