# Delivery and Keys Schema **Proto files:** `proto/qpc/v1/delivery.proto`, `proto/qpc/v1/keys.proto` **Package:** `qpc.v1` **Method IDs:** 200-205 (delivery), 300-304 (key packages and hybrid keys), 510-520 (key transparency) This page documents the Protobuf message definitions for the delivery service (store-and-forward message relay) and the key management service (MLS KeyPackages, hybrid post-quantum keys, and key transparency). --- ## delivery.proto The delivery service is a store-and-forward relay. It is intentionally MLS-unaware: all payloads are opaque byte strings routed by recipient key and channel ID. The server never inspects or decrypts message content. ### Full proto listing ```protobuf syntax = "proto3"; package qpc.v1; // Delivery service: enqueue, fetch, peek, ack, batch (6 methods). // Method IDs: 200-205. message Envelope { uint64 seq = 1; bytes data = 2; } message EnqueueRequest { bytes recipient_key = 1; bytes payload = 2; bytes channel_id = 3; uint32 ttl_secs = 4; // Client-generated idempotency key (16 bytes, UUID v7). // Server deduplicates enqueue requests with the same message_id within a TTL window. bytes message_id = 5; } message EnqueueResponse { uint64 seq = 1; bytes delivery_proof = 2; // True if this was a duplicate enqueue (message_id already seen). bool duplicate = 3; } message FetchRequest { bytes recipient_key = 1; bytes channel_id = 2; uint32 limit = 3; // Device ID for multi-device scoping. bytes device_id = 4; } message FetchResponse { repeated Envelope payloads = 1; } message FetchWaitRequest { bytes recipient_key = 1; bytes channel_id = 2; uint64 timeout_ms = 3; uint32 limit = 4; bytes device_id = 5; } message FetchWaitResponse { repeated Envelope payloads = 1; } message PeekRequest { bytes recipient_key = 1; bytes channel_id = 2; uint32 limit = 3; bytes device_id = 4; } message PeekResponse { repeated Envelope payloads = 1; } message AckRequest { bytes recipient_key = 1; bytes channel_id = 2; uint64 seq_up_to = 3; bytes device_id = 4; } message AckResponse {} message BatchEnqueueRequest { repeated bytes recipient_keys = 1; bytes payload = 2; bytes channel_id = 3; uint32 ttl_secs = 4; bytes message_id = 5; } message BatchEnqueueResponse { repeated uint64 seqs = 1; } ``` ### Envelope The `Envelope` wrapper is returned by fetch, peek, and fetch-wait operations. | Field | Type | Description | |-------|------|-------------| | `seq` | `uint64` | Server-assigned monotonic sequence number for ordering and acknowledgment. | | `data` | `bytes` | The original payload bytes submitted at enqueue time. | ### Enqueue (ID 200) Appends an opaque payload to a recipient's queue. Returns immediately. **Request:** | Field | Type | Description | |-------|------|-------------| | `recipient_key` | `bytes` | Recipient's Ed25519 identity public key (32 bytes). Primary queue index. | | `payload` | `bytes` | Opaque byte string. Typically a TLS-encoded MLS ciphertext blob. | | `channel_id` | `bytes` | Channel identifier (16-byte UUID v7 recommended). Empty = default channel. | | `ttl_secs` | `uint32` | Time-to-live in seconds. Server garbage-collects expired messages. 0 = server default. | | `message_id` | `bytes` | Client-generated idempotency key (16 bytes, UUID v7). Server deduplicates within the TTL window. | **Response:** | Field | Type | Description | |-------|------|-------------| | `seq` | `uint64` | Server-assigned sequence number for this message. | | `delivery_proof` | `bytes` | Cryptographic proof of delivery (reserved for future use). | | `duplicate` | `bool` | `true` if this `message_id` was already seen within the TTL window; the payload was not stored again. | ### Fetch (ID 201) Returns and retains queued messages up to `limit`. Does not remove messages from the queue; use `Ack` to advance the read cursor. **Request:** | Field | Type | Description | |-------|------|-------------| | `recipient_key` | `bytes` | Recipient's Ed25519 identity public key (32 bytes). | | `channel_id` | `bytes` | Channel identifier. Must match the value used at enqueue time. | | `limit` | `uint32` | Maximum number of envelopes to return. 0 = server default. | | `device_id` | `bytes` | Optional device identifier for multi-device queue scoping. | **Response:** | Field | Type | Description | |-------|------|-------------| | `payloads` | `repeated Envelope` | Messages in FIFO order. Empty list if no messages are pending. | ### FetchWait (ID 202) Long-poll variant of `Fetch`. Blocks on the server until messages arrive or `timeout_ms` elapses. **Request:** | Field | Type | Description | |-------|------|-------------| | `recipient_key` | `bytes` | Recipient's Ed25519 identity public key (32 bytes). | | `channel_id` | `bytes` | Channel identifier. | | `timeout_ms` | `uint64` | Maximum wait time in milliseconds. 0 = return immediately (equivalent to Fetch). | | `limit` | `uint32` | Maximum number of envelopes to return. | | `device_id` | `bytes` | Optional device identifier. | **Response:** Same as `FetchResponse`. FetchWait eliminates polling latency: the server holds the RPC open until a `Notify` is signalled by a concurrent `Enqueue` call, or until `timeout_ms` expires. ### Peek (ID 203) Non-destructive read. Returns messages without removing them and without advancing the acknowledgment cursor. **Request / Response:** Same field layout as `FetchRequest` / `FetchResponse`. Peek is useful for inspecting pending messages without marking them as delivered. ### Ack (ID 204) Advances the delivery cursor, removing all messages with `seq <= seq_up_to` from the queue. **Request:** | Field | Type | Description | |-------|------|-------------| | `recipient_key` | `bytes` | Recipient's Ed25519 identity public key (32 bytes). | | `channel_id` | `bytes` | Channel identifier. | | `seq_up_to` | `uint64` | All messages with sequence number <= this value are removed. | | `device_id` | `bytes` | Optional device identifier. | **Response:** Empty (`AckResponse {}`). ### BatchEnqueue (ID 205) Fan-out: enqueues the same payload to multiple recipients in a single RPC call. **Request:** | Field | Type | Description | |-------|------|-------------| | `recipient_keys` | `repeated bytes` | List of recipient Ed25519 identity public keys. | | `payload` | `bytes` | Opaque payload, delivered identically to all recipients. | | `channel_id` | `bytes` | Channel identifier. | | `ttl_secs` | `uint32` | Time-to-live in seconds. | | `message_id` | `bytes` | Idempotency key (16 bytes). | **Response:** | Field | Type | Description | |-------|------|-------------| | `seqs` | `repeated uint64` | Server-assigned sequence numbers, one per `recipient_key`, in the same order. | --- ## keys.proto Key management for MLS KeyPackages, hybrid post-quantum keys, and key transparency audit. ### Full proto listing ```protobuf syntax = "proto3"; package qpc.v1; // Key package + hybrid key CRUD (5 methods). // Method IDs: 300-304. message UploadKeyPackageRequest { bytes identity_key = 1; bytes package = 2; } message UploadKeyPackageResponse { bytes fingerprint = 1; } message FetchKeyPackageRequest { bytes identity_key = 1; } message FetchKeyPackageResponse { bytes package = 1; } message UploadHybridKeyRequest { bytes identity_key = 1; bytes hybrid_public_key = 2; } message UploadHybridKeyResponse {} message FetchHybridKeyRequest { bytes identity_key = 1; } message FetchHybridKeyResponse { bytes hybrid_public_key = 1; } message FetchHybridKeysRequest { repeated bytes identity_keys = 1; } message FetchHybridKeysResponse { repeated bytes keys = 1; } // Key revocation (method ID 510). message RevokeKeyRequest { bytes identity_key = 1; string reason = 2; // "compromised", "superseded", "user_revoked" } message RevokeKeyResponse { bool success = 1; uint64 leaf_index = 2; } // Check revocation status (method ID 511). message CheckRevocationRequest { bytes identity_key = 1; } message CheckRevocationResponse { bool revoked = 1; string reason = 2; uint64 timestamp_ms = 3; } // KT audit log retrieval (method ID 520). message AuditKeyTransparencyRequest { uint64 start = 1; uint64 end = 2; } message AuditKeyTransparencyResponse { repeated LogEntry entries = 1; uint64 tree_size = 2; bytes root = 3; } message LogEntry { uint64 index = 1; bytes leaf_hash = 2; } ``` ### UploadKeyPackage (ID 300) Uploads a single-use MLS KeyPackage. KeyPackages are stored in a FIFO queue per identity; each is consumed once by `FetchKeyPackage`. | Field | Type | Description | |-------|------|-------------| | `identity_key` | `bytes` | Uploader's Ed25519 identity public key (32 bytes). Index key for the queue. | | `package` | `bytes` | openmls-serialised KeyPackage (bincode format, as required by `DiskKeyStore`). | Response: `fingerprint` -- SHA-256 digest of the stored package (32 bytes). Callers should record this to detect tampering. ### FetchKeyPackage (ID 301) Fetches and atomically removes one KeyPackage for the given identity. Returns empty bytes if no packages are stored. The removal is atomic; concurrent fetches will not receive the same package. ### UploadHybridKey (ID 302) Uploads the client's hybrid (X25519 + ML-KEM-768) public key. Unlike KeyPackages, hybrid keys are not single-use -- each identity stores exactly one, overwriting the previous value. | Field | Type | Description | |-------|------|-------------| | `identity_key` | `bytes` | Uploader's Ed25519 identity public key (32 bytes). | | `hybrid_public_key` | `bytes` | Concatenated X25519 public key (32 bytes) + ML-KEM-768 encapsulation key. | ### FetchHybridKey (ID 303) Fetches a single peer's hybrid public key. Non-destructive. ### FetchHybridKeys (ID 304) Batch variant of `FetchHybridKey`. Returns one key per input identity key, in the same order. Missing keys are returned as empty bytes at the corresponding index. ### RevokeKey (ID 510) Revokes an identity key by appending a revocation entry to the key transparency Merkle log. | Field | Type | Description | |-------|------|-------------| | `identity_key` | `bytes` | Identity key to revoke (32 bytes). | | `reason` | `string` | One of: `"compromised"`, `"superseded"`, `"user_revoked"`. | Response: `leaf_index` is the index of the revocation entry in the KT Merkle log. ### CheckRevocation (ID 511) Checks whether an identity key has been revoked. Response fields: `revoked` (bool), `reason` (string), `timestamp_ms` (uint64 unix milliseconds of the revocation event). ### AuditKeyTransparency (ID 520) Returns a range of entries from the key transparency append-only Merkle log. | Field | Type | Description | |-------|------|-------------| | `start` | `uint64` | First leaf index (inclusive). | | `end` | `uint64` | Last leaf index (exclusive). 0 = up to current tree size. | Response: `entries` (list of `LogEntry`), `tree_size` (current log size), `root` (Merkle root hash). --- ## Further reading - [Wire Format Overview](overview.md) -- frame format and transport parameters - [Method ID Reference](envelope-schema.md) -- all 44 method IDs - [Auth Schema](auth-schema.md) -- OPAQUE authentication proto definitions - [RPC Reference](node-service-schema.md) -- all proto definitions for all 14 files - [Storage Backend](../internals/storage-backend.md) -- how KeyPackages and hybrid keys are persisted