# v2 Wire Format The quicproquo v2 protocol uses QUIC (RFC 9000) with TLS 1.3 as the transport layer and Protocol Buffers for message serialization. ## Connection - **Protocol**: QUIC with TLS 1.3 - **ALPN**: `qpq` - **Port**: 5001 (default) - **Certificate**: Server presents a TLS certificate; clients verify against a CA cert ## Frame Format Every RPC request and response is wrapped in a 10-byte binary header: ``` 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | method_id (u16) | req_id (u32) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | req_id (cont.) | payload_len (u32) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | payload_len (cont.) | protobuf payload ... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ``` | Field | Type | Bytes | Description | |---|---|---|---| | `method_id` | `u16` | 0-1 | RPC method identifier (network byte order) | | `req_id` | `u32` | 2-5 | Client-generated request correlation ID (network byte order) | | `payload_len` | `u32` | 6-9 | Length of the protobuf payload (network byte order) | | payload | bytes | 10+ | Protobuf-encoded request or response message | All multi-byte integers are **big-endian** (network byte order). ## Stream Model Each RPC call uses a **dedicated QUIC bidirectional stream**: 1. Client opens a new stream 2. Client sends the request frame and marks end-of-stream 3. Server reads the request, processes it, and sends the response frame 4. Server marks end-of-stream This allows concurrent RPCs without head-of-line blocking. ## Method IDs ### Auth (100-103) | ID | Method | Request | Response | |---|---|---|---| | 100 | `OpaqueRegisterStart` | `OpaqueRegisterStartRequest` | `OpaqueRegisterStartResponse` | | 101 | `OpaqueRegisterFinish` | `OpaqueRegisterFinishRequest` | `OpaqueRegisterFinishResponse` | | 102 | `OpaqueLoginStart` | `OpaqueLoginStartRequest` | `OpaqueLoginStartResponse` | | 103 | `OpaqueLoginFinish` | `OpaqueLoginFinishRequest` | `OpaqueLoginFinishResponse` | ### Delivery (200-205) | ID | Method | Request | Response | |---|---|---|---| | 200 | `Enqueue` | `EnqueueRequest` | `EnqueueResponse` | | 201 | `Fetch` | `FetchRequest` | `FetchResponse` | | 202 | `FetchWait` | `FetchWaitRequest` | `FetchWaitResponse` | | 203 | `Peek` | `PeekRequest` | `PeekResponse` | | 204 | `Ack` | `AckRequest` | `AckResponse` | | 205 | `BatchEnqueue` | `BatchEnqueueRequest` | `BatchEnqueueResponse` | ### Keys (300-304) | ID | Method | Request | Response | |---|---|---|---| | 300 | `UploadKeyPackage` | `UploadKeyPackageRequest` | `UploadKeyPackageResponse` | | 301 | `FetchKeyPackage` | `FetchKeyPackageRequest` | `FetchKeyPackageResponse` | | 302 | `UploadHybridKey` | `UploadHybridKeyRequest` | `UploadHybridKeyResponse` | | 303 | `FetchHybridKey` | `FetchHybridKeyRequest` | `FetchHybridKeyResponse` | | 304 | `FetchHybridKeys` | `FetchHybridKeysRequest` | `FetchHybridKeysResponse` | ### Channel (400) | ID | Method | Request | Response | |---|---|---|---| | 400 | `CreateChannel` | `CreateChannelRequest` | `CreateChannelResponse` | ### Group Management (410-413) | ID | Method | Request | Response | |---|---|---|---| | 410 | `RemoveMember` | `RemoveMemberRequest` | `RemoveMemberResponse` | | 411 | `UpdateGroupMetadata` | `UpdateGroupMetadataRequest` | `UpdateGroupMetadataResponse` | | 412 | `ListGroupMembers` | `ListGroupMembersRequest` | `ListGroupMembersResponse` | | 413 | `RotateKeys` | `RotateKeysRequest` | `RotateKeysResponse` | ### User (500-501) | ID | Method | Request | Response | |---|---|---|---| | 500 | `ResolveUser` | `ResolveUserRequest` | `ResolveUserResponse` | | 501 | `ResolveIdentity` | `ResolveIdentityRequest` | `ResolveIdentityResponse` | ### Blob (600-601) | ID | Method | Request | Response | |---|---|---|---| | 600 | `UploadBlob` | `UploadBlobRequest` | `UploadBlobResponse` | | 601 | `DownloadBlob` | `DownloadBlobRequest` | `DownloadBlobResponse` | ### Device (700-702) | ID | Method | Request | Response | |---|---|---|---| | 700 | `RegisterDevice` | `RegisterDeviceRequest` | `RegisterDeviceResponse` | | 701 | `ListDevices` | `ListDevicesRequest` | `ListDevicesResponse` | | 702 | `RevokeDevice` | `RevokeDeviceRequest` | `RevokeDeviceResponse` | ### P2P / Health (800-802) | ID | Method | Request | Response | |---|---|---|---| | 800 | `PublishEndpoint` | `PublishEndpointRequest` | `PublishEndpointResponse` | | 801 | `ResolveEndpoint` | `ResolveEndpointRequest` | `ResolveEndpointResponse` | | 802 | `Health` | `HealthRequest` | `HealthResponse` | ### Federation (900-905) | ID | Method | Request | Response | |---|---|---|---| | 900 | `RelayEnqueue` | `RelayEnqueueRequest` | `RelayEnqueueResponse` | | 901-905 | Reserved | -- | -- | ### Account (950) | ID | Method | Request | Response | |---|---|---|---| | 950 | `DeleteAccount` | `DeleteAccountRequest` | `DeleteAccountResponse` | ## Protobuf Definitions All message types are defined in `proto/qpq/v1/*.proto`: | File | Services | |---|---| | `auth.proto` | OPAQUE registration and login | | `common.proto` | Auth context, account deletion | | `delivery.proto` | Message enqueue, fetch, peek, ack | | `keys.proto` | MLS key packages, hybrid keys | | `channel.proto` | Channel creation | | `user.proto` | User/identity resolution | | `group.proto` | Group management | | `blob.proto` | Binary object storage | | `device.proto` | Multi-device management | | `p2p.proto` | P2P endpoints, health | | `federation.proto` | Cross-server relay | | `push.proto` | Push notifications | | `recovery.proto` | Account recovery | | `moderation.proto` | Content moderation | ## Authentication Flow Authentication uses the OPAQUE protocol (asymmetric PAKE): ``` Client Server │ │ │ OpaqueRegisterStart(username, │ │ registration_request) │ │ ──────────────────────────────────►│ │ │ │ OpaqueRegisterStartResponse( │ │ registration_response) │ │ ◄──────────────────────────────────│ │ │ │ OpaqueRegisterFinish(username, │ │ upload, identity_key) │ │ ──────────────────────────────────►│ │ │ │ OpaqueRegisterFinishResponse( │ │ success) │ │ ◄──────────────────────────────────│ │ │ │ OpaqueLoginStart(username, │ │ login_request) │ │ ──────────────────────────────────►│ │ │ │ OpaqueLoginStartResponse( │ │ login_response) │ │ ◄──────────────────────────────────│ │ │ │ OpaqueLoginFinish(username, │ │ finalization, identity_key) │ │ ──────────────────────────────────►│ │ │ │ OpaqueLoginFinishResponse( │ │ session_token) │ │ ◄──────────────────────────────────│ ``` The `session_token` is used for subsequent authenticated RPCs. ## Error Handling The server returns protobuf-encoded error responses on the same stream. Error conditions include: - Invalid method ID: stream reset - Authentication failure: error response with details - Rate limiting: error response with retry-after hint - Internal errors: generic error response