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

> Start iroh development in Rust by adding the iroh crate, binding an Endpoint, and implementing ProtocolHandler for authenticated QUIC connections. Build P2P apps easily.

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

---

**To get started with iroh development, add the `iroh` crate to your Rust project, bind an `Endpoint` using the `presets::N0` configuration, and implement the `ProtocolHandler` trait to handle authenticated QUIC connections.**

iroh is a Rust library maintained by n0-computer that enables developers to build peer-to-peer applications using hole-punched, encrypted QUIC connections. It abstracts complex networking concerns like NAT traversal, fallback relays, and address lookup through a high-level API centered on the `Endpoint` type. This guide covers the essential steps to get started with iroh development, from initial setup to running your first node.

## Prerequisites and Installation

Ensure you have Rust installed via the official installer. 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"] }

```

## Creating Your First iroh Application

The `Endpoint` struct defined in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs) serves as the primary interface for both dialing outbound connections and accepting inbound peers.

### Binding an Endpoint

Use `Endpoint::bind()` with the `N0` preset to automatically configure your node for the public "number 0" relay fleet:

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

#[tokio::main]
async fn main() -> Result<()> {
    let ep = Endpoint::bind(presets::N0).await?;
    println!("Node ID: {}", ep.node_id());
    Ok(())
}

```

This configuration handles TLS encryption, address registration via DNS/Pkarr (as implemented in [`iroh-dns-server/src/main.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/main.rs)), and automatic relay fallback when direct paths fail.

### Implementing a Protocol Handler

Create a struct that implements `ProtocolHandler` and define the `accept` method to handle incoming connections. The following echo server implementation is defined in [`src/main.rs`](https://github.com/n0-computer/iroh/blob/main/src/main.rs):

```rust
use iroh::{ProtocolHandler, Connection};
use n0_error::Result;

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?;
        // Echo the inbound bytes back to the sender
        tokio::io::copy(&mut recv, &mut send).await?;
        send.finish()?;
        Ok(())
    }
}

```

### Building the Router

Use `Router::builder()` to register your protocol handler with an ALPN identifier:

```rust
use std::sync::Arc;
use n0_error::Result;

#[tokio::main]
async fn main() -> Result<()> {
    let ep = Endpoint::bind(presets::N0).await?;
    
    let router = iroh::Router::builder(ep)
        .accept(b"iroh-demo/1".to_vec(), Arc::new(Echo))
        .spawn()
        .await?;
    
    // Keep the process alive
    router.wait_until_shutdown().await;
    Ok(())
}

```

### Connecting to Peers

Clients use `Endpoint::connect()` with an `EndpointAddr` and matching ALPN identifier:

```rust
use n0_error::Result;

#[tokio::main]
async fn main() -> Result<()> {
    let ep = Endpoint::bind(presets::N0).await?;
    let alpn = b"iroh-demo/1";
    
    // Parse an address in the format: iroh://<peer-id>@<relay-url>
    let addr = "iroh://example-id@relay.iroh.link".parse()?;
    let conn = ep.connect(addr, alpn).await?;
    
    let (mut send, mut recv) = conn.open_bi().await?;
    send.write_all(b"ping").await?;
    send.finish()?;
    
    let resp = recv.read_to_end(256).await?;
    println!("Received: {:?}", resp);
    Ok(())
}

```

## Core Architecture and Source Files

Understanding the codebase structure helps when debugging or extending functionality.

### Key Components

- **Endpoint**: Defined in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs), manages node identity, connection state, and the authentication layer using `SecretKey` and `PublicKey` types.
- **Relay Server**: Implemented in [`iroh-relay/src/server/http_server.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server/http_server.rs), forwards encrypted traffic when direct peer-to-peer paths are blocked by NAT or firewalls.
- **Address Lookup**: Located in [`iroh/src/address_lookup.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup.rs), resolves `RelayUrl` and direct addresses via DNS/Pkarr.
- **Base Types**: Found in [`iroh-base/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/lib.rs), includes `EndpointId`, `PublicKey`, `SecretKey`, and `RelayUrl` used throughout the ecosystem.

## Running a Custom Relay Server

To host your own relay instead of using the public fleet, clone the repository and execute the relay server binary:

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

```

The server defined in [`iroh-relay/src/server/http_server.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server/http_server.rs) accepts encrypted iroh traffic on port 443 and forwards it to destination nodes based on their `EndpointId`. See the iroh-relay README for advanced configuration options including TLS certificates and authentication.

## Working with Built-in Protocols

### Content-Addressed Storage with iroh-blobs

Add `iroh-blobs` to your dependencies for BLAKE3-based content addressing:

```rust
use iroh_blobs::StoreBuilder;
use std::path::PathBuf;
use anyhow::Result;

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

```

The `iroh-blobs` crate provides content-addressed storage where data is identified by its BLAKE3 hash, enabling efficient deduplication and verification.

## Summary

- **Install** the `iroh` crate from crates.io to begin development
- **Bind an Endpoint** using `presets::N0` in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs) for automatic relay configuration and NAT traversal
- **Implement ProtocolHandler** to define how your node responds to incoming ALPN-identified connections
- **Use the Router** to register multiple protocol handlers and manage the application lifecycle with `wait_until_shutdown()`
- **Reference source files** including [`iroh-relay/src/server/http_server.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server/http_server.rs) for relay functionality and [`iroh-base/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/lib.rs) for cryptographic types
- **Run a custom relay** by executing the binary in the `iroh-relay` directory with your desired listen address

## Frequently Asked Questions

### What is the difference between an Endpoint and a Relay in iroh?

An **Endpoint** represents your local node in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs), managing both outgoing connections via `connect()` and incoming listeners. A **Relay** is a publicly reachable server defined in [`iroh-relay/src/server/http_server.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server/http_server.rs) that forwards encrypted traffic when direct peer-to-peer paths are blocked by firewalls or NAT. Endpoints automatically use relays as fallback when direct connections fail, with address lookup handled by the DNS service in [`iroh-dns-server/src/main.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/main.rs).

### How do I configure TLS certificates for a custom relay server?

The relay server in [`iroh-relay/src/server/http_server.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server/http_server.rs) accepts command-line arguments for TLS configuration. When running the server binary with `cargo run --release`, specify certificate paths and private keys using the appropriate flags. The server handles TLS termination for encrypted iroh traffic before forwarding it to destination nodes based on their `EndpointId`.

### What is the purpose of the ALPN identifier in iroh connections?

The **Application-Layer Protocol Negotiation (ALPN)** identifier is a byte string that both client and server must agree upon before establishing a connection. It functions similarly to port numbers in TCP, allowing a single `Endpoint` to host multiple protocols simultaneously. The `Router` defined in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs) uses ALPN values to dispatch incoming connections to the correct `ProtocolHandler` implementation via the `accept()` method.

### Where are the base types like EndpointId and SecretKey defined?

Core identifiers including `EndpointId`, `PublicKey`, `SecretKey`, and `RelayUrl` are defined in [`iroh-base/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/lib.rs). These types provide the cryptographic foundation for node identity and address resolution throughout the iroh ecosystem, and are re-exported through the main `iroh` crate for convenience.