Files
quicproquo/docs/src/architecture/service-architecture.md
Christian Nennemann d073f614b3 docs: rewrite mdBook documentation for v2 architecture
Update 25+ files and add 6 new pages to reflect the v2 migration from
Cap'n Proto to Protobuf framing over QUIC. Integrates SDK and Operations
docs into the mdBook, restructures SUMMARY.md, and rewrites the wire
format, architecture, and protocol sections with accurate v2 content.
2026-03-04 22:02:31 +01:00

13 KiB

Service Architecture

The quicproquo 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 qpq. Each RPC call uses a dedicated QUIC bidirectional stream; calls are concurrent and do not block each other.

quicproquo-server (port 5001, ALPN: "qpq")
|
+-- 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:

Client                                          Server
------                                          ------
1. UDP QUIC INITIAL ->

2.                       <- QUIC HANDSHAKE
   TLS 1.3 ServerHello +
   Certificate (self-signed)
   ALPN: "qpq"

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:

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 QPQ_LISTEN 0.0.0.0:5001 QUIC listen address (host:port).
--data-dir QPQ_DATA_DIR data Directory for persisted state.
--tls-cert QPQ_TLS_CERT data/server-cert.der Path to TLS certificate (DER). Auto-generated if missing.
--tls-key QPQ_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