# Understanding the iroh Node Data Model: PKARR Zones and DNS Authority

> Explore the iroh node data model and discover how NodeZoneHandler merges static DNS records with mutable PKARR zones, establishing DNS authority for public-key naming.

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

---

**The iroh node data model centers on `NodeZoneHandler`, a struct that combines static DNS records with mutable PKARR zones to serve as a DNS authority for public-key-based naming.**

In the `n0-computer/iroh` repository, the node's data model defines how the system stores, retrieves, and serves DNS records. Rather than a traditional file-based zone configuration, the iroh node implements a hybrid approach that merges immutable static records with dynamic public-key addressable records stored in a `ZoneStore`.

## Core Components of the iroh Node Data Model

The data model is implemented primarily in the `iroh-dns-server` crate, where the `NodeZoneHandler` struct acts as the single source of truth for DNS resolution.

### NodeZoneHandler as the Central Authority

The `NodeZoneHandler` struct in [`iroh-dns-server/src/dns/node_zone_handler.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/dns/node_zone_handler.rs) defines the complete state of an iroh node. It implements the `hickory_server::zone_handler::ZoneHandler` trait, allowing it to plug directly into a DNS server.

The struct contains five critical fields:

- **`serial: u32`** — The DNS serial number incremented on every change to signal resolvers that zone data has updated.
- **`origins: Vec<Name>`** — The list of DNS zones (origins) the node is authoritative for. At least one origin must be supplied during instantiation.
- **`static_zone_handler: InMemoryZoneHandler`** — A read-only handler providing the static portion of the zone, including SOA and NS records.
- **`zones: ZoneStore`** — The mutable storage for PKARR records, mapping public keys to their associated record sets.
- **`first_origin: LowerName`** — A cached lower-cased version of the first origin, used for the `ZoneHandler::origin` implementation.

### The ZoneStore for PKARR Records

The **ZoneStore** (defined in [`iroh-dns-server/src/store.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/store.rs)) persists the dynamic portion of the iroh node data model. Unlike traditional DNS zones that require manual editing, PKARR zones are generated dynamically from public keys. The store resolves names formatted as `<pubkey>.<origin>` to their corresponding record sets, enabling cryptographic addressability without centralized registration.

### Static Zone Handling

The **static zone** provides the foundational DNS infrastructure that rarely changes. This includes the Start of Authority (SOA) record and Name Server (NS) records. The `InMemoryZoneHandler` manages these entries separately from the mutable PKARR data, ensuring that core DNS metadata remains stable while user records update.

## How NodeZoneHandler Manages DNS Lookups

The `lookup` method in [`iroh-dns-server/src/dns/node_zone_handler.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/dns/node_zone_handler.rs) (lines 94-138) implements the resolution logic that distinguishes between static and dynamic records:

1. **SOA and NS queries** — Delegated immediately to the `static_zone_handler`.
2. **PKARR resolution** — For other record types, the method attempts to parse the queried name as a PKARR identifier using `parse_name_as_pkarr_with_origin` (from [`iroh-dns-server/src/util.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/util.rs)).
3. **Record construction** — If successful, `resolve_pkarr` builds a DNS record set that includes the node's current `serial`, ensuring cache consistency across distributed resolvers.
4. **Fallback** — Names that cannot be parsed as PKARR identifiers fall back to the static zone handler.

This architecture allows the iroh node to serve as a standards-compliant DNS server while supporting the flexible, key-based addressing scheme that defines the iroh network.

## Creating and Configuring a Node

To instantiate a node with the iroh node data model, you combine a `ZoneStore` with static records and origin definitions:

```rust
use hickory_server::authority::ZoneHandler;
use iroh_dns_server::dns::NodeZoneHandler;
use iroh_dns_server::store::ZoneStore;
use hickory_server::store::in_memory::InMemoryZoneHandler;
use hickory_proto::rr::Name;

// Define the DNS origins this node will serve
let origins = vec![
    Name::from_ascii("example.com.")?,
    Name::from_ascii("iroh.example.")?,
];

// Create the static zone with SOA/NS records
let static_zone = InMemoryZoneHandler::new(/* pre-populated static records */)?;

// Initialize the mutable PKARR storage
let zone_store = ZoneStore::new();

// Construct the node with serial starting at 1
let node = NodeZoneHandler::new(zone_store, static_zone, origins, 1)?;

```

Performing a DNS lookup against the node:

```rust
use hickory_proto::rr::{LowerName, RecordType};
use hickory_server::authority::LookupOptions;

let request_name = LowerName::from_ascii("mykey.example.com.")?;
let result = node
    .lookup(&request_name, RecordType::A, None, LookupOptions::default())
    .await;

```

Inserting PKARR records into the dynamic store:

```rust
use iroh_dns_server::util::PublicKeyBytes;

let pubkey = PublicKeyBytes::from_z32("mypublickeyz32")?;
let pkarr_set = /* build a RecordSet for the pubkey */;
node.zones.insert(pubkey, pkarr_set).await?;

```

## Key Source Files and Implementation Details

| File Path | Purpose |
|-----------|---------|
| [`iroh-dns-server/src/dns/node_zone_handler.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/dns/node_zone_handler.rs) | Contains the `NodeZoneHandler` struct definition and its `ZoneHandler` trait implementation. |
| [`iroh-dns-server/src/store.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/store.rs) | Defines the `ZoneStore` for persisting mutable PKARR zones. |
| [`iroh-dns-server/src/util.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/util.rs) | Provides helper functions including `PublicKeyBytes` and `record_set_append_origin` for PKARR name parsing. |

## Summary

- The **iroh node data model** is implemented by the `NodeZoneHandler` struct, which implements the DNS `ZoneHandler` trait.
- It combines **static zones** (SOA/NS records via `InMemoryZoneHandler`) with **dynamic PKARR zones** (stored in `ZoneStore`).
- The **`serial`** field ensures DNS cache consistency across the distributed network.
- **Origins** define the DNS domains the node controls, with at least one required for operation.
- Resolution logic prioritizes static records for infrastructure queries while dynamically generating PKARR responses for public-key-based lookups.

## Frequently Asked Questions

### What is the difference between static zones and PKARR zones in iroh?

Static zones contain traditional DNS records like SOA and NS that define the node's authority and are immutable during runtime. PKARR zones are dynamic mappings between public keys and DNS record sets, stored in the `ZoneStore`, that enable cryptographic addressability without requiring changes to the static zone configuration.

### How does the iroh node handle DNS serial numbers?

The `NodeZoneHandler` maintains a `u32` serial field that increments whenever the zone data changes. This value is included in PKARR record responses and SOA records, allowing DNS resolvers to detect when cached data has become stale and needs refreshing from the authoritative iroh node.

### Where is PKARR data stored in the iroh node?

PKARR data resides in the `ZoneStore` instance, defined in [`iroh-dns-server/src/store.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/store.rs). This store is held as the `zones` field within `NodeZoneHandler` and provides the mutable backing for public-key-to-record mappings that power the iroh naming system.

### Can an iroh node serve multiple DNS origins?

Yes. The `NodeZoneHandler` accepts a `Vec<Name>` of origins during construction, allowing a single node to be authoritative for multiple DNS domains simultaneously. The first origin in the vector is cached as `first_origin` and used as the default origin in the `ZoneHandler` implementation.