# Protocol Stack

quicprochat 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`. The v2 Protobuf framing protocol rides on individual QUIC streams,
one per RPC call.

```text
+---------------------------------------------+
|   Application / MLS ciphertext              |  <- group key ratchet (RFC 9420)
+---------------------------------------------+
|   Protobuf framing (custom binary header)   |  <- typed, length-prefixed 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: `qpc`.
- Multiplexed streams over a single UDP socket -- one bidirectional stream
  per RPC call, preventing head-of-line blocking.
- Uni-directional streams for server-to-client push events.

**Protobuf framing** (`quicprochat-rpc`, `quicprochat-proto`)

- Three frame types: Request, Response, Push.
- Fixed-length binary headers carry method/status codes, request correlation
  IDs, and payload length; zero-copy from header to `bytes::Bytes`.
- 44 RPC method IDs across 14 service categories.
- 4 push event types (NewMessage, Typing, Presence, Membership).
- All multi-byte integers in big-endian (network byte order).
- Maximum payload size: 4 MiB per frame.

**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: Protobuf**         | Typed serialisation, length-prefixed framing, method dispatch, push events | `quicprochat-rpc`, `prost`    |
| **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:

```text
Sender                                                            Recipient
------                                                            ---------

plaintext bytes
    |
    v
MLS create_message()
    |  -- encrypts with group AEAD key (AES-128-GCM) --
    v
TLS-encoded MlsMessageOut (opaque ciphertext blob)
    |
    v
Protobuf encode: EnqueueRequest { recipient_key, payload, ... }
    |
    v
RequestFrame: [200: u16][req_id: u32][len: u32][protobuf bytes]
    |
    v
QUIC bidirectional stream (TLS 1.3 encrypted)
    |
    v
    .............. network ..............
    |
    v
Server: handler reads RequestFrame, stores payload in queue
    |
    v
ResponseFrame: [0: u8 (Ok)][req_id: u32][len: u32][EnqueueResponse bytes]
    (or PushFrame on uni-stream when push event fires)
    |
    v
Client: Fetch(201) or receives PushFrame (event_type=1000)
    |
    v
MLS process_message()
    |  -- decrypts with group AEAD key --
    v
plaintext bytes
```

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

---

## Further Reading

- [Architecture Overview](overview.md) -- high-level system diagram and identity key model
- [QUIC + TLS 1.3](../protocol-layers/quic-tls.md) -- QUIC configuration, ALPN, and certificate handling
- [Protobuf Framing](../protocol-layers/capn-proto.md) -- frame format, method IDs, status codes
- [MLS (RFC 9420)](../protocol-layers/mls.md) -- ciphersuite selection, key schedule, and ratchet tree
- [Hybrid KEM: X25519 + ML-KEM-768](../protocol-layers/hybrid-kem.md) -- post-quantum envelope encryption
