Files
quicproquo/docs/src/architecture/protocol-stack.md
Chris Nennemann 853ca4fec0 chore: rename project quicnprotochat -> quicproquo (binaries: qpq)
Rename the entire workspace:
- Crate packages: quicnprotochat-{core,proto,server,client,gui,p2p,mobile} -> quicproquo-*
- Binary names: quicnprotochat -> qpq, quicnprotochat-server -> qpq-server,
  quicnprotochat-gui -> qpq-gui
- Default files: *-state.bin -> qpq-state.bin, *-server.toml -> qpq-server.toml,
  *.db -> qpq.db
- Environment variable prefix: QUICNPROTOCHAT_* -> QPQ_*
- App identifier: chat.quicnproto.gui -> chat.quicproquo.gui
- Proto package: quicnprotochat.bench -> quicproquo.bench
- All documentation, Docker, CI, and script references updated

HKDF domain-separation strings and P2P ALPN remain unchanged for
backward compatibility with existing encrypted state and wire protocol.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 20:11:51 +01:00

5.1 KiB

Protocol Stack

quicproquo layers three protocol stages to move a plaintext message from sender to recipient with end-to-end encryption, typed RPC framing, and authenticated transport. This page describes each layer and provides a comparison table.


Transport: QUIC + TLS 1.3

The transport layer is QUIC over UDP with TLS 1.3 negotiated by quinn and rustls. Cap'n Proto RPC rides on a bidirectional QUIC stream.

┌─────────────────────────────────────────────┐
│   Application / MLS ciphertext              │  <- group key ratchet (RFC 9420)
├─────────────────────────────────────────────┤
│   Cap'n Proto RPC                           │  <- typed, schema-versioned framing
├─────────────────────────────────────────────┤
│   QUIC + TLS 1.3  (quinn / rustls)          │  <- mutual auth + transport secrecy
└─────────────────────────────────────────────┘

What each layer provides

QUIC + TLS 1.3 (quinn, rustls)

  • Encrypted, authenticated transport with 0-RTT connection establishment (where resumed).
  • TLS 1.3 provides perfect forward secrecy per connection via ephemeral ECDHE.
  • The server presents a self-signed certificate by default; the client pins the server certificate via --ca-cert.
  • ALPN protocol identifier: capnp.
  • Multiplexed streams over a single UDP socket -- one bidirectional stream per RPC session.

Cap'n Proto RPC (capnp, capnp-rpc)

  • Zero-copy, schema-versioned serialisation.
  • Asynchronous RPC with promise pipelining (multiple in-flight calls).
  • The NodeService interface (defined in schemas/node.capnp) multiplexes Authentication and Delivery operations on a single connection.
  • The two-party VatNetwork runs over tokio::io::compat adapters wrapping QUIC send/recv streams.

MLS (RFC 9420) (openmls, openmls_rust_crypto)

  • Group key agreement with ratchet-tree-based key schedule.
  • Forward secrecy: past messages remain confidential if a member's key is compromised.
  • Post-compromise security (PCS): the group heals after a compromise once an honest update occurs.
  • Identity binding: each member's Ed25519 public key is embedded in the MLS BasicCredential.
  • Ciphersuite: MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519.

Comparison Table

Layer Provides Crate(s)
Transport: QUIC + TLS 1.3 Confidentiality, server authentication, forward secrecy, multiplexed streams, congestion control quinn, rustls
Framing: Cap'n Proto Zero-copy typed serialisation, schema versioning, async RPC with promise pipelining capnp, capnp-rpc
Encryption: MLS Group key agreement, forward secrecy, post-compromise security, identity binding openmls, openmls_rust_crypto
Encryption: Hybrid KEM (optional) Post-quantum confidentiality for individual payloads (X25519 + ML-KEM-768) ml-kem, x25519-dalek, chacha20poly1305, hkdf

Data Path Summary

A plaintext message traverses the stack as follows:

Sender                                                            Recipient
──────                                                            ─────────

plaintext bytes
    │
    ▼
MLS create_message()
    │  ── encrypts with group AEAD key (AES-128-GCM) ──
    ▼
TLS-encoded MlsMessageOut (opaque ciphertext blob)
    │
    ▼
Cap'n Proto: enqueue(recipientKey, payload)
    │  ── serialised into NodeService RPC call ──
    ▼
QUIC stream (TLS 1.3 encrypted)
    │
    ▼
    ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ network ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
    │
    ▼
Server: NodeService.enqueue() stores payload in FIFO queue
    │
    ▼
Cap'n Proto: fetch() / fetchWait() returns payload
    │
    ▼
MLS process_message()
    │  ── decrypts with group AEAD key ──
    ▼
plaintext bytes

The server never holds the MLS group key. It sees only the encrypted MlsMessageOut blob.


Further Reading