# How Iroh's Address Lookup Service Works: DNS, PKARR, and Pluggable Endpoint Discovery

> Discover how Iroh's address lookup service leverages DNS, PKARR, and pluggable endpoints for seamless remote identifier resolution. Learn the technical details behind this essential feature.

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

---

**Iroh's address lookup service resolves remote endpoint identifiers through a pluggable trait system that supports DNS, PKARR, and in-memory backends, automatically publishing and resolving addressing information via the `AddressLookup` trait defined in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs).**

The n0-computer/iroh repository implements a flexible endpoint discovery mechanism through its `address_lookup` module. This system abstracts address resolution behind the `AddressLookup` trait, allowing applications to discover peers via DNS TXT records, PKARR signed packets, or custom in-memory stores. The Iroh address lookup service seamlessly integrates with the socket layer to handle background publishing and asynchronous resolution.

## The AddressLookup Trait Interface

At the core of the Iroh address lookup service is the **`AddressLookup`** trait exposed from [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs). This trait defines two optional behaviors that implementations provide:

- **`publish(&self, data: &EndpointData)`**: Publishes the local endpoint's addressing information for remote peers to discover. Only services that advertise addresses implement this method.
- **`resolve(&self, endpoint_id: EndpointId) -> Option<BoxStream<Result<Item, Error>>>`**: Resolves a remote `EndpointId` into a stream of `Item` objects containing `EndpointInfo` (relay URLs, direct IPs, and user data).

The trait uses `BoxStream` to support asynchronous, multi-item resolution, allowing lookup services to return multiple candidate addresses or update addresses over time.

## Built-in Lookup Implementations

The repository provides three concrete implementations of the `AddressLookup` trait, each serving different network discovery requirements.

### DNS Resolution via TXT Records

The **`DnsAddressLookup`** implementation in [`iroh/src/address_lookup/dns.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup/dns.rs) resolves endpoints by querying DNS TXT records. It constructs queries using the format `_iroh.<z-base-32-id>.<origin>`, where the endpoint ID is encoded in z-base-32. The parser extracts attributes such as `relay=` to construct `EndpointInfo` objects containing relay URLs and direct connection hints.

### PKARR Publishing and Resolution

The PKARR implementation in [`iroh/src/address_lookup/pkarr.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup/pkarr.rs) provides public-key addressable resource records through HTTP relays. This module contains two primary components:

- **`PkarrPublisher`**: Signs endpoint data with the local secret key and pushes records to PKARR relays. An internal **`PublisherService`** background task periodically republishes information and retries on failure, ensuring remote peers can always discover the latest address.
- **`PkarrResolver`**: Fetches and verifies signed PKARR records from relays to resolve remote endpoint IDs.

The publisher supports configurable TTL values and address filtering via `AddrFilter` to control which addressing information gets advertised.

### In-Memory Lookup for Testing

The **`MemoryLookup`** struct in [`iroh/src/address_lookup/memory.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup/memory.rs) provides a simple mutable map for manual endpoint injection. This implementation stores `EndpointInfo` objects supplied directly by the application, making it ideal for testing scenarios or out-of-band discovery mechanisms where network-based lookup is unnecessary.

## Socket Layer Integration and Remote Mapping

The `AddressLookupServices` struct combines publisher and resolver implementations into a unified service used by `Endpoint::address_lookup`. This structure holds an optional publisher and an optional resolver, allowing endpoints to resolve addresses without publishing their own, or publish without performing lookups.

When the socket layer needs to contact a remote endpoint, it:

1. Calls `address_lookup.resolve(id)` to obtain a stream of `Item`s.
2. Processes the stream to extract `EndpointInfo` containing relay URLs and direct IPs.
3. Selects the best address based on `AddrFilter` constraints and connection preferences.

The **remote-map** component in `iroh/src/socket/remote_map/*.rs` tracks pending connection attempts and drives the lookup process through **`trigger_address_lookup`** and **`handle_address_lookup_item`**. This tight integration means address discovery is transparent to the rest of the library—an `Endpoint` simply declares which lookup services it wants, and the socket layer automatically performs the necessary DNS or PKARR queries.

## Practical Implementation Examples

The following examples demonstrate how to configure different lookup services in an Iroh application.

Building a DNS lookup for the production iroh DNS zone:

```rust
let dns_lookup = iroh::address_lookup::DnsAddressLookup::n0_dns()
    .build(); // builder → DnsAddressLookup

```

Configuring a PKARR publisher with custom TTL and address filtering:

```rust
let pkarr_pub = iroh::address_lookup::PkarrPublisher::n0_dns()
    .ttl(60)                      // optional custom TTL
    .addr_filter(iroh::address_lookup::AddrFilter::unfiltered())
    .build(secret_key, tls_config);

```

Combining services into an `AddressLookupServices` and attaching to an endpoint:

```rust
let services = iroh::address_lookup::AddressLookupServices::builder()
    .resolver(dns_lookup)   // discover other peers via DNS
    .publisher(pkarr_pub)   // publish our own address via PKARR
    .build();

let endpoint = iroh::Endpoint::builder(presets::N0)
    .address_lookup(services)
    .bind()
    .await?;

```

Using the in-memory lookup for testing or manual endpoint injection:

```rust
let mem_lookup = iroh::address_lookup::memory::MemoryLookup::new();
mem_lookup.add_endpoint_info(iroh::EndpointInfo::from_parts(
    secret_key.public(),
    iroh::EndpointData::default()
        .add_relay("https://relay.example.com".parse()?),
));

```

Resolving a remote endpoint ID after receiving a connection ticket:

```rust
if let Some(mut stream) = endpoint.address_lookup().resolve(remote_id) {
    while let Some(item) = stream.next()..await {
        let info = item?.info();          // EndpointInfo with relay URLs, etc.
        // Use `info` to open a connection.
    }
}

```

## Summary

- **Pluggable Architecture**: The `AddressLookup` trait in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs) abstracts endpoint discovery behind `publish` and `resolve` methods, enabling custom lookup implementations.
- **Multiple Backends**: The framework provides DNS-based resolution ([`iroh/src/address_lookup/dns.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup/dns.rs)), PKARR signed records ([`iroh/src/address_lookup/pkarr.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup/pkarr.rs)), and in-memory storage ([`iroh/src/address_lookup/memory.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup/memory.rs)).
- **Automatic Publishing**: The `PkarrPublisher` uses a background `PublisherService` to periodically republish endpoint information and retry on network failures.
- **Socket Integration**: The `AddressLookupServices` struct combines publishers and resolvers, while the remote-map component in `iroh/src/socket/remote_map/*.rs` handles `trigger_address_lookup` and `handle_address_lookup_item` to automate address discovery during connection establishment.

## Frequently Asked Questions

### What is the difference between the publisher and resolver in Iroh's address lookup service?

The **publisher** advertises the local endpoint's addressing information (relay URLs, IPs) to the network, while the **resolver** queries the network to discover addressing information for remote endpoints. An `AddressLookupServices` instance can configure either one or both, allowing nodes to resolve addresses without publishing their own, or publish without performing lookups.

### How does PKARR address resolution work in Iroh?

PKARR resolution uses the `PkarrResolver` to fetch signed resource records from PKARR HTTP relays. The `PkarrPublisher` signs endpoint data with the node's secret key and pushes it to relays, where the `PublisherService` background task handles periodic republishing and failure retries. Remote nodes resolve these records to obtain verified `EndpointInfo` containing relay URLs and direct addresses.

### Can I use custom address lookup implementations with Iroh?

Yes, you can implement the `AddressLookup` trait from [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs) to create custom lookup mechanisms. The trait only requires implementing `publish` and/or `resolve` methods, allowing integration with proprietary discovery services, distributed hash tables, or other naming systems beyond the built-in DNS and PKARR options.

### How does Iroh handle address changes and updates?

When the local endpoint's address changes (such as when a new relay URL becomes reachable), the publisher side pushes a fresh signed packet or DNS record immediately. The `PublisherService` in [`pkarr.rs`](https://github.com/n0-computer/iroh/blob/main/pkarr.rs) maintains a background task that periodically republishes this information and retries on failure, ensuring that remote peers can always discover the latest address through subsequent `resolve` calls.