How to Configure Address Lookup Services in iroh
To configure address lookup services in iroh, implement the AddressLookup trait or use built-in builders like PkarrPublisher or DnsAddressLookup, then register them via Endpoint::builder().address_lookup() before calling bind().
The iroh library from n0-computer provides a flexible address lookup subsystem that enables endpoints to publish their own addressing information and resolve remote peers by EndpointId. When you configure address lookup services in iroh, you allow nodes to dynamically discover relay URLs, direct socket addresses, and custom metadata without relying on static IP configuration.
Core Address Lookup Components
The address lookup system in iroh is built around four primary abstractions defined in iroh/src/address_lookup.rs.
The AddressLookup Trait
The AddressLookup trait defines the minimal interface any service must implement. It requires two methods: publish for announcing local endpoint data and resolve for querying remote endpoint information, returning a stream of results.
AddressLookupBuilder
The AddressLookupBuilder trait allows services to defer initialization until the endpoint is fully constructed. This is essential when a service needs to read the endpoint's own configuration, such as its relay URL or direct addresses, before becoming active.
AddressLookupServices
The AddressLookupServices struct acts as the central registry held by each Endpoint. It stores a list of boxed AddressLookup implementations, an optional AddrFilter, and the last published EndpointData. According to the source code in iroh/src/address_lookup.rs (lines 60-89), this registry ensures all services receive the same data and merges their resolution streams into a single AddressLookupStream.
AddrFilter
The AddrFilter is a user-supplied function that prunes or reorders addresses before they are published to any service. For example, you can configure AddrFilter::relay_only() to ensure only relay URLs are published, hiding direct IP addresses from the lookup services.
Built-in Address Lookup Services
Iroh ships with several ready-to-use implementations suitable for different deployment scenarios.
MemoryLookup provides an in-process, ephemeral lookup service ideal for unit tests and embedded scenarios where you want to avoid external network dependencies.
DnsAddressLookup resolves peers via standard DNS using the iroh-dns crate. The convenience constructor DnsAddressLookup::n0_dns() points to the public n0.computer DNS infrastructure.
PkarrPublisher and its resolver counterpart publish endpoint data to a pkarr relay and resolve via HTTPS. The PkarrPublisher::n0_dns() constructor configures publication to the public n0.computer pkarr relay.
Configuring Address Lookup Services
You configure these services during the endpoint builder phase. Here are the four primary patterns for setting up address lookup in iroh.
Adding Default DNS and Pkarr Services
To enable both publishing your own address via pkarr and resolving others via DNS, chain multiple address_lookup calls:
use iroh::{
Endpoint,
address_lookup::{self, AddrFilter},
endpoint::presets,
};
#[tokio::main]
async fn main() -> Result<(), iroh::Error> {
let ep = Endpoint::builder(presets::Minimal)
.addr_filter(AddrFilter::relay_only())
.address_lookup(address_lookup::PkarrPublisher::n0_dns())
.address_lookup(address_lookup::DnsAddressLookup::n0_dns())
.bind()
.await?;
Ok(())
}
This registers both services before binding. The endpoint will automatically publish its relay URL to the pkarr system and resolve remote peers using DNS.
Using In-Memory Lookup for Testing
For test harnesses or local-only networks, use MemoryLookup to avoid external dependencies:
use iroh::{Endpoint, address_lookup::MemoryLookup, endpoint::presets};
let memory = MemoryLookup::new();
let ep = Endpoint::builder(presets::Minimal)
.address_lookup(memory)
.bind()
.await?;
Since MemoryLookup implements both AddressLookup and AddressLookupBuilder, you can pass it directly to Builder::address_lookup().
Filtering Published Addresses
You can apply an address filter after the endpoint is built to restrict what information gets shared:
let ep = Endpoint::builder(presets::Minimal).bind().await?;
let lookup = ep.address_lookup().expect("still open");
lookup.set_addr_filter(address_lookup::AddrFilter::relay_only());
As implemented in iroh/src/address_lookup.rs, this filter applies to all future publish calls, ensuring only relay URLs are distributed to lookup services while direct socket addresses remain private.
Implementing Custom Resolvers
To integrate a proprietary discovery protocol, implement the AddressLookup trait and add it to the running endpoint:
use iroh::address_lookup::{AddressLookup, Item, Error};
use futures::stream::BoxStream;
struct MyResolver;
impl AddressLookup for MyResolver {
fn resolve(
&self,
endpoint_id: iroh_base::EndpointId
) -> Option<BoxStream<Result<Item, Error>>> {
// Custom resolution logic here
None
}
}
let ep = Endpoint::builder(presets::Minimal).bind().await?;
ep.address_lookup().unwrap().add(MyResolver);
This pattern allows you to merge iroh's built-in services with custom address discovery mechanisms.
Address Lookup Lifecycle and Resolution
Understanding the internal lifecycle helps debug connectivity issues. The process follows three distinct phases.
Builder Phase: While constructing the Endpoint, the Builder::address_lookup method accepts any object implementing AddressLookupBuilder or AddressLookup. These are stored in the AddressLookupServices registry.
Publish Phase: After transport addresses change, the endpoint calls AddressLookupServices::publish. The data is filtered through any configured AddrFilter and forwarded to every registered service, as defined in iroh/src/address_lookup.rs lines 60-89.
Resolve Phase: When Endpoint::connect needs a remote's address, it invokes AddressLookupServices::resolve. This method creates a merged stream (AddressLookupStream) that yields items from all services as soon as they become available. The implementation guarantees that a fast-failing service does not hide later successes from slower services, verified by the test address_lookup_succeeds_after_other_resolver_errors in the iroh test suite. If all services return no results, the stream ends with AddressLookupFailed::NoResults.
Summary
- Configure address lookup services in iroh by registering
AddressLookuporAddressLookupBuilderimplementations viaEndpoint::builder().address_lookup()before binding. - Built-in services include
MemoryLookupfor testing,DnsAddressLookupfor DNS resolution, andPkarrPublisherfor decentralized address publishing. - The
AddressLookupServicesregistry iniroh/src/address_lookup.rsmanages multiple services and merges their resolution streams automatically. - Use
AddrFilterto control which addresses (relay-only vs. direct) get published to external services. - Custom resolvers implement the
AddressLookuptrait and can be added to a running endpoint viaep.address_lookup().unwrap().add().
Frequently Asked Questions
What is an address lookup service in iroh?
An address lookup service is a module that implements the AddressLookup trait to publish an endpoint's addressing information (relay URLs, direct addresses) to a discovery system and resolve remote EndpointId values into connection details. According to the n0-computer/iroh source code in iroh/src/address_lookup.rs, these services enable peer-to-peer connections without hardcoded IP addresses by acting as decentralized directories.
How do I add multiple address lookup services to one endpoint?
Call Builder::address_lookup() multiple times before bind(), or use ep.address_lookup().unwrap().add() after binding. The AddressLookupServices registry maintains a list of all services and merges their resolution streams, meaning you can combine DNS resolution, pkarr publishing, and custom resolvers simultaneously. Each service receives the same filtered data during the publish phase.
Can I filter which addresses get published to lookup services?
Yes. Use the AddrFilter trait, implemented in iroh/src/address_lookup.rs and re-exported via iroh::address_lookup::AddrFilter. Common filters include AddrFilter::relay_only() to restrict publication to relay URLs only, preventing direct IP addresses from being exposed to external lookup services. You can set this during building with Builder::addr_filter() or dynamically via lookup.set_addr_filter() after the endpoint is constructed.
How does iroh handle failures in address lookup services?
Iroh implements a non-failing merge strategy in AddressLookupServices::resolve. The method creates a merged stream that yields results from any service as soon as they arrive, buffering errors from individual services. This design ensures that if one lookup service (like DNS) times out or errors, it will not prevent another service (like pkarr) from returning valid results. The stream only returns AddressLookupFailed::NoResults if all registered services fail to provide addresses.
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 →