# Understanding the iroh Network Stack Architecture: A Layer-by-Layer Breakdown

> Explore the iroh network stack architecture a layer-by-layer breakdown Learn about its peer-to-peer networking using QUIC public-key dialing hole-punching and modular components.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: architecture
- Published: 2026-07-12

---

**The iroh network stack is a full-stack, peer-to-peer networking layer built on QUIC that provides public-key-based dialing, hole-punching, and relay fallback through modular components including transport abstraction, path selection, and pluggable discovery.**

The n0-computer/iroh repository implements a production-ready peer-to-peer networking library designed for robust connectivity across diverse network conditions. This article examines the iroh network stack architecture, detailing how its layered design—from public API to QUIC implementation—enables seamless NAT traversal and public-key-based endpoint identification.

## Architectural Layers of the iroh Network Stack

### Public API and Endpoint Construction

The user-facing entry point resides in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs), exposing the `Endpoint` and `Builder` types. The `Builder` struct (lines 22-48) allows configuration of ALPNs, transport stacks, and path selectors before calling `bind()` to initialize the stack. Presets such as `N0` provide sensible defaults for immediate connectivity, handling TLS configuration, transport initialization, and address lookup services automatically.

### Transport Abstraction Layer

Located in [`iroh/src/socket/transports.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports.rs), this layer defines the `Transport` trait and concrete implementations including `Ip`, `Relay`, and custom transports. It handles socket binding, network-change notifications, and path creation while remaining agnostic to the underlying wire protocol. This abstraction enables the stack to treat direct IP sockets, relay connections, and custom network implementations uniformly.

### QUIC Implementation via noq

The actual encrypted transport relies on the **noq** crate, configured in [`iroh/src/protocol.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/protocol.rs). This layer supplies `noq::Endpoint` and stream primitives, handling connection establishment, cryptography, and multiplexing without exposing implementation details to higher layers. The `QuicTransportConfig` struct bridges iroh's configuration with the underlying QUIC implementation.

### Remote State and Path Management

[`iroh/src/socket/remote_map.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map.rs) maintains per-remote state through `RemoteMap`, tracking all available network paths for each peer. The `BiasedRttPathSelector` in [`iroh/src/socket/biased_rtt_path_selector.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/biased_rtt_path_selector.rs) implements the default path-selection policy, preferring low-RTT IPv6 paths, falling back to IPv4, then to relays. This component maintains stickiness to prevent path flapping while continuously evaluating path health.

### Address Discovery and Resolution

The `address_lookup` module in [`iroh/src/address_lookup.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup.rs) resolves `EndpointId` values to reachable addresses. Built-in implementations include `PkarrResolver` for distributed DNS and `DnsResolver` for traditional lookups, allowing custom discovery mechanisms via the `AddressLookup` trait. This pluggable architecture supports resolution workflows ranging from centralized DNS to decentralized pkarr records.

### Relay Subsystem for NAT Traversal

When direct connections fail, the relay subsystem takes over. Defined in [`iroh-relay/src/relay_map.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/relay_map.rs) with client and server implementations in [`iroh-relay/src/client.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/client.rs) and [`iroh-relay/src/server.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server.rs), this component provides fallback connectivity through publicly accessible relay servers. The `RelayMap` tracks available relay endpoints, while the `Client` implementation handles the protocol details of proxying traffic through these servers.

### Supporting Infrastructure

Additional components include TLS configuration ([`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs)) generating `TlsConfig` and `CaTlsConfig` from permanent secret keys, port mapping via UPnP/PCP/NAT-PMP ([`iroh/src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/portmapper.rs)), continuous connectivity probing ([`iroh/src/net_report.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/net_report.rs)), and observability ([`iroh/src/metrics.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/metrics.rs) exposing `EndpointMetrics`).

## Data Flow Through the Stack

**Construction**: Users instantiate a `Builder`, configure transports and lookup services, then call `bind()` to create an `Endpoint` backed by `socket::EndpointInner` holding static configuration and a running QUIC endpoint.

**Discovery**: Upon calling `Endpoint::connect()`, the provided `EndpointAddr` passes through address-lookup services to resolve public keys into concrete IP addresses or relay URLs.

**Path Selection**: The `RemoteMap` registers all viable paths, and the `PathSelector` evaluates them using the biased RTT algorithm, selecting the optimal route.

**Connection**: The selected path's configuration and TLS credentials are passed to the `noq` endpoint to establish a QUIC `Connection` using the configured ALPN.

**Data Transport**: Bidirectional and unidirectional streams are multiplexed over QUIC, with the socket layer demultiplexing incoming packets to the appropriate connection handler.

**Adaptation**: Network-change events trigger path re-evaluation and health checks, with `NetReport` updating the endpoint's advertised capabilities and the `RemoteMap` potentially selecting alternative paths.

## Key Design Principles

- **Key-based addressing**: The `EndpointId` (public key) serves as the stable identifier, eliminating dependence on mutable IP addresses or DNS names.
- **Transport-agnostic core**: The `Transport` trait allows seamless integration of new network technologies beyond IP and relay without modifying higher-level code.
- **Fine-grained path control**: Multiple simultaneous paths per remote enable smart selection and seamless failover without connection interruption.
- **Pluggable discovery**: The `AddressLookup` interface supports custom resolution mechanisms beyond pkarr and DNS through a simple trait implementation.
- **Graceful fallback**: Automatic degradation from direct IPv6 → IPv4 → relay ensures connectivity in restrictive NAT environments.

## Implementation Examples

The following examples demonstrate common patterns for constructing and using the iroh network stack:

```rust
// 1️⃣ Create a default endpoint (preset “N0”) and bind it.
//    See Builder::new() → Builder::bind().
let ep = iroh::Endpoint::builder(iroh::endpoint::presets::N0)
    .alpns(vec![b"my-proto".to_vec()])   // ALPN that we will accept
    .bind()
    .await?;                             // → https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs#L24-L30

```

```rust
// 2️⃣ Connect to a remote endpoint identified by its public key.
//    `EndpointAddr::from_id` creates an address containing only the key.
let remote_id = iroh_base::EndpointId::from_hex("...")?;
let addr = iroh::EndpointAddr::from_id(remote_id);
let conn = ep.connect(addr, b"my-proto").await?; // → https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs#L50-L66

```

```rust
// 3️⃣ Open a bidirectional QUIC stream and exchange data.
//    Under the hood this uses `noq::Connection::open_bi`.
let (mut send, mut recv) = conn.open_bi().await?; // → https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs#L101-L106
send.write_all(b"hello").await?;
send.finish()?;
let mut buf = Vec::new();
recv.read_to_end(&mut buf).await?;
println!("got: {}", String::from_utf8_lossy(&buf));

```

```rust
// 4️⃣ Customize the transport stack – replace the default IPv4/IPv6 sockets
//    with a single relay transport.
let ep = iroh::Endpoint::builder(iroh::endpoint::presets::N0)
    .clear_ip_transports()                // remove IP transports
    .relay_mode(iroh::RelayMode::Default) // add the default relay transport
    .bind()
    .await?;                              // → https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs#L94-L106

```

```rust
// 5️⃣ Install a custom path selector (unstable feature) that always prefers a
//    user‑provided transport.
#[cfg(feature = "unstable-custom-transports")]
let selector = std::sync::Arc::new(my_custom_selector);
let ep = iroh::Endpoint::builder(iroh::endpoint::presets::N0)
    .path_selector(selector)               // → https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs#L138-L144
    .bind()
    .await?;

```

## Summary

- The iroh network stack architecture layers QUIC-based transport with modular components for path selection, discovery, and relay fallback.
- The `Endpoint` and `Builder` in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) provide the public API for constructing and configuring the stack.
- Transport abstraction in [`iroh/src/socket/transports.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports.rs) enables IP, relay, and custom transport implementations through a unified trait interface.
- Path selection via `BiasedRttPathSelector` optimizes connectivity by evaluating RTT across IPv6, IPv4, and relay paths while maintaining connection stability.
- Address lookup services in [`iroh/src/address_lookup.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup.rs) provide extensible endpoint discovery via pkarr and DNS implementations.
- The relay subsystem ensures connectivity when direct paths fail, implemented across [`iroh-relay/src/client.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/client.rs) and [`iroh-relay/src/server.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server.rs).

## Frequently Asked Questions

### What transport protocol does iroh use?

The iroh network stack uses QUIC as its primary transport protocol, implemented through the **noq** crate. This provides encrypted, multiplexed connections with built-in congestion control and stream isolation, enabling multiple simultaneous streams over a single connection without head-of-line blocking.

### How does iroh handle NAT traversal?

iroh implements multiple NAT traversal strategies including UPnP/PCP/NAT-PMP port mapping via the `Portmapper` component in [`iroh/src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/portmapper.rs), STUN-based address discovery through the `NetReport` system, and a relay fallback system (`iroh-relay`) that proxies traffic when direct connections fail. The stack automatically attempts direct connection first, then falls back to relay servers only when necessary.

### What is the role of the PathSelector in iroh?

The `PathSelector` determines which network path to use when multiple options exist (direct IPv6, IPv4, or relay). The default `BiasedRttPathSelector` in [`iroh/src/socket/biased_rtt_path_selector.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/biased_rtt_path_selector.rs) prioritizes low-latency paths while maintaining stickiness to prevent flapping between equivalent routes. This component can be customized at runtime for specific network requirements.

### How does iroh discover endpoint addresses?

Address discovery occurs through pluggable `AddressLookup` implementations in [`iroh/src/address_lookup.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup.rs). The library includes resolvers for pkarr (distributed DNS) and traditional DNS, while allowing custom discovery mechanisms for specific deployment scenarios. When `Endpoint::connect()` is called, the provided `EndpointId` is enriched by these services to produce reachable addresses.