Skip to main content

Decision

Content is encrypted with envelope encryption: each blob gets a random symmetric key (K_blob, XChaCha20-Poly1305). The ciphertext is content-addressed and cached normally — one hash, one copy for all clients. An app server, operated by the content provider, gates access: on each play request it wraps K_blob with a rotating epoch key and delivers the envelope over authenticated cdn/keys/v1. CDN nodes only ever see ciphertext. Closing the epoch key stream revokes access within one epoch (~5 minutes).

Why envelope?

  • One copy. Encrypting per-viewer would defeat caching — the whole network would store N copies of every blob for N viewers. Envelope encryption encrypts the key per-session, not the bytes.
  • Fast rotation. Re-wrapping K_blob under a new epoch key is O(bytes-in-key-envelope), not O(blob-size).
  • Clean revocation. An access revocation closes the epoch key stream; within one epoch the client can no longer decrypt new requests.

Flow

App server responsibilities

  • Stores K_blob for every encrypted blob (received from origin at ingest).
  • Authenticates client sessions (OAuth, session token — content provider’s existing stack).
  • Issues envelopes on play requests: envelope = epoch_key.wrap(K_blob).
  • Rotates the epoch key every 5 minutes.
  • Delivers envelopes over an authenticated cdn/keys/v1 QUIC stream.
  • Optionally issues offline leases — client-sealed envelopes that the client persists to a local keystore for offline playback.
The app server shares the iroh QUIC transport with the CDN but is not a CDN protocol participant. It does not gossip, probe, or stake.

Why iroh QUIC for cdn/keys/v1?

Key delivery is tightly coupled to the client’s iroh identity. The QUIC handshake provides mutual authentication (client NodeId ↔ app server NodeId) and TLS 1.3 confidentiality in one step — eliminating the need for separate crypto_box_seal + X25519 key management. Clients run a single transport stack for both CDN delivery and key delivery.

0-RTT is rejected here

cdn/keys/v1 does not accept 0-RTT early data. The protocol requires EpochKeyAuth before any request stream, and 0-RTT would break that authentication sequencing.

Trust boundary and blast radius

  • CDN nodes only see ciphertext. Compromise of a node does not leak plaintext.
  • App server compromise exposes all content served through that app server — K_blob keys are long-lived there. Mitigation: epoch key rotation limits access-control blast radius, but does not provide forward secrecy for stored K_blob. Accepted trade-off.
  • Epoch key forward secrecy is a protocol property: epoch keys are ephemeral and rotate every 5 minutes, so a compromised epoch key cannot decrypt past envelopes retained by other clients.
Source ADR: 006-e2e-encryption.md