How iroh's Address Lookup Service Works with DNS and Pkarr
TLDR: iroh resolves endpoint addresses by querying DNS TXT records that contain cryptographically signed Pkarr packets, verifying the signatures against the public key, and extracting connection endpoints from the embedded DNS data.
The iroh address lookup service in the n0-computer/iroh repository provides a decentralized mechanism for discovering peer connection information using DNS and Pkarr (Public Key Addressable Resource Records). This system allows clients to retrieve endpoint addresses, relay URLs, and ports by querying human-readable domain names that encode Ed25519 public keys, eliminating the need for centralized directory services.
Constructing the DNS Query Name
The resolution process begins with constructing a specialized DNS name that encodes the target's public key. The format follows _iroh.<z-base-32-encoded-public-key>.<origin>, where:
- The
<origin>is eitherdns.iroh.link.for production environments orstaging-dns.iroh.link.for staging. - The public key is extracted from the
EndpointIdthat the caller already possesses and encoded using Z-base-32.
This naming logic is implemented in DnsResolver::lookup_endpoint_by_id within iroh-dns/src/dns.rs. The method formats the query string before delegating to the underlying resolver.
Retrieving Signed Packets via DNS TXT Lookup
Once the DNS name is constructed, the system performs a TXT record lookup using the DnsResolver struct, which is backed by HickoryResolver. The lookup executes with a default 3-second timeout defined as DNS_TIMEOUT in the resolver configuration.
The query flow proceeds through DnsResolver::lookup_txt → HickoryResolver::lookup_txt → the hickory_resolver crate. The returned TXT payload contains a Pkarr signed packet—a self-authenticating data structure defined in iroh-dns/src/pkarr.rs that encapsulates the endpoint's connection details.
Cryptographic Verification and Packet Parsing
After retrieving the TXT records, the raw data undergoes strict verification and parsing before being trusted. The EndpointInfo::from_txt_lookup method concatenates the TXT strings and constructs a SignedPacket via SignedPacket::from_bytes.
The packet layout follows a precise binary format defined in iroh-dns/src/pkarr.rs:
- 32 bytes: Ed25519 public key
- 64 bytes: Signature
- 8 bytes: Timestamp
- Variable: DNS wire-format packet
The total size is bounded by MAX_SIGNED_PACKET_SIZE, with HEADER_SIZE accounting for the non-DNS prefix.
The verification process:
- Signature validation using
PublicKey::verifyto ensure the packet was signed by the private key corresponding to the embedded public key. - DNS parsing of the inner payload using
simple_dns::Packet::parseto extract the actual resource records. - Record extraction via helper methods
SignedPacket::txt_recordsandSignedPacket::all_txt_records, which convert raw DNS answers into structured strings thatEndpointInfointerprets.
Extracting Endpoint Information
Upon successful verification, the system instantiates an EndpointInfo struct defined in iroh-dns/src/endpoint_info.rs. This structure extracts critical connection fields including relay_url, quic_addr, and tcp_addr from the validated DNS records. The EndpointId used in the initial query (handled in iroh-base/src/endpoint_addr.rs) ensures that the resolved information cryptographically matches the expected peer identity.
Caching and Network Resilience
The resolver implements several strategies to ensure reliable operation across flaky networks:
- DNS caching: Responses are cached to reduce redundant queries and improve resolution latency.
- Cache management: The
clear_cachemethod onDnsResolverallows explicit cache invalidation, whileresetautomatically rebuilds the resolver when network changes are detected. - Staggered lookups: The
lookup_endpoint_by_id_staggeredmethod implements jittered retry logic with configurable delays (e.g., 0ms, 200ms, 500ms) to improve success rates on unstable connections.
Implementation Examples
Resolving by Endpoint ID
To resolve an endpoint using its public key identifier and the production DNS infrastructure:
use iroh_dns::{DnsResolver, N0_DNS_ENDPOINT_ORIGIN_PROD};
use iroh_base::EndpointId;
async fn resolve_endpoint(id: EndpointId) -> Result<(), Box<dyn std::error::Error>> {
// Create a resolver with default system DNS settings
let resolver = DnsResolver::new();
// Perform the lookup (3 s timeout is the default DNS_TIMEOUT)
let info = resolver
.lookup_endpoint_by_id(&id, N0_DNS_ENDPOINT_ORIGIN_PROD)
.await?;
println!("Resolved endpoint: {:?}", info);
Ok(())
}
Resolving by Human-Readable Domain
For lookups using traditional domain names, the resolver automatically handles the _iroh. prefix:
use iroh_dns::DnsResolver;
async fn resolve_by_name(name: &str) -> Result<(), Box<dyn std::error::Error>> {
let resolver = DnsResolver::default(); // same as DnsResolver::new()
let info = resolver
.lookup_endpoint_by_domain_name(name) // adds the "_iroh." prefix if missing
.await?;
println!("Endpoint info for {name}: {:?}", info);
Ok(())
}
Staggered Resolution with Retries
For improved reliability in adverse network conditions, use staggered lookups with custom delay patterns:
use iroh_dns::DnsResolver;
use iroh_base::EndpointId;
async fn resolve_staggered(id: EndpointId) -> Result<(), Box<dyn std::error::Error>> {
let resolver = DnsResolver::default();
// Try up to three attempts with 0 ms, 200 ms and 500 ms delays
let delays = [200u64, 500];
let info = resolver
.lookup_endpoint_by_id_staggered(&id, iroh_dns::N0_DNS_ENDPOINT_ORIGIN_PROD, &delays)
.await?;
println!("Staggered result: {:?}", info);
Ok(())
}
Key Source Files
The iroh address lookup service spans several critical files in the repository:
iroh-dns/src/pkarr.rs: Defines the Pkarr signed-packet format, parsing logic, signature verification usingPublicKey::verify, and helper methods for TXT record extraction.iroh-dns/src/dns.rs: Implements the high-levelDnsResolver, theResolvertrait, and public APIs includinglookup_endpoint_by_id,lookup_endpoint_by_domain_name, andlookup_endpoint_by_id_staggered.iroh-dns/src/endpoint_info.rs: Parses validated TXT data into theEndpointInfostruct, mapping DNS records to typed fields like relay URLs and transport addresses.iroh-base/src/endpoint_addr.rs: Contains theEndpointIdtype and Z-base-32 encoding logic used in DNS name construction.iroh-base/src/relay_url.rs: Represents relay URLs stored within endpoint TXT records.
Summary
- iroh stores endpoint connection data in Pkarr-signed DNS packets published as TXT records.
- DNS names follow the format
_iroh.<z-base-32-pubkey>.<origin>, encoding the target's public key in the query itself. - The
DnsResolverqueries these records with a 3-second timeout, usingHickoryResolverfor underlying DNS operations. - Retrieved packets undergo cryptographic verification via
PublicKey::verifybefore parsing withsimple_dns::Packet::parse. - Validated data populates an
EndpointInfostruct containingrelay_url,quic_addr, andtcp_addr. - The system provides caching, cache clearing, and staggered retry mechanisms for robust operation across network conditions.
Frequently Asked Questions
What is Pkarr and why does iroh use it for address lookup?
Pkarr (Public Key Addressable Resource Records) is a system for publishing signed DNS records that are self-authenticating using Ed25519 public keys. iroh uses Pkarr to enable decentralized endpoint discovery where the DNS name itself encodes the public key, allowing clients to verify that retrieved addresses were published by the legitimate key owner without trusting the DNS server.
How does the DNS name format encode the public key?
The DNS name uses the format _iroh.<z-base-32-encoded-public-key>.<origin>, where the public key extracted from the EndpointId is encoded using Z-base-32 (a variant of base32 that avoids visually similar characters). This encoding appears between the _iroh. prefix and the origin domain (e.g., dns.iroh.link.), making the public key both human-readable and DNS-compatible.
What happens if the DNS lookup fails or the signature is invalid?
If the DNS query returns no TXT records or the network times out (default 3 seconds), the resolver returns an error indicating the endpoint could not be found. If TXT records exist but SignedPacket::from_bytes fails to verify the signature via PublicKey::verify, or if the packet structure violates MAX_SIGNED_PACKET_SIZE constraints, the resolution fails with a verification error, preventing connection to potentially malicious or corrupted endpoints.
How does iroh handle network changes during resolution?
The DnsResolver provides a reset method that rebuilds the underlying resolver when network changes are detected, ensuring the system uses current DNS servers and network routes. Additionally, applications can call clear_cache to invalidate stale DNS responses, and use lookup_endpoint_by_id_staggered to implement jittered retries that accommodate transient network failures during the lookup process.
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 →