How Iroh Handles Address Lookup: DNS, PKARR, and the Pluggable Discovery System

Iroh resolves remote endpoint addresses through a pluggable subsystem centered on the AddressLookup trait, supporting DNS, PKARR, and in-memory backends that publish and resolve EndpointData through asynchronous streams.

In the n0-computer/iroh repository, establishing connectivity between peers depends on a flexible address lookup architecture that adapts to diverse network environments. The system abstracts address discovery behind a unified trait, allowing applications to resolve EndpointId values into actionable network coordinates via DNS records, PKARR packets, or custom in-memory stores.

The AddressLookup Trait: Core Abstraction

At the heart of Iroh's discovery mechanism lies the AddressLookup trait, exposed from iroh/src/lib.rs. This interface defines two primary behaviors that implementations can provide:

  • publish(&self, data: &EndpointData) – Advertises the local endpoint's addressing information for remote peers to discover.
  • resolve(&self, endpoint_id: EndpointId) – Resolves a remote identifier into a BoxStream of Result<Item, Error> containing addressing details.

The resolve method returns an asynchronous stream of Item objects, each encapsulating EndpointInfo with relay URLs, direct IP addresses, and user data. This streaming approach accommodates scenarios where multiple address options exist or discovery occurs incrementally.

Built-in Lookup Services

The Iroh codebase provides three concrete implementations of the AddressLookup trait, each optimized for different discovery scenarios.

DNS Lookup

The DNS lookup implementation queries TXT records under the _iroh.<z-base-32-id>.<origin> namespace. It parses attributes such as relay= to extract relay server URLs. The implementation resides in iroh/src/address_lookup/dns.rs and provides a DnsAddressLookup builder for easy configuration.

PKARR Lookup

For decentralized discovery, Iroh implements the PKARR protocol (public-key addressable resource records) via iroh/src/address_lookup/pkarr.rs. This module contains two distinct components:

  • PkarrPublisher – Signs and pushes resource records to an HTTP relay, periodically republishing via a background PublisherService task that retries on failure.
  • PkarrResolver – Fetches and verifies signed records from the PKARR relay network.

The PKARR implementation supports additional configuration such as custom TTL values and address filtering through AddrFilter.

In-Memory Lookup

For testing scenarios or out-of-band discovery, iroh/src/address_lookup/memory.rs provides a MemoryLookup type. This mutable map stores EndpointInfo objects supplied directly by the application, bypassing network-based resolution entirely.

The Resolution Flow

When the socket layer initiates a connection to a remote peer, Iroh's address lookup follows a specific sequence:

  1. The system calls address_lookup.resolve(id), returning a stream of Item objects.
  2. Each Item yields EndpointInfo containing relay URLs, direct IPs, and metadata.
  3. The socket layer selects the optimal address (preferring relays, respecting AddrFilter constraints) and attempts the connection.

This process is orchestrated through the AddressLookupServices struct, created via Endpoint::address_lookup. This structure holds an optional publisher and resolver, allowing asymmetric configurations where a node might resolve addresses via DNS while publishing via PKARR.

Publishing Endpoint Information

When local addressing information changes—such as when a new relay URL becomes reachable—the publisher side activates. In PkarrPublisher, a background task periodically constructs fresh signed packets and pushes them to the configured relay. Similarly, DNS-based publishing updates TXT records to reflect current reachability. This ensures remote peers can always discover the latest valid addresses without manual intervention.

Integration with the Socket Layer

The lookup service integrates tightly with Iroh's connection management through the remote-map component located in iroh/src/socket/remote_map/*.rs. This subsystem:

  • Tracks pending connection attempts awaiting address resolution.
  • Triggers lookups via trigger_address_lookup when connection establishment begins.
  • Processes incoming lookup items through handle_address_lookup_item, updating the routing table with new addressing information.

This integration makes address discovery transparent to the rest of the library. An Endpoint simply declares its preferred lookup services, and iroh/src/socket.rs automatically manages the background DNS or PKARR queries required to maintain connectivity.

Practical Examples

Configure a DNS resolver combined with a PKARR publisher:

// Build a DNS lookup that queries the production iroh DNS zone.
let dns_lookup = iroh::address_lookup::DnsAddressLookup::n0_dns()
    .build();

// Build a PKARR publisher that signs records with our secret key.
let pkarr_pub = iroh::address_lookup::PkarrPublisher::n0_dns()
    .ttl(60)
    .addr_filter(iroh::address_lookup::AddrFilter::unfiltered())
    .build(secret_key, tls_config);

// Combine them into a service used by an Endpoint.
let services = iroh::address_lookup::AddressLookupServices::builder()
    .resolver(dns_lookup)
    .publisher(pkarr_pub)
    .build();

// Attach the service to an endpoint.
let endpoint = iroh::Endpoint::builder(presets::N0)
    .address_lookup(services)
    .bind()
    .await?;

Use the in-memory lookup for testing:

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()?),
));

Resolve a remote endpoint manually:

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

Summary

  • Pluggable architecture: The AddressLookup trait in iroh/src/lib.rs abstracts address discovery, supporting DNS, PKARR, and custom implementations.
  • Dual-mode operation: Services can act as resolvers (fetching remote addresses), publishers (advertising local addresses), or both.
  • Key source files: dns.rs for TXT record queries, pkarr.rs for decentralized signed records, and memory.rs for manual injection.
  • Socket integration: The remote-map component in iroh/src/socket/remote_map/*.rs transparently drives lookups during connection establishment.
  • Streaming resolution: The resolve method returns asynchronous streams, enabling incremental discovery and multiple address candidates.

Frequently Asked Questions

What is the difference between PKARR and DNS lookup in Iroh?

DNS lookup queries centralized DNS zones for TXT records containing relay URLs, suitable for traditional infrastructure. PKARR lookup uses the decentralized PKARR protocol with cryptographically signed records, allowing nodes to publish addresses without centralized DNS control. Both implement the same AddressLookup trait and can be used interchangeably or combined.

How does Iroh handle address changes when my IP or relay changes?

The publisher side of the lookup service detects addressing changes and pushes updates automatically. In PkarrPublisher, a background PublisherService task periodically republishes signed packets with fresh EndpointData. Similarly, DNS publishers update TXT records. Remote peers receive these updates through subsequent resolve calls.

Can I use Iroh's address lookup without publishing my own address?

Yes. The AddressLookupServices builder allows you to configure only a resolver without a publisher. This creates a "read-only" configuration where your node can discover others via DNS or PKARR but does not advertise its own addressing information, which is useful for clients that only initiate connections.

Where does the actual connection logic use the resolved addresses?

The socket layer in iroh/src/socket.rs and the remote-map subsystem in iroh/src/socket/remote_map/*.rs consume lookup results. When you call resolve, the remote-map tracks pending attempts via trigger_address_lookup and processes results through handle_address_lookup_item, selecting optimal paths and updating connection state without requiring manual intervention.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →