How Iroh Endpoint Discovery Works Without a Relay Server

Iroh discovers peers directly by publishing socket addresses to public naming services like DNS or PKARR, allowing nodes to connect without relying on a dedicated relay server.

Iroh is a peer-to-peer networking library that enables direct connections between nodes even without centralized infrastructure. The library achieves this through a modular address-lookup system that publishes and resolves node addresses via globally reachable naming services rather than requiring dedicated relay infrastructure.

Configuring Address Lookup Services

When building an Endpoint in iroh/src/endpoint.rs, the library exposes a configurable address-lookup mechanism through the builder API. The Builder::address_lookup method (lines 590-608) accepts services that implement the AddressLookup trait, allowing the endpoint to publish and resolve addressing information through multiple channels.

Default DNS Configuration

By default, iroh uses the N0 preset defined in iroh/src/endpoint/presets.rs (lines 99-106). This preset automatically injects a DnsAddressLookup service that queries DNS records for node addresses. Users can override this behavior to use PKARR resolution or in-memory services for testing environments.

use iroh::{Endpoint, endpoint::presets};

// Configure endpoint with DNS lookup but no relay
let ep = Endpoint::builder(presets::N0)
    .clear_relay_transports()
    .bind()
    .await?;

Publishing Endpoint Information

Each address-lookup service implements the AddressLookup trait. When an endpoint goes online, it automatically calls publish on every configured service, transmitting an EndpointInfo structure defined in iroh-dns/src/endpoint_info.rs (lines 7-14).

This structure contains:

  • The node's EndpointId
  • Direct socket addresses bound by the node
  • Optional user-defined metadata
  • The relay URL (if configured)

The publish method pushes this information to the respective naming service, making the node's public addresses discoverable by peers.

Resolving Remote Endpoints

When initiating a connection via Endpoint::connect in iroh/src/endpoint.rs (lines 274-279), the library follows a fallback sequence. First, it attempts any direct addresses supplied in the EndpointAddr. If those fail or are absent, the endpoint invokes self.inner.resolve_remote to query the configured address-lookup services.

The lookup services return streams of AddressLookupItem instances. The remote-state actor aggregates these results, and upon finding at least one reachable address, creates a MappedAddr that the underlying QUIC connection can use to establish the direct path.

Optional QUIC Address Discovery

While not required for relay-free operation, iroh includes an optional QUIC Address Discovery (QAD) mechanism. When a relay is configured as a "home relay," the endpoint sends discovery probes carrying the node's public key. The relay responds with the public source address observed from the probe, helping the node learn its own public IP.

This mechanism, configured via QuicTransportConfig, only activates when a relay is present. Without a relay, the endpoint skips this step and relies purely on the DNS or PKARR lookup results.

Constructing the EndpointAddr

After successful resolution, the endpoint's Watcher updates the local EndpointAddr (accessible via endpoint.addr() or endpoint.watch_addr()). This address contains the set of direct socket addresses that peers can dial, along with any fallback relay URLs. Because the information originates from globally reachable DNS or PKARR records, peers connect directly without requiring a relay intermediary.

Practical Implementation

The following example demonstrates building a relay-free endpoint that publishes to and resolves from DNS:

use iroh::{Endpoint, endpoint::presets};
use iroh::address_lookup::DnsAddressLookup;

// Build endpoint without relays
let ep = Endpoint::builder(presets::N0)
    .clear_relay_transports()
    .bind()
    .await?;

// Publish addresses to DNS
ep.online().await?;

// Connect to remote using only DNS lookup
let remote_id = "0x1234abcd...".parse()?;
let remote_addr = Endpoint::builder(presets::N0)
    .address_lookup(DnsAddressLookup::n0_dns())
    .bind()
    .await?
    .connect(remote_id, b"my-alpn")
    .await?;

To use PKARR instead of DNS, substitute DnsAddressLookup with PkarrResolverBuilder from iroh/src/address_lookup/pkarr.rs.

Summary

  • Address lookup services replace relay servers by publishing node addresses to DNS or PKARR systems
  • The AddressLookup trait in iroh/src/endpoint.rs enables pluggable discovery mechanisms
  • EndpointInfo structures contain direct socket addresses and relay URLs for publication
  • Resolution occurs via resolve_remote in the connection path, falling back from direct addresses to lookup services
  • QUIC Address Discovery is optional and only active when relays are configured

Frequently Asked Questions

Can iroh connect to peers without any centralized server at all?

Yes. By configuring DnsAddressLookup or PkarrResolver services and clearing relay transports, iroh nodes publish addresses to decentralized naming systems. The DNS or PKARR infrastructure serves as the discovery mechanism, while the actual connection occurs directly between peers via QUIC.

What is the difference between DNS and PKARR address lookup in iroh?

DNS lookup (DnsAddressLookup in iroh/src/address_lookup/dns.rs) queries traditional DNS records for node addresses, while PKARR lookup (PkarrResolver in iroh/src/address_lookup/pkarr.rs) uses the peer-to-peer PKARR protocol for decentralized name resolution. DNS requires existing DNS infrastructure, whereas PKARR operates entirely peer-to-peer without centralized authorities.

Does iroh endpoint discovery reveal my IP address publicly?

Yes. When using address lookup services, the EndpointInfo structure published to DNS or PKARR includes your node's direct socket addresses. This visible information enables other peers to connect directly to your node, which is necessary for relay-free operation but does expose your public IP address to the lookup service and querying peers.

What happens if the address lookup service fails?

If DNS or PKARR resolution fails and no direct addresses are provided in the EndpointAddr, the Endpoint::connect call will timeout. The connection logic in iroh/src/endpoint.rs attempts direct addresses first, then queries lookup services; if neither source yields reachable addresses, the connection attempt fails with a resolution error.

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 →