How iroh Endpoint Binding Works and Which BindOpts Are Available

iroh endpoints bind to UDP sockets through a builder pattern that processes TransportConfig entries created from BindOpts, allowing you to configure subnet routing, default routes, and failure handling for each socket.

The n0-computer/iroh crate provides a sophisticated endpoint binding mechanism that lets you control exactly how UDP sockets are created and routed. Understanding iroh endpoint binding is essential when deploying on multi-homed systems or when you need specific network topology configurations. The BindOpts struct in iroh/src/endpoint/bind.rs provides the granular control needed for these advanced scenarios.

How Endpoint Binding Works in iroh

iroh endpoints are created through the iroh::endpoint::Builder. When you await the builder’s bind() method, it constructs an EndpointInner that creates the underlying UDP sockets. According to the source code in iroh/src/endpoint.rs, this binding process splits into two distinct steps:

  1. Selecting the transport configuration – The builder maintains a list of TransportConfig objects. For each address supplied via Builder::bind_addr or Builder::bind_addr_with_opts, the builder adds a TransportConfig::Ip entry containing an IpConfig (IPv4 or IPv6) that describes the socket address, prefix length, and flags such as required and default route.

  2. Creating the UDP socketsocket::EndpointInner::bind receives a socket::Options struct containing the filled-out transports vector and calls tokio::net::UdpSocket::bind for each entry. This actual socket creation lives in the socket module, specifically referenced in iroh/src/socket/mod.rs.

The builder starts with two pre-configured transports that bind to the unspecified addresses 0.0.0.0 (IPv4) and [::] (IPv6). Adding a custom bind address via bind_addr removes the corresponding unspecified one, whereas bind_addr_with_opts does not replace the default bind address, allowing you to bind multiple sockets of the same IP family.

Available BindOpts for iroh Endpoint Configuration

BindOpts lives in iroh/src/endpoint/bind.rs and controls how individual sockets behave within the routing table. The struct contains three configurable fields:

Field Type Default Description
prefix_len u8 0 Network prefix length for the subnet that this socket represents. Used by iroh’s routing table to select the correct socket for outgoing packets.
is_required bool true Whether a failure to bind this socket should abort endpoint creation (true) or be silently ignored (false).
is_default_route Option<bool> None Marks the socket as the default route for destinations that do not match any bound subnet. When None, the socket acts as default route only if prefix_len == 0.

Chainable Setters

BindOpts provides chainable setters for configuration:

  • set_prefix_len(u8) – Sets the subnet mask (e.g., 24 for /24).
  • set_is_required(bool) – Makes binding failures non-fatal when set to false.
  • set_is_default_route(bool) – Explicitly chooses or deselects the socket as the default route.

Routing Semantics

As implemented in iroh/src/endpoint.rs, the routing logic follows these rules:

  • Subnet matching – Subnets are ordered by longest prefix first; the first subnet containing the destination IP is chosen.
  • Default route – At most one socket per address family may be marked as the default route. If no subnet matches, the default route socket is used, assuming the attached subnet has a gateway router.
  • Error handling – When is_required is false, a binding error (such as a port already in use) is ignored and the endpoint starts with the remaining transports.

Practical Examples of iroh Endpoint Binding

The following examples demonstrate how to configure endpoint binding with different BindOpts settings.

Basic Binding with Default Options

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

#[tokio::main]
async fn main() -> n0_error::Result<()> {
    // Uses default options (prefix /0, required, default route)
    let ep = Endpoint::builder(presets::N0)
        .clear_ip_transports()      // Start from a clean slate
        .bind_addr("127.0.0.1:0")?  // IPv4 bind, default opts
        .bind_addr("[::1]:0")?      // IPv6 bind, default opts
        .bind()
        .await?;
    
    Ok(())
}

Custom Subnet with Non-Default Route

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

#[tokio::main]
async fn main() -> n0_error::Result<()> {
    let ep = Endpoint::builder(presets::N0)
        .clear_ip_transports()
        .bind_addr_with_opts(
            "192.168.10.5:0",
            BindOpts::default()
                .set_prefix_len(24)           // Only addresses in 192.168.10.0/24
                .set_is_default_route(false)  // Not the default route
        )?
        .bind()
        .await?;
    
    Ok(())
}

Optional Binding with Failure Tolerance

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

#[tokio::main]
async fn main() -> n0_error::Result<()> {
    let ep = Endpoint::builder(presets::N0)
        .clear_ip_transports()
        .bind_addr_with_opts(
            "0.0.0.0:9000",                 // Try to listen on port 9000
            BindOpts::default()
                .set_is_required(false)   // If 9000 is taken, continue anyway
        )?
        .bind()
        .await?;
    
    Ok(())
}

Key Source Files for Endpoint Binding

Understanding the complete flow requires examining these specific files in the n0-computer/iroh repository:

  • iroh/src/endpoint/bind.rs – Defines BindOpts, its default values, chainable setters, and the ToSocketAddr trait used for address conversion.

  • iroh/src/endpoint.rs – Contains Builder::bind_addr, Builder::bind_addr_with_opts, and the high-level binding flow, plus extensive documentation on routing and default-route behavior.

  • iroh/src/socket/mod.rs – Implements EndpointInner::bind where the transports gathered from the builder are turned into actual UDP sockets.

  • iroh/src/socket/transports.rs – Holds the TransportConfig::Ip enum variant that stores the IpConfig built from BindOpts.

These files illustrate how iroh’s endpoint binding is a two-stage process: first the user-visible configuration (BuilderBindOpts), then the low-level socket creation (EndpointInner::bind).

Summary

  • iroh endpoint binding uses a builder pattern where Builder::bind() creates an EndpointInner that spawns UDP sockets via tokio::net::UdpSocket::bind.
  • The builder starts with default transports on 0.0.0.0 and [::], which bind_addr replaces but bind_addr_with_opts preserves.
  • BindOpts in iroh/src/endpoint/bind.rs provides three controls: prefix_len for subnet routing, is_required for error handling, and is_default_route for gateway selection.
  • Routing selects sockets by longest prefix match first, falling back to the default route socket when no subnet matches.
  • Setting is_required to false allows the endpoint to start even if a specific socket fails to bind, enabling "best-effort" multi-NIC configurations.

Frequently Asked Questions

What is the difference between bind_addr and bind_addr_with_opts in iroh?

bind_addr adds a socket with default BindOpts and removes the corresponding unspecified address (0.0.0.0 or [::]) from the builder. bind_addr_with_opts accepts a custom BindOpts struct and does not remove the default unspecified address, allowing you to bind multiple sockets of the same IP family.

How does iroh handle default routes when multiple sockets are bound?

The routing table selects sockets by longest prefix match first. If no subnet matches the destination, iroh uses the socket marked as the default route. At most one socket per address family (IPv4/IPv6) may be marked as the default route, and if is_default_route is None, a socket acts as default only when prefix_len == 0.

What happens when is_required is set to false in BindOpts?

When is_required is false, iroh ignores binding failures (such as address-in-use errors) for that specific socket and continues constructing the endpoint with the remaining transports. This is useful for optional ports or when binding to interfaces that may not always be available.

Which file contains the BindOpts struct definition?

The BindOpts struct and its associated methods are defined in iroh/src/endpoint/bind.rs in the n0-computer/iroh repository. This file also contains the ToSocketAddr trait used for converting addresses during the binding process.

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 →