iroh Endpoint Builder Pattern and Configuration Options: A Complete Guide

The iroh::endpoint::Builder implements a chainable builder pattern that collects optional network, identity, and transport settings and finalizes them into a live Endpoint through a single bind call.

The iroh crate in the n0-computer/iroh repository exposes its core networking primitive via the Endpoint type, configured through the builder API defined in iroh/src/endpoint.rs. Understanding the iroh Endpoint builder pattern and its configuration options is essential for customizing peer-to-peer connections, whether you need to tweak socket binding, swap crypto providers, or disable relay fallback entirely.

How the iroh Endpoint Builder Pattern Works

An Endpoint requires a large amount of runtime information, including crypto providers, transport configuration, and relay settings. The iroh::endpoint::Builder holds a partial configuration and exposes a chainable API so you can override only the values you care about.

You create the builder through Endpoint::builder(preset), passing a preset such as presets::N0 or presets::Empty. Each configuration method takes &mut self and returns Self, enabling fluent chaining. When you are ready, Builder::bind validates the collected state—for example, ensuring a crypto provider is present—and then constructs the actual networking resources.

Core Endpoint Builder Configuration Options

As implemented in iroh/src/endpoint.rs, the builder surfaces methods across several functional categories. Below are the key chainable methods and what they control.

Preset Initialization

  • new(preset), preset(preset), empty() — Initialize the builder with a bundled configuration or start from a clean slate. For example, presets::N0 selects a default crypto provider, relay map, and DNS/PKARR address-lookup services, while presets::Empty leaves every option unset.

Network Transports and Socket Binding

  • bind_addr and bind_addr_with_opts — Bind the endpoint to specific IP sockets. bind_addr_with_opts accepts a BindOpts struct for advanced control such as prefix length.
  • clear_ip_transports and clear_relay_transports — Remove the built-in IPv4/IPv6 or relay transports so you can define custom behavior.

The underlying transport primitives are defined in iroh/src/socket/transports.rs, and socket allocation is handled by iroh/src/socket/bind.rs.

Identity and Application Protocols

  • secret_key — Set the secret key that determines the endpoint’s EndpointId. If omitted, bind generates one automatically.
  • alpns — Provide a list of ALPN identifiers the endpoint will accept during connection negotiation.

Relay Configuration and Address Discovery

  • relay_mode — Choose a relay strategy: Default, Staging, Custom, or Disabled. This controls whether and how the endpoint uses relay servers for NAT traversal.
  • address_lookup, clear_address_lookup, addr_filter, clear_addr_filter, user_data_for_address_lookup — Register or remove services that publish and discover the endpoint’s addresses, such as PKARR and DNS. Trait definitions for these services live in iroh/src/address_lookup/mod.rs.
  • external_addr — Manually advertise additional publicly reachable socket addresses beyond those discovered automatically.

Transport Tuning, DNS, and Proxy Settings

  • transport_config — Override the default QuicTransportConfig to adjust max streams, flow-control windows, and other QUIC parameters.
  • dns_resolver (non-WASM targets only) — Supply a custom DNS resolver for relay hostnames and address lookups.
  • proxy_url and proxy_from_env — Force all HTTP(S) traffic, including relay communication, through a proxy server.
  • ca_tls_config — Set trusted CA certificates for TLS connections to relays or DoH resolvers.
  • keylog and max_tls_tickets — Enable TLS pre-master-key logging for debugging and adjust the TLS ticket cache size.

Crypto Providers, Hooks, and Advanced Extensions

  • crypto_provider — Select the Rustls crypto implementation, such as ring or aws-lc-rs. This is required unless a crate feature supplies a default.
  • hooks — Install an EndpointHooks implementation to intercept connection establishment events.
  • portmapper_config — Enable or disable UPnP, PCP, and NAT-PMP for automatic gateway port mapping.
  • net_report_config — Tune the optional net-report component that probes relays to detect NAT behavior, latency, and captive portals.
  • add_custom_transport (unstable feature) — Register a user-defined transport implementation.
  • path_selector (unstable feature) — Replace the default biased-RTT path selector with a custom algorithm.

What Builder::bind Does Under the Hood

When you invoke bind, the builder executes a strict validation and construction sequence defined in iroh/src/endpoint.rs:

  1. Generates a SecretKey if you did not provide one via secret_key.
  2. Validates that a crypto_provider is present; otherwise, construction fails.
  3. Builds TLS server and client configs, a QUIC StaticConfig, and a socket options struct from the accumulated state.
  4. Calls socket::EndpointInner::bind to allocate sockets and start the underlying QUIC endpoint.
  5. Installs address-lookup services and applies any address filter you configured.

This encapsulation keeps the public Endpoint API clean while ensuring that every fully built instance is in a valid state.

Practical iroh Endpoint Builder Examples

Default N0 Preset

The presets::N0 preset bundles a crypto provider, a default relay map, and standard DNS/PKARR address-lookup services. It is the fastest way to spin up an endpoint:

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

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let ep = Endpoint::builder(presets::N0).bind().await?;
    println!("Running as {}", ep.id().fmt_short());
    Ok(())
}

This example uses Endpoint::builder followed immediately by bind, relying entirely on the N0 defaults defined in iroh/src/endpoint/presets.rs.

Custom Transports and ALPN

To bind specific addresses and restrict accepted protocols, start from presets::Empty and layer on exactly what you need:

use iroh::{Endpoint, endpoint::presets, endpoint::BindOpts};
use std::sync::Arc;

let ep = Endpoint::builder(presets::Empty)
    .crypto_provider(Arc::new(rustls::crypto::ring::default_provider()))
    .clear_ip_transports()
    .bind_addr_with_opts("127.0.0.1:0", BindOpts::default().set_prefix_len(24))?
    .bind_addr_with_opts("[::1]:0", BindOpts::default().set_prefix_len(48))?
    .alpns(vec![b"my-protocol".to_vec()])
    .bind()
    .await?;

Here, clear_ip_transports removes defaults before bind_addr_with_opts attaches IPv4 and IPv6 listeners. The alpns call restricts the endpoint to a single application protocol.

Custom Address Lookup Service

You can integrate your own address discovery logic by implementing the AddressLookupBuilder trait and registering it with the builder:

struct MyLookup;

impl iroh::address_lookup::AddressLookupBuilder for MyLookup {
    fn into_address_lookup(
        self,
        _ep: &iroh::Endpoint,
    ) -> Result<
        Box<dyn iroh::address_lookup::AddressLookup>,
        iroh::address_lookup::AddressLookupFailed,
    > {
        // ...implementation...
        todo!()
    }
}

let ep = Endpoint::builder(presets::Minimal)
    .address_lookup(MyLookup)
    .bind()
    .await?;

The builder stores this service and installs it during bind so the endpoint can publish or resolve addresses through your custom backend.

Disable Relays for Direct Connections

If your deployment guarantees direct IP connectivity, disable relay fallback entirely:

let ep = Endpoint::builder(presets::Minimal)
    .relay_mode(iroh::RelayMode::Disabled)
    .clear_relay_transports()
    .bind()
    .await?;

Setting relay_mode to RelayMode::Disabled and clearing relay transports ensures the endpoint never attempts relayed communication.

Summary

  • The iroh::endpoint::Builder implements a classic builder pattern in iroh/src/endpoint.rs, collecting partial configuration through chainable methods and producing a finalized Endpoint via bind.
  • Presets such as presets::N0 supply sensible defaults, while presets::Empty lets you opt into every setting manually.
  • Key configuration areas include socket binding, secret_key and alpns, relay_mode, address_lookup services, transport_config, and crypto_provider.
  • The bind method validates required state—especially the crypto provider—generates missing secrets, builds TLS/QUIC internals, and starts the socket layer inside socket::EndpointInner::bind.

Frequently Asked Questions

What is the difference between presets::N0 and presets::Empty?

presets::N0 bundles a default crypto provider, relay map, and DNS/PKARR address-lookup services so you can call bind immediately. presets::Empty starts from a blank slate, requiring you to manually configure the crypto provider, transports, and any discovery logic before the endpoint can start.

Is crypto_provider always required when using the iroh Endpoint builder?

Yes, a crypto provider must be present before bind succeeds. If you use a preset like N0 that includes one, or if a crate feature supplies a default, you do not need to call crypto_provider explicitly. Otherwise, you must set it yourself.

Can I run an iroh endpoint without any relay servers?

Yes. Call .relay_mode(iroh::RelayMode::Disabled) and .clear_relay_transports() on the builder. This removes relay fallback and forces the endpoint to rely solely on direct IP connections.

How does Builder::bind validate configuration before spawning the endpoint?

Inside iroh/src/endpoint.rs, bind first ensures a crypto_provider is present and generates a SecretKey if none was supplied. It then constructs TLS and QUIC configurations, calls socket::EndpointInner::bind to open sockets, and finally installs any address-lookup services and filters. If any required state is missing, construction fails before network resources are allocated.

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 →