Key Modules in the iroh Codebase: A Guide to the P2P Networking Stack

The iroh codebase organizes its functionality into six distinct Rust crates—iroh, iroh-base, iroh-relay, iroh-dns, iroh-dns-server, and iroh-bench—that collectively provide a complete peer-to-peer networking stack built on QUIC.

The iroh project is structured as a Rust workspace where each crate exposes a focused set of responsibilities. Understanding these key modules is essential for developers integrating peer-to-peer capabilities, as the separation allows for selective dependency inclusion and clear API boundaries. The architecture handles everything from cryptographic identity and address lookup to relay-assisted hole-punching and performance benchmarking.

The Six Core Modules

The repository at n0-computer/iroh divides its functionality into the following workspace members, each defined in the root Cargo.toml:

Module Purpose Primary Source
iroh Supplies the public API (Endpoint, RelayMode) and orchestrates high‑level P2P workflows including connection establishment, stream handling, and relay usage. iroh/src/lib.rs
iroh-base Defines core identity and addressing types (EndpointId, PublicKey, SecretKey, RelayUrl, EndpointAddr, TransportAddr) shared across all crates. iroh-base/src/lib.rs
iroh-relay Implements the DERP‑style relay protocol with both server and client libraries, plus associated TLS utilities for NAT traversal. iroh-relay/src/lib.rs
iroh-dns Handles DNS‑based endpoint discovery using the PKARR packet format, publishing and resolving relay and direct addresses as DNS TXT records. iroh-dns/src/lib.rs
iroh-dns-server Provides a minimal in‑memory DNS server for storing endpoint records, useful for local testing or private deployments. iroh-dns-server/src/lib.rs
iroh-bench Contains benchmarking utilities for throughput and latency tests that exercise the QUIC stack, relay protocol, and address‑lookup service. iroh/bench/src/lib.rs

Module Architecture and Data Flow

The crates interact through a specific sequence to establish peer-to-peer connections. This architecture ensures that cryptographic identity is centralized while networking concerns remain modular.

  1. Endpoint Creation – Applications construct an Endpoint via the iroh crate. This struct internally holds a SecretKey from iroh-base that identifies the node and secures TLS handshakes.

  2. Address Lookup – When connecting to a peer, the endpoint queries DnsAddressLookup from iroh-dns to retrieve the peer’s RelayUrl and direct TransportAddrs, using the peer’s EndpointId as the lookup key.

  3. Relay Interaction – If direct paths fail, the endpoint contacts a relay server via iroh-relay client utilities. The relay forwards encrypted packets until hole‑punching establishes a direct connection.

  4. Connection Management – After QUIC handshake completion, the Endpoint exposes Connection objects supporting uni‑directional and bi‑directional streams. These streams are cheap, multiplexed, and created on‑the‑fly.

  5. Metrics and Diagnostics – The iroh crate re‑exports metrics and runtime reporting utilities, allowing monitoring of connection health and relay latency.

  6. Testing – The iroh-bench crate validates performance at each layer, ensuring relay throughput and DNS resolution meet requirements.

Practical Implementation Examples

Creating Endpoints with the iroh Crate

The primary entry point for applications is the Endpoint struct, which coordinates keys, relays, and connections. Below, the endpoint uses the default "number 0" relay preset and connects to a peer using internally managed iroh-relay and iroh-base types:

use iroh::{Endpoint, endpoint::presets};
use n0_error::Result;

#[tokio::main]
async fn main() -> Result<()> {
    // The Endpoint holds its own secret key (iroh-base) and performs TLS handshakes.
    let ep = Endpoint::bind(presets::N0).await?;        // ← iroh::Endpoint
    // Obtain a peer’s address (could be from DNS or a config file).
    let peer_addr = "example-endpoint-id".parse()?;    // ← iroh-base::EndpointId
    // Connect via the relay (iroh-relay client is used internally).
    let conn = ep.connect(peer_addr, b"my-alpn").await?; // ← iroh::Endpoint::connect
    // Open a bi-directional stream and exchange data.
    let (mut send, mut recv) = conn.open_bi().await?;
    send.write_all(b"hello").await?;
    send.finish().await?;
    let mut buf = vec![];
    recv.read_to_end(&mut buf).await?;
    println!("Received: {:?}", String::from_utf8_lossy(&buf));
    // Gracefully shut down.
    conn.close(0u8.into(), b"done");
    ep.close().await;
    Ok(())
}

DNS-Based Discovery with iroh-dns

For scenarios requiring explicit address resolution, the iroh-dns crate exposes DnsAddressLookup to resolve EndpointIds to connection parameters:

use iroh::address_lookup::DnsAddressLookup;
use iroh::EndpointId;

#[tokio::main]
async fn lookup_example() -> Result<()> {
    let lookup = DnsAddressLookup::default();                 // ← iroh-dns
    let endpoint_id = EndpointId::from_hex("deadbeef…")?;
    let address = lookup.lookup(endpoint_id).await?;          // Returns RelayUrl + direct addresses
    println!("Discovered relay: {}", address.relay_url);
    Ok(())
}

Running a Custom Relay Server

The iroh-relay crate provides server capabilities for hosting private relay infrastructure. This is the binary implementation used when running iroh-relay:

use iroh_relay::server::RelayServer;
use std::net::SocketAddr;

#[tokio::main]
async fn start_relay() -> Result<()> {
    let bind_addr: SocketAddr = "0.0.0.0:4000".parse()?;
    let server = RelayServer::bind(bind_addr).await?;
    println!("Relay listening on {}", bind_addr);
    server.run().await?;
    Ok(())
}

Entry Points and Source Files

When navigating the repository, these files represent the primary entry points for each module:

  • iroh/src/lib.rs – Defines the Endpoint struct, connection handling logic, and re‑exports core types for the public API.
  • iroh-base/src/lib.rs – Central definitions for cryptographic primitives (SecretKey, PublicKey) and addressing types (EndpointId, RelayUrl).
  • iroh-relay/src/lib.rs – Implements the RelayServer and client components for the DERP protocol.
  • iroh-dns/src/lib.rs – Provides DnsAddressLookup and PKARR packet handling for decentralized discovery.
  • iroh-dns-server/src/lib.rs – Minimal DNS server implementation for local endpoint publishing.
  • iroh/bench/src/lib.rs – Performance harnesses for validating stream throughput and relay latency.
  • Cargo.toml (root) – Coordinates workspace members and ensures feature flags propagate consistently across the networking stack.

Summary

  • The iroh crate provides the high‑level Endpoint API that orchestrates connections, while iroh-base supplies the underlying cryptographic identities and address types.
  • iroh-relay implements the DERP-style relay protocol for NAT traversal, available as both client library and server binary.
  • iroh-dns enables PKARR-based discovery through DNS TXT records, complemented by iroh-dns-server for private testing environments.
  • iroh-bench supplies performance testing utilities to validate the QUIC stack and relay throughput.
  • All modules share a unified versioning scheme and feature flag system defined in the workspace root, ensuring consistent API compatibility across the peer-to-peer networking stack.

Frequently Asked Questions

What is the difference between the iroh and iroh-base crates?

The iroh crate contains the high‑level public API, including the Endpoint struct and connection management logic, while iroh-base defines the low‑level primitives such as SecretKey, PublicKey, and EndpointId that are shared across all workspace members. Applications typically depend on iroh, which re‑exports necessary types from iroh-base internally.

When should I use iroh-dns versus iroh-dns-server?

Use iroh-dns when your application needs to resolve or publish endpoint addresses using the PKARR protocol against existing DNS infrastructure. Use iroh-dns-server when you need to run a standalone, in‑memory DNS server for local development, testing, or private networks that do not rely on public DNS resolvers.

How does iroh-relay integrate with the main iroh crate?

The iroh crate internally uses iroh-relay client utilities when an Endpoint cannot establish a direct connection to a peer. When you call Endpoint::connect, the library automatically contacts relay servers (specified via RelayUrl from address lookup) to forward encrypted packets until hole‑punching succeeds, after which traffic migrates to direct paths. This integration is transparent to the application developer.

Why are the modules organized as separate crates rather than a single library?

The modular architecture allows developers to depend only on the functionality they need—such as importing iroh-base for cryptographic types without pulling in relay or DNS logic. This separation also enforces clean API boundaries, enables independent versioning of stable components, and facilitates targeted testing and benchmarking via iroh-bench without affecting the core library.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →