Files
quicproquo/docs/src/architecture/service-architecture.md
Christian Nennemann 2e081ead8e 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
2026-03-21 19:14:06 +01:00

363 lines
13 KiB
Markdown

# Service Architecture
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
the v2 Protobuf framing protocol. This page documents the method reference,
connection lifecycle, storage model, and authentication flow.
---
## RPC Endpoint
A single QUIC + TLS 1.3 listener on **port 5001** serves all operations.
The ALPN identifier is `qpc`. Each RPC call uses a dedicated QUIC
bidirectional stream; calls are concurrent and do not block each other.
```text
quicprochat-server (port 5001, ALPN: "qpc")
|
+-- Auth (100-103)
| +-- 100: OpaqueRegisterStart
| +-- 101: OpaqueRegisterFinish
| +-- 102: OpaqueLoginStart
| +-- 103: OpaqueLoginFinish
|
+-- Delivery (200-205)
| +-- 200: Enqueue
| +-- 201: Fetch
| +-- 202: FetchWait
| +-- 203: Peek
| +-- 204: Ack
| +-- 205: BatchEnqueue
|
+-- Keys (300-304)
| +-- 300: UploadKeyPackage
| +-- 301: FetchKeyPackage
| +-- 302: UploadHybridKey
| +-- 303: FetchHybridKey
| +-- 304: FetchHybridKeys
|
+-- Channel (400)
| +-- 400: CreateChannel
|
+-- Group Management (410-413)
| +-- 410: RemoveMember
| +-- 411: UpdateGroupMetadata
| +-- 412: ListGroupMembers
| +-- 413: RotateKeys
|
+-- Moderation (420-424)
| +-- 420: ReportMessage
| +-- 421: BanUser
| +-- 422: UnbanUser
| +-- 423: ListReports
| +-- 424: ListBanned
|
+-- User (500-501)
| +-- 500: ResolveUser
| +-- 501: ResolveIdentity
|
+-- Key Transparency (510-520)
| +-- 510: RevokeKey
| +-- 511: CheckRevocation
| +-- 520: AuditKeyTransparency
|
+-- Blob (600-601)
| +-- 600: UploadBlob
| +-- 601: DownloadBlob
|
+-- Device (700-710)
| +-- 700: RegisterDevice
| +-- 701: ListDevices
| +-- 702: RevokeDevice
| +-- 710: RegisterPushToken
|
+-- Recovery (750-752)
| +-- 750: StoreRecoveryBundle
| +-- 751: FetchRecoveryBundle
| +-- 752: DeleteRecoveryBundle
|
+-- P2P (800-802)
| +-- 800: PublishEndpoint
| +-- 801: ResolveEndpoint
| +-- 802: Health
|
+-- Federation (900-905)
| +-- 900: RelayEnqueue
| +-- 901: RelayBatchEnqueue
| +-- 902: ProxyFetchKeyPackage
| +-- 903: ProxyFetchHybridKey
| +-- 904: ProxyResolveUser
| +-- 905: FederationHealth
|
+-- Account (950)
+-- 950: DeleteAccount
Push event types (server -> client, uni-stream):
1000: PushNewMessage
1001: PushTyping
1002: PushPresence
1003: PushMembership
```
---
## RPC Method Reference
### Auth (100-103)
OPAQUE password authentication (asymmetric PAKE). The password is never sent
to the server. Method IDs 100-103 implement the 4-step OPAQUE handshake.
| ID | Method | Description |
|-----|-------------------------|-------------|
| 100 | `OpaqueRegisterStart` | Client initiates registration with `username` and OPAQUE `registration_request` blob. Server returns `registration_response`. |
| 101 | `OpaqueRegisterFinish` | Client completes registration with `username`, OPAQUE `upload` blob, and Ed25519 `identity_key`. Server stores the OPAQUE record. |
| 102 | `OpaqueLoginStart` | Client initiates login with `username` and OPAQUE `login_request` blob. Server returns `login_response`. |
| 103 | `OpaqueLoginFinish` | Client completes login with `username`, OPAQUE `finalization` blob, and `identity_key`. Server returns a `session_token`. |
The `session_token` is an opaque bearer token used for subsequent authenticated
RPCs. It is passed in the Protobuf request body (not as a frame-level header).
### Delivery (200-205)
Store-and-forward relay. The server never inspects MLS ciphertext -- it routes
opaque byte blobs by recipient key.
| ID | Method | Description |
|-----|----------------|-------------|
| 200 | `Enqueue` | Append an opaque payload to the recipient's FIFO queue. Wakes `FetchWait` waiters. |
| 201 | `Fetch` | Drain and return all queued payloads in FIFO order. |
| 202 | `FetchWait` | Same as `Fetch`, but long-polls if the queue is empty (up to `timeout_ms`). |
| 203 | `Peek` | Return queued payloads without removing them. |
| 204 | `Ack` | Acknowledge and remove specific payloads by sequence number. |
| 205 | `BatchEnqueue` | Enqueue multiple payloads in a single RPC call. |
### Keys (300-304)
MLS KeyPackage distribution and hybrid PQ public key management.
| ID | Method | Description |
|-----|--------------------|-------------|
| 300 | `UploadKeyPackage` | Append a TLS-encoded MLS KeyPackage to the identity's queue. Single-use: each fetch atomically removes one. |
| 301 | `FetchKeyPackage` | Atomically pop and return the oldest KeyPackage for an identity. Returns empty if none. |
| 302 | `UploadHybridKey` | Store (or replace) the X25519+ML-KEM-768 hybrid public key for an identity. |
| 303 | `FetchHybridKey` | Return the stored hybrid public key for a single identity. |
| 304 | `FetchHybridKeys` | Return hybrid public keys for multiple identities in one call. |
### Group Management (400, 410-413)
| ID | Method | Description |
|-----|-----------------------|-------------|
| 400 | `CreateChannel` | Register a new channel (group) on the server. |
| 410 | `RemoveMember` | Remove a member from a group (server-side record). |
| 411 | `UpdateGroupMetadata` | Update group name, description, or settings. |
| 412 | `ListGroupMembers` | List all members of a group. |
| 413 | `RotateKeys` | Trigger a server-assisted key rotation event. |
### User / Identity (500-501)
| ID | Method | Description |
|-----|-------------------|-------------|
| 500 | `ResolveUser` | Resolve a username to an Ed25519 public key. |
| 501 | `ResolveIdentity` | Resolve an identity key to user profile information. |
### Key Transparency (510-520)
| ID | Method | Description |
|-----|--------------------------|-------------|
| 510 | `RevokeKey` | Append a key revocation record to the transparency log. |
| 511 | `CheckRevocation` | Check whether a given key has been revoked. |
| 520 | `AuditKeyTransparency` | Fetch a transparency log audit proof for a key. |
### Blob Storage (600-601)
| ID | Method | Description |
|-----|----------------|-------------|
| 600 | `UploadBlob` | Store a binary blob (file attachment, avatar, etc.). Returns a content-addressed blob ID. |
| 601 | `DownloadBlob` | Retrieve a blob by ID. |
### Device Management (700-710)
| ID | Method | Description |
|-----|-----------------------|-------------|
| 700 | `RegisterDevice` | Register a new device for a user account. |
| 701 | `ListDevices` | List all registered devices for the authenticated user. |
| 702 | `RevokeDevice` | Revoke a device, invalidating its session. |
| 710 | `RegisterPushToken` | Register a push notification token (APNs / FCM) for a device. |
### Recovery (750-752)
| ID | Method | Description |
|-----|-------------------------|-------------|
| 750 | `StoreRecoveryBundle` | Encrypt and store an account recovery bundle server-side. |
| 751 | `FetchRecoveryBundle` | Retrieve the recovery bundle (requires OPAQUE re-authentication). |
| 752 | `DeleteRecoveryBundle` | Delete the stored recovery bundle. |
### P2P and Health (800-802)
| ID | Method | Description |
|-----|--------------------|-------------|
| 800 | `PublishEndpoint` | Publish a direct P2P endpoint (iroh node address). |
| 801 | `ResolveEndpoint` | Resolve a peer's P2P endpoint by identity key. |
| 802 | `Health` | Liveness/readiness probe. Returns server uptime and status. |
### Federation (900-905)
| ID | Method | Description |
|-----|-------------------------|-------------|
| 900 | `RelayEnqueue` | Relay a single message to a user on another server. |
| 901 | `RelayBatchEnqueue` | Relay multiple messages in one request. |
| 902 | `ProxyFetchKeyPackage` | Fetch a KeyPackage from a remote server on behalf of a local client. |
| 903 | `ProxyFetchHybridKey` | Fetch a hybrid public key from a remote server. |
| 904 | `ProxyResolveUser` | Resolve a username on a remote server. |
| 905 | `FederationHealth` | Check health of the federation link to another server. |
### Moderation (420-424)
| ID | Method | Description |
|-----|----------------|-------------|
| 420 | `ReportMessage` | Submit a content moderation report. |
| 421 | `BanUser` | Ban a user from a channel or server-wide. |
| 422 | `UnbanUser` | Lift a ban. |
| 423 | `ListReports` | List pending moderation reports (admin only). |
| 424 | `ListBanned` | List banned users (admin only). |
### Account (950)
| ID | Method | Description |
|-----|-----------------|-------------|
| 950 | `DeleteAccount` | Permanently delete the authenticated account and all associated data. |
---
## Per-Connection Lifecycle
Each incoming QUIC connection follows this sequence:
```text
Client Server
------ ------
1. UDP QUIC INITIAL ->
2. <- QUIC HANDSHAKE
TLS 1.3 ServerHello +
Certificate (self-signed)
ALPN: "qpc"
3. Client verifies server cert against
pinned CA cert (--ca-cert flag)
4. QUIC connection established
5. Per RPC call:
Client opens bidirectional stream
Client writes RequestFrame:
[method_id: u16][req_id: u32][len: u32][protobuf]
Client marks end-of-write
6. Server reads RequestFrame
Server dispatches to handler by method_id
Handler processes, writes ResponseFrame:
[status: u8][req_id: u32][len: u32][protobuf]
7. For push events (server -> client):
Server opens uni-stream
Server writes PushFrame:
[event_type: u16][len: u32][protobuf]
8. Multiple RPCs run concurrently
(each on its own stream)
```
### Concurrency model
Unlike the v1 Cap'n Proto RPC (which was `!Send` due to `Rc<RefCell<>>`
internals and required `LocalSet`), the v2 RPC framework uses `Arc`-based
shared state and spawns each handler with `tokio::spawn`. The server can
handle many concurrent requests per connection without a `LocalSet`.
---
## Status Codes
Response frames carry a `status: u8` field:
| Value | Status | Meaning |
|-------|------------------|---------|
| 0 | `Ok` | Success |
| 1 | `BadRequest` | Malformed request or missing required field |
| 2 | `Unauthorized` | Missing or invalid session token |
| 3 | `Forbidden` | Valid token but insufficient permissions |
| 4 | `NotFound` | Requested resource does not exist |
| 5 | `RateLimited` | Request rate limit exceeded; retry after backoff |
| 8 | `DeadlineExceeded` | Request timed out on the server |
| 9 | `Unavailable` | Server temporarily unable to serve the request |
| 10 | `Internal` | Unexpected server error |
| 11 | `UnknownMethod` | The requested method_id is not registered |
---
## Authentication Flow
OPAQUE (RFC-compliant asymmetric PAKE) prevents the password from reaching
the server in any form:
```text
Client Server
| |
| OpaqueRegisterStart(100): |
| username, registration_request |
| --------------------------------->|
| |
| registration_response |
| <---------------------------------|
| |
| OpaqueRegisterFinish(101): |
| username, upload, identity_key |
| --------------------------------->|
| |
| success |
| <---------------------------------|
| |
| OpaqueLoginStart(102): |
| username, login_request |
| --------------------------------->|
| |
| login_response |
| <---------------------------------|
| |
| OpaqueLoginFinish(103): |
| username, finalization, |
| identity_key |
| --------------------------------->|
| |
| session_token |
| <---------------------------------|
```
The `session_token` is then passed in subsequent Protobuf requests. The server
validates it on every authenticated method call.
---
## Configuration
| Flag | Env var | Default | Description |
|----------------|----------------------------|------------------------|-------------|
| `--listen` | `QPC_LISTEN` | `0.0.0.0:5001` | QUIC listen address (host:port). |
| `--data-dir` | `QPC_DATA_DIR` | `data` | Directory for persisted state. |
| `--tls-cert` | `QPC_TLS_CERT` | `data/server-cert.der` | Path to TLS certificate (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`).
---
## Further Reading
- [Architecture Overview](overview.md) -- two-service model and system diagram
- [End-to-End Data Flow](data-flow.md) -- sequence diagrams for registration, group creation, and messaging
- [Protobuf Framing](../protocol-layers/capn-proto.md) -- frame format details and method ID constants
- [Wire Format Reference](../wire-format/overview.md) -- full Protobuf schema documentation