# How iroh Endpoint Binding Works and Which BindOpts Are Available

> Learn how iroh endpoint binding works via a builder pattern processing BindOpts and TransportConfig. Discover subnet routing, default routes, and failure handling options.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: deep-dive
- Published: 2026-06-19

---

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

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

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

```rust
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`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/bind.rs)** – Defines `BindOpts`, its default values, chainable setters, and the `ToSocketAddr` trait used for address conversion.

- **[`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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 (`Builder` → `BindOpts`), 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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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.