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:
-
Selecting the transport configuration – The builder maintains a list of
TransportConfigobjects. For each address supplied viaBuilder::bind_addrorBuilder::bind_addr_with_opts, the builder adds aTransportConfig::Ipentry containing anIpConfig(IPv4 or IPv6) that describes the socket address, prefix length, and flags such asrequiredanddefault route. -
Creating the UDP socket –
socket::EndpointInner::bindreceives asocket::Optionsstruct containing the filled-outtransportsvector and callstokio::net::UdpSocket::bindfor each entry. This actual socket creation lives in thesocketmodule, specifically referenced iniroh/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.,24for/24).set_is_required(bool)– Makes binding failures non-fatal when set tofalse.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_requiredisfalse, 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– DefinesBindOpts, its default values, chainable setters, and theToSocketAddrtrait used for address conversion. -
iroh/src/endpoint.rs– ContainsBuilder::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– ImplementsEndpointInner::bindwhere the transports gathered from the builder are turned into actual UDP sockets. -
iroh/src/socket/transports.rs– Holds theTransportConfig::Ipenum variant that stores theIpConfigbuilt fromBindOpts.
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 anEndpointInnerthat spawns UDP sockets viatokio::net::UdpSocket::bind. - The builder starts with default transports on
0.0.0.0and[::], whichbind_addrreplaces butbind_addr_with_optspreserves. BindOptsiniroh/src/endpoint/bind.rsprovides three controls:prefix_lenfor subnet routing,is_requiredfor error handling, andis_default_routefor gateway selection.- Routing selects sockets by longest prefix match first, falling back to the default route socket when no subnet matches.
- Setting
is_requiredtofalseallows 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →