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
11 KiB
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
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
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 -- frame format and transport parameters
- Method ID Reference -- all 44 method IDs
- Auth Schema -- OPAQUE authentication proto definitions
- RPC Reference -- all proto definitions for all 14 files
- Storage Backend -- how KeyPackages and hybrid keys are persisted