Decision
Four ALPN-negotiated QUIC protocols plus iroh-gossip.cdn/client/v1 covers all paid delivery — client→node and node→node.
Core CDN protocols
| Protocol | Purpose |
|---|---|
cdn/probe/v1 | Parallel latency + availability check before node selection |
cdn/client/v1 | Paid delivery: client→node and node→node (cache miss) |
cdn/dht/v1 | Kademlia-subset content discovery (content discovery) |
cdn/watchtower/v1 | Channel-dispute monitoring, voucher registration (watchtower) |
| iroh-gossip | Node metadata broadcast (NodeAnnounce), rate changes (RateChange), watchtower discovery (WatchtowerAnnounce) |
Companion protocol (app server — not a CDN participant)
| Protocol | Purpose |
|---|---|
cdn/keys/v1 | Epoch key delivery, play requests, offline leases — used by the app server |
Gossip topics
| Topic | Messages |
|---|---|
cdn/global/v1 | NodeAnnounce, RateChange, WatchtowerAnnounce |
cdn/region/{cc}/v1 | NodeAnnounce |
cdn/reputation/v1 | Reputation reports (reputation) |
Core message types
ProbeRequest / ProbeResponse
StreamRequest / StreamResponse
Voucher (client→node, off-chain)
Origin URL hiding
redirect in StreamResponse always points to a NodeId, never an external URL. The origin backend is never revealed. This is enforced at the schema level — the type is Option<NodeId>, not Option<String>.
Probe-triggered eviction hold
When a node signshas_blob: true in a ProbeResponse, the blob is temporarily exempt from eviction via the eviction hold (probe_hold_duration). This prevents false phantom-announcement slashing when cache pressure would otherwise evict a blob between probe and subsequent stream request.
Nodes expose decdn_probe_hold_slots_used / decdn_probe_hold_slots_max (observability) and should alert when saturation exceeds ~80%.
Error semantics
Stream errors use typed variants:BlobTooLarge, RateExceeded, ChannelInsufficient, Blacklisted, Unavailable. Clients should back off exponentially on Unavailable and re-probe on RateExceeded.
max_blob_size is per-node operational policy (recommended default 10 GB) — the protocol does not mandate a single value.
Source ADR: 005-protocol.md