How to Get Started with the iroh Project: Building Peer-to-Peer Applications in Rust

iroh is a Rust library that provides authenticated QUIC connections for peer-to-peer applications, handling automatic hole-punching, relay fallback, and TLS encryption through the high-level Endpoint API.

The n0-computer/iroh repository offers a complete toolkit for building decentralized applications without managing low-level networking complexity. Whether you need direct device-to-device communication or content-addressed storage, iroh abstracts NAT traversal and cryptographic setup into a simple, async-first interface centered on the Endpoint type.

Core Concepts of the iroh Architecture

Understanding how to get started with the iroh project requires familiarity with several key components defined in the source code.

The Endpoint Type

The Endpoint is the central abstraction defined in iroh/src/lib.rs. It represents a node that can both dial and accept connections, managing the underlying QUIC transport, secret keys, and relay configuration. You instantiate an endpoint using Endpoint::bind() with a preset configuration like presets::N0 or a custom builder pattern.

Relay Servers and NAT Traversal

When direct peer-to-peer connections fail due to NAT or firewalls, iroh falls back to relay servers. These publicly reachable middle-men are implemented in iroh-relay/src/server/http_server.rs and forward encrypted traffic without decrypting it. The public "number 0" relay fleet is available for development, or you can run your own infrastructure.

Address Lookup via DNS

iroh uses DNS with Pkarr (DNSSEC-protected public key-based addressing) for peer discovery. The iroh/src/address_lookup.rs module handles resolving EndpointId values to direct addresses and RelayUrl endpoints. This allows peers to find each other without centralized registries using the EndpointId, PublicKey, and SecretKey types defined in iroh-base/src/lib.rs.

Setting Up Your First iroh Project

Follow these steps to create a working iroh application from scratch.

  1. Install Rust using the official installer at rust-lang.org if you haven't already configured your toolchain.

  2. Add the iroh crate to your Cargo.toml:

    [dependencies]
    iroh = "0.12"
    tokio = { version = "1", features = ["full"] }
  3. Create a basic endpoint in src/main.rs:

    use iroh::{Endpoint, endpoint::presets};
    use n0_error::Result;
    
    #[tokio::main]
    async fn main() -> Result<()> {
        // Bind using the N0 preset (public relay fleet)
        let ep = Endpoint::bind(presets::N0).await?;
        println!("Node ID: {:?}", ep.node_id());
        Ok(())
    }
  4. Run your node:

    cargo run --release

The node will automatically register with the default relay fleet and print its unique identifier.

Building an Echo Server and Client

To get started with the iroh project effectively, implement both connection initiation and acceptance patterns.

Echo Server Implementation

The server uses the Router pattern to accept incoming connections on a specific Application-Layer Protocol Negotiation (ALPN) identifier:

use iroh::{Endpoint, endpoint::presets, ProtocolHandler, Connection};
use n0_error::Result;
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<()> {
    let ep = Endpoint::bind(presets::N0).await?;
    const ALPN: &[u8] = b"iroh-example/echo/0";
    
    let router = iroh::Router::builder(ep)
        .accept(ALPN.to_vec(), Arc::new(Echo))
        .spawn()
        .await?;
    
    router.wait_until_shutdown().await;
    Ok(())
}

struct Echo;

#[iroh::async_trait]
impl ProtocolHandler for Echo {
    async fn accept(&self, conn: Connection) -> Result<()> {
        let (mut send, mut recv) = conn.accept_bi().await?;
        tokio::io::copy(&mut recv, &mut send).await?;
        send.finish()?;
        Ok(())
    }
}

Client Implementation

The client connects to a known peer address and opens a bidirectional stream:

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

#[tokio::main]
async fn main() -> Result<()> {
    let ep = Endpoint::bind(presets::N0).await?;
    let alpn = b"iroh-example/echo/0";
    
    // Replace with actual peer address
    let addr = "iroh://<peer-id>@<relay-url>".parse()?;
    let conn = ep.connect(addr, alpn).await?;
    let (mut send, mut recv) = conn.open_bi().await?;
    
    send.write_all(b"Hello, world!").await?;
    send.finish()?;
    
    let resp = recv.read_to_end(1024).await?;
    println!("Echo: {:?}", String::from_utf8_lossy(&resp));
    Ok(())
}

Exploring Built-in Protocols

The iroh ecosystem includes higher-level protocols for common use cases that extend the core Endpoint functionality.

Content-Addressed Storage with iroh-blobs

The iroh-blobs crate provides BLAKE3-based content addressing. According to the source structure in the iroh-blobs/ directory, you can integrate persistent storage as follows:

use iroh_blobs::StoreBuilder;
use std::path::PathBuf;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let store = StoreBuilder::default()
        .root(PathBuf::from("./blobdir"))
        .build()
        .await?;
    
    let hash = store.put_path("./Cargo.toml").await?;
    println!("Stored file with hash: {}", hash);
    
    let data = store.get_bytes(&hash).await?;
    println!("Retrieved {} bytes", data.len());
    Ok(())
}

Publish-Subscribe with iroh-gossip

For multi-peer broadcast scenarios, the iroh-gossip crate implements a publish-subscribe overlay network. Add it to your dependencies alongside the main iroh crate to enable decentralized messaging patterns without central servers.

Running a Custom Relay Server

If you prefer not to rely on the public relay fleet, deploy your own relay using the implementation in iroh-relay/src/server/http_server.rs:

git clone https://github.com/n0-computer/iroh.git
cd iroh/iroh-relay
cargo run --release -- --listen 0.0.0.0:443

This starts a relay that accepts encrypted iroh traffic on port 443. For production deployment, configure TLS certificates and authentication as documented in the iroh-relay README.

Summary

  • iroh provides peer-to-peer QUIC connections with automatic NAT traversal via the Endpoint API defined in iroh/src/lib.rs.
  • The Router pattern simplifies accepting incoming connections using the ProtocolHandler trait and ALPN identifiers.
  • Relay servers (iroh-relay/src/server/http_server.rs) ensure connectivity when direct paths fail, maintaining end-to-end encryption without decrypting forwarded traffic.
  • Address lookup uses DNS/Pkarr resolution defined in iroh/src/address_lookup.rs for decentralized peer discovery using EndpointId and RelayUrl types from iroh-base/src/lib.rs.
  • Built-in crates like iroh-blobs and iroh-gossip extend the core with content-addressed storage and publish-subscribe messaging capabilities.

Frequently Asked Questions

What is iroh and when should I use it?

iroh is a Rust networking library for building peer-to-peer applications that require direct, encrypted communication between devices. Use it when you need features like hole-punching, automatic relay fallback, and content-addressed storage without managing the low-level complexities of QUIC, TLS, or NAT traversal yourself.

How does iroh handle NAT traversal and hole punching?

iroh attempts direct QUIC connections first. When direct paths fail, it uses the relay protocol implemented in iroh-relay/src/server/http_server.rs to forward encrypted traffic. The Endpoint type manages this automatically, switching between direct and relayed paths based on network conditions while maintaining the same cryptographic guarantees.

What is the difference between iroh and libp2p?

While both enable peer-to-peer networking, iroh focuses specifically on authenticated QUIC connections with a smaller, more focused API surface. iroh uses the Endpoint as the primary abstraction in iroh/src/lib.rs, whereas libp2p uses a more modular transport-swarm approach. iroh also integrates DNS/Pkarr address lookup directly in iroh/src/address_lookup.rs, providing a batteries-included approach to peer discovery.

How do I deploy a production iroh relay server?

Deploy the relay binary from the iroh-relay crate with TLS certificates and a stable domain name. The server listens for HTTP/QUIC traffic as implemented in iroh-relay/src/server/http_server.rs and forwards encrypted packets to destination nodes. Configure multiple relay instances for redundancy, and ensure your clients specify your custom relay URLs when binding endpoints instead of using presets::N0.

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 →