How pkarr Publishing Works with iroh: Signed Packets to DNS Resolution

pkarr publishing with iroh works by creating cryptographically signed DNS packets containing TXT records, transmitting them via HTTP PUT requests to an iroh-dns-server, and resolving them through a persistent store with LRU caching and DHT fallback.

The iroh networking library implements pkarr (Public Key Addressable Resource Records) to enable decentralized, self-certified DNS publishing without centralized authorities. This guide explains how pkarr publishing works with iroh, covering the wire format, HTTP API endpoints, and storage mechanisms implemented in the n0-computer/iroh repository.

The pkarr Signed Packet Format

iroh uses the standard pkarr signed-packet wire format defined in iroh-dns/src/pkarr.rs. The binary structure consists of a 32-byte public key, 64-byte signature, 8-byte timestamp, and the DNS packet payload.

This format ensures cryptographic integrity: the signature covers the timestamp and DNS packet, proving the publisher controls the private key associated with the public-key label. The specific constants and struct definitions are located at lines 17-30 of iroh-dns/src/pkarr.rs.

Creating and Signing Packets

Applications construct publishable packets using the SignedPacket::from_txt_strings method. This helper takes a secret key, DNS name, TXT values, and TTL, then builds a DNS packet, signs it, and returns the serialized bytes.

Building a Signed Packet in Rust

use iroh_base::SecretKey;
use iroh_dns::pkarr::SignedPacket;

// Generate or load a secret key
let sk = SecretKey::generate();

// Publish two TXT records under "_iroh.example."
let packet = SignedPacket::from_txt_strings(
    &sk,
    "_iroh.example.",
    ["node=12345", "proto=quic"],
    300, // TTL in seconds
)?;

// The bytes to send over HTTP (omit the leading public-key)
let payload = packet.to_relay_payload();

The from_txt_strings implementation (lines 41-88 in iroh-dns/src/pkarr.rs) handles DNS packet construction, Ed25519 signing, and timestamp injection automatically.

Publishing via the HTTP API

The iroh-dns-server exposes a PUT /pkarr/:key endpoint for publishing. The URL parameter :key represents the publisher's public key in z-base-32 encoding.

Request Structure

The HTTP request body must contain the pkarr signed-packet without the leading 32 public-key bytes. The server reconstructs the full packet using SignedPacket::from_relay_payload (lines 14-28 in iroh-dns-server/src/http/pkarr.rs), then verifies the signature and timestamp before storage.

Publishing via cURL


# Public key in z-base-32 (derived from the secret key)

PUBKEY=$(echo $SK | iroh key to-z32)

curl -X PUT "http://localhost:8080/pkarr/$PUBKEY" \
     -H "Content-Type: application/octet-stream" \
     --data-binary "@payload.bin"

Server-Side Storage and Caching

Published packets are managed by ZoneStore, which maintains both a persistent SignedPacketStore and an in-memory LRU cache.

Insertion Process

When a packet arrives, ZoneStore::insert performs an upsert operation: it validates the packet, updates the persistent store, clears any cached entry for that public key, and increments metrics. This logic is implemented in lines 75-95 of iroh-dns-server/src/store.rs.

Query Resolution

For DNS queries, the server checks the LRU cache first. If the packet is missing, it queries the persistent store, and finally attempts a mainline DHT fallback for global resolution. This three-tier lookup is defined in lines 97-124 of iroh-dns-server/src/store.rs.

Timestamp Monotonicity Requirements

pkarr requires strictly increasing timestamps for each publish to prevent replay attacks. iroh handles this through the Timestamp::now implementation, which guarantees monotonicity even if the system clock moves backwards.

The timestamp logic (lines 25-33 and 36-62 in iroh-dns/src/pkarr.rs) atomically tracks the last returned value, ensuring that successive calls always return a higher timestamp than previous publications from the same key.

DNS Resolution and Record Conversion

After storage, the server converts pkarr packets into standard DNS records using signed_packet_to_hickory_records_without_origin. This function (located in iroh-dns-server/src/util.rs, lines 101-145) strips the zone-origin (the public-key label) from the embedded DNS packet, producing hickory-server record sets that can be looked up by name and type.

Retrieving Published Packets

Clients can retrieve raw packets via the GET /pkarr/:key endpoint. The server returns the signed-packet payload (everything after the public-key bytes) with the MIME type application/x-pkarr-signed-packet.

Retrieving and Verifying in Rust

curl -X GET "http://localhost:8080/pkarr/$PUBKEY" \
     -H "Accept: application/x-pkarr-signed-packet" \
     -o retrieved.bin
use iroh_dns::pkarr::SignedPacket;

let pkt = SignedPacket::from_bytes(&retrieved)?;
let txts = pkt.txt_records("_iroh.example."); // → Vec<String>
println!("{:?}", txts);

The retrieval endpoint is implemented in lines 37-50 of iroh-dns-server/src/http/pkarr.rs.

Summary

  • pkarr publishing with iroh uses a signed-packet format consisting of <32 pubkey><64 sig><8 timestamp><DNS packet> to cryptographically bind DNS records to public keys.
  • The SignedPacket::from_txt_strings method in iroh-dns/src/pkarr.rs constructs and signs packets, while to_relay_payload prepares them for HTTP transmission.
  • The iroh-dns-server accepts packets via PUT /pkarr/:key, verifies them using from_relay_payload, and stores them in ZoneStore with persistent storage and LRU caching.
  • Monotonic timestamps enforced by Timestamp::now prevent replay attacks by ensuring each publication has a higher timestamp than the last.
  • DNS resolution converts stored packets into standard records via signed_packet_to_hickory_records_without_origin, with fallback to mainline DHT for global lookups.

Frequently Asked Questions

What is the exact pkarr wire format used by iroh?

The format is <32 pubkey><64 sig><8 timestamp><DNS packet>, as defined in iroh-dns/src/pkarr.rs. The 32-byte public key identifies the publisher, the 64-byte Ed25519 signature authenticates the content, and the 8-byte timestamp ensures freshness. This structure allows self-certified DNS records without certificate authorities.

How does iroh prevent old pkarr packets from being replayed?

iroh enforces strictly monotonic timestamps using the Timestamp::now implementation. Even if the system clock moves backwards, the timestamp generator atomically tracks the last returned value and guarantees each new timestamp is higher than the previous one. The server rejects packets with timestamps equal to or lower than the stored version.

What happens when a DNS query hits the iroh-dns-server?

The server performs a three-tier lookup: first checking the in-memory LRU cache, then the persistent SignedPacketStore, and finally falling back to the mainline DHT for globally distributed records. Once found, signed_packet_to_hickory_records_without_origin converts the packet into standard DNS records by stripping the public-key origin label.

Can I retrieve a raw pkarr packet after publishing?

Yes. The GET /pkarr/:key endpoint returns the signed-packet payload (excluding the public-key bytes) with MIME type application/x-pkarr-signed-packet. Clients can then verify and parse this using SignedPacket::from_bytes to extract TXT records and other DNS data without trusting the server.

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 →