# How to Integrate iroh with Other Applications: Endpoint and Builder API Guide

> Integrate iroh into your Rust app using the Endpoint and Builder API. Handle identity, NAT traversal, and QUIC connections easily. Dispatch streams with the Router.

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

---

**Integrate iroh into any Rust application by creating an `Endpoint` through the `Builder` API, which handles cryptographic identity, NAT traversal, and QUIC connections, then use the `Router` to dispatch incoming streams by ALPN protocol.**

The n0-computer/iroh repository provides a Rust library that embeds a complete peer-to-peer networking stack into your program. To integrate iroh with other applications, you instantiate an **`Endpoint`**—the core abstraction that manages cryptographic keys, relay connections, and address publication—then bind it to handle inbound and outbound QUIC streams. This architecture allows any existing codebase to gain direct connectivity without managing low-level socket operations or NAT traversal logic manually.

## Understanding the iroh Integration Architecture

The integration surface centers on a few high-level types that encapsulate the complexity of QUIC networking.

### The Endpoint as the Core Gateway

The **`Endpoint`** struct, defined in [[`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs), serves as the public API for creating connections. It owns a cryptographic identity, handles NAT traversal automatically, communicates with relay servers when direct paths are unavailable, and exposes methods for dialing and accepting connections. When you bind an endpoint, it spawns the underlying QUIC server, contacts configured relays, and publishes your address to the chosen lookup services.

### Builder Pattern for Configuration

Configuration occurs through the **`Builder`**, implemented in the same file starting around line 22. The builder accepts settings for ALPN protocols, relay modes, address-lookup services (such as Pkarr DNS), and custom transport layers. Key methods include `alpns()`, `relay_mode()`, and `address_lookup()`. Calling `bind().await` on the builder finalizes the configuration and returns a ready-to-use `Endpoint`.

### Router for Protocol Dispatch

The optional **`Router`** utility, created via `Router::builder(endpoint.clone())`, dispatches inbound streams to protocol handlers based on the ALPN identifier. This decouples connection management from application logic, allowing you to register multiple protocol handlers on a single endpoint.

## Step-by-Step Integration Workflow

Follow this sequence to embed iroh into your existing application:

1. **Add the dependency** to your [`Cargo.toml`](https://github.com/n0-computer/iroh/blob/main/Cargo.toml) (`iroh = "…"`).
2. **Create a builder** using a preset like `presets::N0` for sensible defaults.
3. **Configure** ALPN protocols, relay mode, and address-lookup services.
4. **Bind the endpoint** with `builder.bind().await` to initialize the networking stack.
5. **Accept** inbound connections manually via `endpoint.accept()` or automatically via a `Router`.
6. **Dial** remote endpoints using `endpoint.connect()` or `connect_with_opts()` for advanced scenarios.

All networking is end-to-end encrypted, requiring no external trust anchors beyond optional CA TLS configuration for relay or DNS services.

## Code Examples for iroh Integration

### Basic Echo Server and Client

This example demonstrates a complete server that accepts connections and a client that dials it, using the `Router` to handle protocol dispatch.

```rust
use iroh::{
    Endpoint,
    endpoint::{presets, Connection, Router},
    protocol::{AcceptError, ProtocolHandler},
};

const ALPN: &[u8] = b"iroh-example/echo/0";

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // ---------- Server side ----------
    let server_ep = Endpoint::builder(presets::N0)
        .alpns(vec![ALPN.to_vec()])      // Register the ALPN we will accept
        .bind()
        .await?;                         // Bind (creates sockets, contacts relay)

    let router = Router::builder(server_ep.clone())
        .accept(ALPN, Echo)               // Route incoming streams to the handler
        .spawn();

    // Make the server reachable (optional, for NAT traversal)
    server_ep.online().await;

    // ---------- Client side ----------
    let client_ep = Endpoint::builder(presets::N0).bind().await?;
    let remote_addr = server_ep.addr();   // Publishable address of the server

    // Dial the server using the same ALPN
    let conn = client_ep.connect(remote_addr, ALPN).await?;
    let (mut send, mut recv) = conn.open_bi().await?;
    send.write_all(b"Hello, iroh!").await?;
    send.finish()?;
    let reply = recv.read_to_end(1024).await?;
    assert_eq!(&reply, b"Hello, iroh!");

    // Clean shutdown
    router.shutdown().await?;
    client_ep.close().await;
    server_ep.close().await;
    Ok(())
}

// Simple echo protocol handler
#[derive(Clone)]
struct Echo;
impl ProtocolHandler for Echo {
    async fn accept(&self, conn: Connection) -> Result<(), AcceptError> {
        let (mut send, mut recv) = conn.accept_bi().await?;
        tokio::io::copy(&mut recv, &mut send).await?;
        send.finish()?;
        Ok(())
    }
}

```

**Key points**: `presets::N0` pre-configures the Pkarr DNS lookup and a default relay set. The `alpns` method registers the protocol identifier, which must match on both sides. The `Router::builder(...).accept(ALPN, Echo)` call automatically spawns a task that invokes `accept` on each incoming stream.

### Configuring Custom Relays and Addresses

To integrate with a private relay infrastructure or advertise specific addresses, use `RelayMode::Custom` and `add_external_addr`:

```rust
use iroh::{
    Endpoint,
    endpoint::{presets, RelayMode},
    iroh_relay::RelayConfig,
    iroh_base::RelayUrl,
};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Build a custom relay map (replace with real URLs)
    let custom_relay = RelayUrl::parse("https://my-relay.example.com/")?;
    let relay_cfg = RelayConfig::default(); // configure auth, etc. if needed

    let ep = Endpoint::builder(presets::N0)
        .relay_mode(RelayMode::Custom(vec![(custom_relay.clone(), relay_cfg)]))
        .bind()
        .await?;

    // Optionally add a directly reachable address
    ep.add_external_addr("203.0.113.42:4000".parse()?).await;

    // Use the endpoint as usual
    // …
    Ok(())
}

```

`RelayMode::Custom` overrides the default relay set, directing the endpoint to your own relay cluster defined in [`iroh-base/src/relay_url.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs).

### Implementing Custom Transport Layers

For advanced integration requiring custom packet transport, enable the unstable feature and implement the `CustomTransport` trait:

```toml

# Cargo.toml

iroh = { version = "…", features = ["unstable-custom-transports"] }

```

```rust
use std::{sync::Arc, net::SocketAddr};
use iroh::{
    Endpoint,
    endpoint::{presets, transports::CustomTransport},
    socket::transports::TransportConfig,
};

struct MyTransport;
impl CustomTransport for MyTransport {
    // Implement required methods (see iroh/src/socket/transports/custom.rs)
    // – this is an advanced use-case.
}
let custom = Arc::new(MyTransport);

let ep = Endpoint::builder(presets::N0)
    .add_custom_transport(custom)               // Register the transport
    .bind()
    .await?;

```

This API is gated behind the `unstable-custom-transports` feature and allows you to replace or supplement the default UDP socket implementation.

## Key Source Files in the n0-computer/iroh Repository

When integrating iroh, reference these specific source locations to understand the implementation details:

- **[`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs)** – Contains the `Endpoint` and `Builder` definitions, connection logic, and address handling.
- **[`iroh-base/src/relay_url.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs)** – Defines `RelayUrl` and `RelayMap` types used for relay configuration.
- **[`iroh/src/address_lookup.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup.rs)** – Implements the `AddressLookup` trait and default services (Pkarr, DNS) for publishing and discovering addresses.
- **[`iroh/src/socket.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket.rs)** – Manages low-level socket handling, transport configuration, and QUIC integration.
- **[`iroh/src/presets.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/presets.rs)** – Provides predefined configuration bundles like `presets::N0`.
- **[`iroh-relay/src/server.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server.rs)** – Reference implementation for running your own relay server.
- **[`iroh/examples/echo.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/echo.rs)** – Full-featured echo example demonstrating best practices.

## Summary

- The **`Endpoint`** is the primary interface for iroh integration, defined in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs).
- Use the **`Builder`** pattern to configure ALPN protocols, relay modes, and address lookup services before binding.
- The **`Router`** automates inbound stream dispatch based on ALPN identifiers, decoupling protocol logic from connection management.
- All connections are end-to-end encrypted using the endpoint's cryptographic identity, with optional trust anchors for external services.
- Custom relays can be injected via `RelayMode::Custom`, and custom transports can be implemented via the unstable `CustomTransport` trait.

## Frequently Asked Questions

### What is the minimum code needed to integrate iroh into an existing Rust application?

At minimum, create an `Endpoint` using `Endpoint::builder(presets::N0).bind().await`, then use `endpoint.connect()` to dial peers or `endpoint.accept()` to receive connections. This single call establishes the QUIC-based P2P stack with default relay and DNS configuration, allowing immediate peer-to-peer communication.

### How does iroh handle NAT traversal when integrating with external applications?

The `Endpoint` automatically manages NAT traversal through relay servers configured via `RelayMode`. When direct UDP connectivity fails, traffic routes through relays defined in `RelayMap` (see [`iroh-base/src/relay_url.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs)), while `address_lookup` services publish public addresses for discovery according to the implementation in [`iroh/src/address_lookup.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup.rs).

### Can I use iroh with custom application protocols?

Yes, register your protocol identifier (ALPN) with `Builder::alpns()` and handle streams via a `Router` or manual `accept` calls on the `Connection`. For transport-level customization, implement the `CustomTransport` trait (unstable feature) as defined in the socket transport modules, allowing you to integrate iroh with specialized network hardware or simulation environments.

### Where is the Connection struct defined and how do I use it for stream handling?

`Connection` is defined in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) and represents a QUIC connection to a remote peer. Obtain it via `Endpoint::connect()` or `Endpoint::accept()`, then open bidirectional or unidirectional streams using methods like `open_bi()` and `open_uni()`. These streams provide standard async I/O traits for reading and writing data.