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

> Start building peer-to-peer applications in Rust with iroh. This library simplifies P2P development with authenticated QUIC, hole-punching, relay fallback, and TLS encryption via the Endpoint API.

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

---

**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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/Cargo.toml):

   ```toml
   [dependencies]
   iroh = "0.12"
   tokio = { version = "1", features = ["full"] }
   ```

3. **Create a basic endpoint** in [`src/main.rs`](https://github.com/n0-computer/iroh/blob/main/src/main.rs):

   ```rust
   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**:

   ```bash
   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:

```rust
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:

```rust
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:

```rust
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`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server/http_server.rs):

```bash
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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup.rs) for decentralized peer discovery using `EndpointId` and `RelayUrl` types from [`iroh-base/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`.