# How to Configure Address Lookup Services in iroh

> Learn how to configure address lookup services in iroh. Implement traits or use builders like PkarrPublisher and DnsAddressLookup for seamless integration.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-06-26

---

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

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

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

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

```rust
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`](https://github.com/n0-computer/iroh/blob/main/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 `AddressLookup` or `AddressLookupBuilder` implementations via `Endpoint::builder().address_lookup()` before binding.
- **Built-in services** include `MemoryLookup` for testing, `DnsAddressLookup` for DNS resolution, and `PkarrPublisher` for decentralized address publishing.
- The **`AddressLookupServices`** registry in [`iroh/src/address_lookup.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup.rs) manages multiple services and merges their resolution streams automatically.
- Use **`AddrFilter`** to control which addresses (relay-only vs. direct) get published to external services.
- Custom resolvers implement the `AddressLookup` trait and can be added to a running endpoint via `ep.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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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.