Iroh: Peer-to-Peer QUIC Networking & Content-Addressed Data
A source-grounded survey of iroh's Rust implementation — n0's stack for authenticated peer-to-peer QUIC connectivity and content-addressed data transfer — read at workspace v1.0.1 (commit 22cac742ca) to inform a clean-room native D reimplementation running on sparkles:event-horizon (single-threaded, completion-first io_uring/kqueue/IOCP, fibers + algebraic effects). Each subsystem was read against its pinned tree, cited to real path:lines, and written up as an independent deep-dive; this page is the map that ties them together.
This survey answers eight questions:
- What is a peer, and how does it prove who it is? — every endpoint is a single Ed25519 keypair whose 32-byte public key is its
EndpointId, authenticated in an RFC 7250 raw-public-key TLS 1.3 handshake carried in QUICCRYPTOframes — no PKI, no X.509. See Identity & Cryptography (shared vocabulary in Concepts). - How does every iroh value become bytes? — exactly one application codec (
postcard), base32-wrapped tickets, and an ALPN registry. See Wire Formats & Serialization. - What carries the connection, and what must a port rebuild? —
noq, n0's sans-io fork ofquinnextended with QUIC Multipath, QUIC Address Discovery, and an in-QUIC NAT-traversal protocol. See QUIC Transport (noq); the app-facingconnect/accept+ protocol-router shell is Endpoint & Protocol Router. - How does one logical connection ride many changing network paths? — the multipath Socket (the module formerly called
magicsock) that fans QUIC datagrams across direct, IPv6, and relay carriers under onenoq::AsyncUdpSocket. - How do two NATed peers get a direct path, and what happens when they cannot? — QUIC-native simultaneous-open NAT Traversal & Address Discovery (the
DISCOUDP side-protocol and STUN are gone); the always-reachable Relay is rendezvous and fallback; Net Report measures the local vantage point vianetwatch/portmapper. - How do you reach a peer knowing only its key? — Address Lookup: BEP-44-signed records published and resolved over pkarr relays and the DNS.
- How is application data moved, verified, synced, and broadcast? — content-addressed Blobs over BLAKE3 verified streaming, multi-writer Document Sync, and epidemic Gossip.
- What concurrency runs all of this, and how does it collapse onto a single-threaded loop? — a complete Tokio Concurrency Inventory feeding the D Architecture Migration onto
sparkles:event-horizon.
NOTE
Scope. This is the master index for the iroh research tree. Each row below links to a deep-dive that was written and fact-checked independently against its pinned source tree; where this index summarizes a subsystem, the deep-dive is the source of truth. The workspace is pinned at iroh v1.0.1 (22cac742ca); companion crates at iroh-blobs 0.103.0, iroh-docs 0.101.0, iroh-gossip 0.101.0, noq 1.0.1 (noq-v1.0.1), bao-tree 0.16.0, iroh-util 0.6.0, and irpc 0.17.0. iroh 1.0 is a major restructure of the 0.x API — magicsock → the socket layer, "discovery" → address_lookup, NodeId → EndpointId, and the DISCO UDP side-protocol + STUN replaced by QUIC-native NAT traversal + QUIC Address Discovery inside noq-proto. NodeAddr-era folklore does not compile; treat pre-1.0 material with suspicion.
Last reviewed: July 6, 2026
Master Catalog
One row per subsystem, grouped by layer. ALPN / role gives the wire-bearing ALPN string where the subsystem owns one, otherwise its function. Versions are the pinned tags read; per-path:line provenance lives in each deep-dive's Sources block.
Foundations
| Subsystem | Crate(s) | Version | ALPN / role | What it does | Link |
|---|---|---|---|---|---|
| Identity & Cryptography | iroh-base, iroh (tls/*) | iroh 1.0.1 (22cac742ca) | none — raw-public-key TLS 1.3 identity | Ed25519 keypair = EndpointId; RFC 7250 handshake; BLAKE3 auxiliary MACs; no PKI, no ECDH | Deep-dive → |
| Wire Formats & Serialization | iroh-tickets, iroh-base, postcard | iroh-tickets 1.0.0 / iroh-base 1.0.1 | owns the ALPN registry | one non-self-describing postcard codec, base32 tickets, five base codecs, per-protocol framing | Deep-dive → |
QUIC Transport (noq) | noq, noq-proto, noq-udp | noq-v1.0.1 (340e9c7) | none — transport is ALPN-agnostic | sans-io QUIC v1 state machine + Multipath + Address Discovery + in-QUIC NAT traversal (≈48k LoC) | Deep-dive → |
Connectivity
| Subsystem | Crate(s) | Version | ALPN / role | What it does | Link |
|---|---|---|---|---|---|
| Endpoint & Protocol Router | iroh (endpoint, protocol, runtime) | iroh 1.0.1 (22cac742ca) | app-chosen (e.g. b"iroh-example/echo/0") | cheaply-clonable Endpoint over noq; Router multiplexes inbound connections onto ProtocolHandlers | Deep-dive → |
| The Multipath Socket | iroh (socket, ex-magicsock) | iroh 1.0.1 (22cac742ca) | none — sits below QUIC/TLS | one AsyncUdpSocket over IP + relay + custom carriers; synthetic-IPv6 mapped addrs; path selection | Deep-dive → |
| NAT Traversal & Address Discovery | noq-proto, noq, iroh, iroh-relay | noq-v1.0.1 / iroh 1.0.1 | /iroh-qad/0 (QAD server); QNT rides the app connection | simultaneous-open ADD_ADDRESS/REACH_OUT/REMOVE_ADDRESS over the encrypted 1-RTT dataplane | Deep-dive → |
| The Relay Protocol | iroh-relay, iroh (relay transport) | iroh-relay 1.0.1 | WS subprotocol iroh-relay-v2; QAD ALPN /iroh-qad/0 | authenticated WebSocket packet-forwarder keyed on EndpointId + a bare QAD QUIC endpoint (port 7842) | Deep-dive → |
| Address Lookup (Discovery) | iroh, iroh-dns, iroh-dns-server | iroh 1.0.1 / iroh-dns 1.0.1 | none — DNS + pkarr HTTP(S) | EndpointId → dialable addresses via BEP-44-signed DNS packets over pkarr relays and the DNS | Deep-dive → |
| Net Report, Watching & Port Mapping | iroh (net_report), netwatch, portmapper | iroh 1.0.1 / netwatch 0.19.1 / portmapper 0.19.1 | /iroh-qad/0 probe; HTTPS fallback | measures UDP reachability, public address, NAT type, home relay; interface watching + UPnP/PCP/NAT-PMP | Deep-dive → |
Protocols
| Subsystem | Crate(s) | Version | ALPN / role | What it does | Link |
|---|---|---|---|---|---|
| Blobs: Content-Addressed Transfer | iroh-blobs, iroh-util, irpc, bao-tree | iroh-blobs 0.103.0 | /iroh-bytes/4 | request/response BLAKE3 verified-stream transfer; crash-consistent store; resumable multi-provider downloader | Deep-dive → |
BLAKE3 Verified Streaming (bao-tree) | bao-tree | bao-tree 0.16.0 | n/a — a codec, not a wire protocol | exposes BLAKE3's internal Merkle tree as an incrementally-verifiable stream (16 KiB chunk groups) | Deep-dive → |
Document Sync (iroh-docs) | iroh-docs | iroh-docs 0.101.0 | /iroh-sync/1 | multi-writer eventually-consistent KV store; recursive range-fingerprint set reconciliation | Deep-dive → |
Epidemic Broadcast (iroh-gossip) | iroh-gossip | iroh-gossip 0.101.0 | /iroh-gossip/1 (per-instance overridable) | HyParView partial-view membership + Plumtree eager/lazy-push broadcast tree, as an IO-less state machine | Deep-dive → |
D migration
| Subsystem | Crate(s) | Version | ALPN / role | What it does | Link |
|---|---|---|---|---|---|
| Tokio Concurrency Inventory | n0-future, n0-watcher, irpc, all | workspace 1.0.1 (irpc 0.17.0) | none — process-internal plumbing | census of every task, channel, lock, select! loop, spawn_blocking, timer; protocol-inherent vs Send + Sync artifact | Deep-dive → |
NOTE
Two catalog entries are cross-cutting rather than subsystems: Concepts & Vocabulary (the shared glossary every page references) and D Architecture Migration (the capstone that maps the whole stack onto event-horizon). Both are in Quick Navigation, not the Master Catalog.
Taxonomy
Three re-cuts of the same fourteen subsystems, one axis each. Every row links back to a deep-dive.
By layer
The catalog's grouping: where a subsystem sits from the identity root up to the runtime that drives it. Higher layers depend on lower ones.
| Layer | Concern | Subsystems |
|---|---|---|
| Foundations | Identity, bytes, and the transport state machine everything rides | Identity & Cryptography, Wire Formats, QUIC Transport |
| Connectivity | Getting an authenticated QUIC path to a peer, wherever it is | Endpoint, Socket, NAT Traversal, Relay, Address Lookup, Net Report |
| Protocols | Application services built on top of a connection | Blobs, bao-tree, Document Sync, Gossip |
| D migration | Porting artifacts — the concurrency census and the architecture plan | Concurrency Inventory, D Architecture Migration |
By network role
The same set cut by what job it does on the wire rather than where it sits in the stack.
| Role | Question it answers | Subsystems |
|---|---|---|
| Identity & encoding | Who is this peer, and how do its values serialize? | Identity & Cryptography, Wire Formats |
| Transport | How is an authenticated, multiplexed byte pipe established? | QUIC Transport, Endpoint |
| Connectivity / NAT | How do we reach a peer, and stay reached as the net changes? | Socket, NAT Traversal, Relay, Address Lookup, Net Report |
| Data protocols | What application semantics ride the connection? | Blobs, bao-tree, Document Sync, Gossip |
| Runtime | What schedules all of the above? | Concurrency Inventory |
By port difficulty
The axis that matters most for the D reimplementation: how a subsystem should be acquired. A subsystem can appear in more than one class — Socket, for instance, is both policy to re-implement and raw OS surface to bind.
| Class | What the port does | Subsystems |
|---|---|---|
| Reuse a sans-io core | The logic is already a pure (state, event) → effects machine with no OS or runtime coupling; re-derive the state machine and drive it from a fiber | QUIC Transport (noq-proto), Gossip (proto/), Document Sync (range reconciler), bao-tree, Wire Formats (postcard) |
| Reimplement policy / glue | Moderate protocol and policy logic over a sans-io core; no exotic OS or crypto surface | Endpoint, Socket (routing fabric), NAT Traversal (orchestration), Relay, Address Lookup, Blobs (protocol/store/downloader), Concurrency substrate |
| Needs an OS binding | Must bind raw per-OS surfaces the language runtime does not provide | Net Report (netwatch netlink/AF_ROUTE//proc/net/route/IP Helper/getifaddrs; portmapper UPnP/PCP/NAT-PMP), Socket (UDP send/recv, GSO/GRO, rebind) |
| Needs a crypto primitive | Depends on cryptographic primitives that must be spec-exact and constant-time | Identity & Cryptography (Ed25519, TLS 1.3 raw-public-key, BLAKE3 keyed_hash/derive_key), bao-tree & Blobs (BLAKE3), Relay (TLS keying-material export, RFC 5705) |
NOTE
The single largest reuse-a-sans-io-core item, noq-proto, is ≈48k lines of protocol logic — deterministic and portable, but the dominant effort of the whole port. Its own crate root flags it as of interest to anyone who wants "a different event loop than the one tokio provides" — precisely event-horizon's brief. See QUIC Transport § Mapping to event-horizon.
Milestones: the 0.x → 1.0 restructure
iroh 1.0 is not an increment; it deletes whole subsystems and QUIC-natively absorbs their jobs. The table below is the evolution the survey documents, subsystem by subsystem.
NOTE
The pinned trees carry no dated changelog of the 0.x release line, and this survey trusts only the pinned sources — so only the 1.0-era tag dates are absolute: noq noq-v1.0.1 released 2026-06-29 (340e9c7); iroh v1.0.1 committed 2026-07-03 (22cac742ca). The "iroh 0.x" column describes the prior architecture each deep-dive contrasts against, not a dated release; treat the ordering as logical, not chronological.
| Capability | iroh 0.x | iroh 1.0 | Surveyed in |
|---|---|---|---|
| NAT-traversal signalling | DISCO — a bespoke UDP ping/pong side-protocol using NaCl crypto_box (node key → Curve25519) | QUIC-native (QNT) — ADD_ADDRESS/REACH_OUT/REMOVE_ADDRESS frames on the encrypted 1-RTT dataplane | NAT Traversal, Identity |
| Reflexive-address discovery | STUN against relay servers | QUIC Address Discovery (/iroh-qad/0) on the main endpoint — no STUN code remains | Net Report, Relay |
| Connectivity module | magicsock (MagicSock) | socket (Socket, pub(crate)) | Socket |
| Path model | Duplicate each datagram onto UDP and relay; DISCO ping/pong picks a winner | Relay and every direct address are concurrent QUIC multipath paths on one connection; switching = flipping a path's PathStatus | Socket, QUIC |
| QUIC library | quinn | noq fork — adds Multipath, Address Discovery, in-QUIC NAT traversal | QUIC Transport |
| Identity vocabulary | NodeId / NodeAddr / NodeTicket | EndpointId / EndpointAddr / EndpointTicket — old node… tickets are unparseable | Identity, Wire |
| Address directory | discovery (Discovery / DiscoveryItem) | address_lookup (AddressLookup / Item); pkarr/DNS-centric | Address Lookup |
| Relay wire format | DERP-era bespoke framing / magic strings | Binary WebSocket subprotocol iroh-relay-v2 (v1 legacy); the Health frame is removed in v2 | Relay |
| Blobs store contract | A Store / Map trait hierarchy | "Whatever handles the Command enum" over irpc | Blobs |
| Blobs downloader | Dial queues, retry backoff, request dedup baked in | ~550-line re-planning orchestrator + a generic ConnectionPool; those concerns pushed to consumers | Blobs |
| Blobs transfer ALPN | /iroh-bytes/4 | /iroh-bytes/4 — unchanged across the rename (a deliberate interop-stability point) | Blobs |
| Gossip ALPN | /iroh-gossip/0 | /iroh-gossip/1, now per-instance overridable via Builder::alpn | Gossip |
Quick Navigation
Suggested reading paths
- "I want the connectivity story." QUIC Transport → Endpoint → Socket → NAT Traversal → Relay → Address Lookup → Net Report.
- "I want the identity & byte layer." Concepts → Identity & Cryptography → Wire Formats.
- "I want the data protocols." Blobs →
bao-tree→ Document Sync → Gossip. - "I want a minimal interop MVP." The smallest byte-compatible target is an echo/
dumbpipepeer — one ALPN, no store, no docs/gossip: Identity → Wire Formats → Endpoint → QUIC Transport → Socket → Relay → NAT Traversal. - "I'm designing the D port." Concurrency Inventory (what actually runs) → D Architecture Migration (the plan) → the four sans-io cores you can port directly (QUIC, Gossip, Document Sync,
bao-tree) → the event-horizon spec and the async-io tree — especially Tokio (the model being replaced) and Glommio / monoio (the thread-per-core, share-nothing stance event-horizon adopts).
Overview & synthesis
- Concepts & Vocabulary — the shared glossary:
EndpointId,EndpointAddr, tickets, ALPN, path, relay, the 0.x → 1.0 rename map every page relies on. - Tokio Concurrency Inventory — the exhaustive census of tasks, channels, locks,
select!loops, and blocking work, classified protocol-inherent vsSend + Syncartifact. - D Architecture Migration — the capstone: Rust constructs beside proposed D equivalents on event-horizon, the bottlenecks (CPU-bound BLAKE3 on the loop thread, the missing cross-fiber channel primitive), and the sans-io reuse strategy.
Cross-tree
- event-horizon spec — the D runtime the port targets: completion-first loop, fibers,
Scope/race/JoinHandlestructured concurrency, capability effects,IoResult!/Outcome!. - async-io survey — the prior-art runtime survey; Tokio, Glommio, and monoio are the directly-relevant deep-dives.
- algebraic-effects corpus — the effect-handler background behind event-horizon's capability layer.
Sources
Each deep-dive carries its own primary-source citations to exact path:lines in the pinned trees; this index's classifications derive from them. The pinned upstream artifacts are:
- iroh workspace (
iroh,iroh-base,iroh-tickets,iroh-relay,iroh-dns,iroh-dns-server) —n0-computer/irohat22cac742ca· docs.rs/iroh. - Transport —
n0-computer/noqatnoq-v1.0.1(noq,noq-proto,noq-udp) · docs.rs/noq; a fork ofquinn. - Data protocols —
iroh-blobs0.103.0 · docs.rs/iroh-blobs;bao-tree0.16.0 · docs.rs/bao-tree;iroh-docs0.101.0 · docs.rs/iroh-docs;iroh-gossip0.101.0 · docs.rs/iroh-gossip. - Runtime & RPC —
irpc0.17.0 · docs.rs/irpc;iroh-util0.6.0 (connection_pool);n0-future/n0-watcher. - Connectivity sensing —
n0-computer/net-tools(netwatch0.19.1,portmapper0.19.1). - Protocol drafts — QUIC Multipath (
draft-ietf-quic-multipath), QUIC NAT-traversal (draft-seemann-quic-nat-traversal), QUIC Address Discovery (draft-seemann-quic-address-discovery); QUIC v1 (RFC 9000), TLS 1.3 (RFC 8446); BLAKE3.