Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Resolution Pipeline & Record Conversion

Crate: kinetic-dns Stage: DNS (Daemon Extension) Reading time: 25 minutes Depends on: Types, Core, Daemon


What Is This?

This file contains the resolve_kinetic function, which serves as the central nervous system and the absolute core of the Kinetic DNS server. It is a massive, asynchronous pipeline that handles the end-to-end resolution of any .kin domain query. To understand what this file is, you must first understand the architectural impedance mismatch between legacy operating systems and decentralized networks. When an operating system (like Linux, macOS, or Windows) or a browser (like Chrome or Firefox) attempts to connect to a domain, it relies on the Domain Name System (DNS) to figure out which IP address to talk to. Standard DNS uses centralized servers, communicates over UDP/TCP on Port 53, and expects responses formatted according to legacy RFC standards established in the 1980s. Kinetic, on the other hand, uses a decentralized Distributed Hash Table (DHT), communicates over peer-to-peer libp2p multiplexed streams, and stores data as cryptographic JSON payloads signed with ed25519 keys. Because operating systems do not understand what a DHT is, and they definitely do not understand Kinetic’s custom cryptographic payloads, this file exists to act as an active, intelligent middleware layer. It receives a raw, legacy UDP/TCP DNS query directly from the operating system. It intercepts that query, fetches the corresponding decentralized data from the local Kinetic Daemon via an HTTP API, verifies all cryptographic signatures locally, enforces strict network security firewalls, and then repackages the decentralized data into a standard legacy DNS packet. Essentially, it allows legacy software to seamlessly interact with a next-generation decentralized network without realizing that anything has changed, bridging the gap between the old internet and the new.


Why Kinetic Needs This

To understand why this file is so large and complex, you have to understand the fundamental difference in trust models between legacy DNS and Kinetic.

In traditional DNS, trust is hierarchical and centralized. When your operating system resolver asks a server for the IP address of saif.kin, the server replies with 192.168.1.5. The operating system trusts this answer blindly because it trusts the server it asked (usually provided by your ISP or Cloudflare). There is no built-in cryptography in standard DNS (excluding DNSSEC, which is complex and centralized). In Kinetic, the network is entirely trustless. Anyone can claim to own any domain if they just broadcast it to the DHT.

Therefore, Kinetic domains are stored as a NameRecord. A NameRecord contains a DnsZone (the actual routing rules) and a cryptographic signature proving ownership.

Caution

If Kinetic simply passed the IP address from the DHT directly to the operating system, the system would be vulnerable to a massive array of catastrophic attacks. A malicious node anywhere in the world could intercept the DHT query and hand us a fake IP address. The OS would blindly accept it, and the user would be instantly phished or exploited.

This file is required because Kinetic needs a rigorous “Trust Boundary” on the local machine before any data is handed to the naive operating system. The resolve_kinetic pipeline acts as that exact boundary. It is a cryptographic bouncer for your network stack. It enforces strict, uncompromising rules before it ever allows the operating system to see a single IP address:

  1. Signature Integrity: It mathematically verifies that the domain data was actually signed by the domain owner.
  2. Identity Authentication: It links the domain’s signing key to a decentralized Kinetic Identity (KID), ensuring the owner is actively authorized to control the domain.
  3. Network Safety (SSRF): It filters out dangerous IP addresses to prevent Server-Side Request Forgery attacks against the user’s local network hardware.

Without this exact process, the Kinetic network would be fundamentally insecure and entirely unusable by standard web browsers.


The Threat Model & The Firewall

Before diving into the mechanical steps of the pipeline, it is crucial to understand the three specific threats this script is designed to neutralize. Every block of code in this file exists to counter one of these vectors.

Threat 1: Malicious DHT Nodes (Memory Exhaustion)

Caution

When this script asks the Daemon for the domain data, the Daemon fetches it from untrusted peers on the DHT. A malicious peer could respond to the DHT query with a 5-Gigabyte text file masquerading as a DNS record. If this script blindly parsed the response into memory, the DNS server would instantly crash from Out-Of-Memory (OOM) errors, taking the user’s internet connection down with it. This file mitigates this by streaming the Daemon API response in chunks and enforcing a strict 100-Kilobyte hard limit.

Threat 2: Domain Hijacking & Key Revocation

Warning

If someone steals your private key, they can sign fake DNS records for your domain. Kinetic solves this by linking domains to Decentralized Identities (KIDs). But the DHT itself does not enforce this link; this script does. By fetching the KID document from the Daemon and verifying that the key used to sign the domain is still actively listed in the controller_keys array, this script ensures that revoked keys cannot be used to hijack domains.

Threat 3: Server-Side Request Forgery (SSRF)

Caution

Because anyone can register a .kin domain, an attacker could register evil.kin and configure its A record to point to 192.168.1.1 (the default IP for most home routers). When you visit evil.kin in your browser, the browser thinks it is talking to a public website, but it is actually sending HTTP requests to your local router’s admin panel. The attacker could use JavaScript to change your router settings. This script completely eliminates this vector by silently dropping any DNS record that points to a local, loopback, or private IP address before the browser ever sees it.


The Lifecycle of a .kin Query

To see how this fits into the broader Kinetic architecture, here is the exact, chronological sequence of events when a user types saif.kin into their browser:

  1. Browser Request: The browser asks the OS networking stack for the IP address of saif.kin.
  2. OS Forwarding: The OS checks its DNS configuration (which the Kinetic app modifies to point to 127.0.0.1:53). It sends a standard UDP DNS query to the local Kinetic DNS server.
  3. Hickory Reception: The Hickory DNS server framework receives the raw UDP packet on port 53 and passes it directly to the resolve_kinetic function.
  4. Cache Lookup: The script checks the in-memory cache to see if we recently resolved this exact domain.
  5. Daemon API Request: If the cache misses, the script acts as an HTTP client and makes a GET request to the local Kinetic Daemon API (/api/resolve/saif.kin).
  6. Daemon DHT Resolution: The Daemon searches the P2P network, finds the cryptographically signed NameRecord, and returns the raw bytes to this script.
  7. Local Verification: The script deserializes the bytes and checks the ed25519 signature on the NameRecord against the current network ID.
  8. Identity Check: If the domain is configured to require E2E Identity, the script makes a second HTTP request to the Daemon (/api/resolve-kid) to verify the identity keys.
  9. Zone Extraction: The script extracts the inner DnsZone structure and looks up the specific requested subdomain (e.g., www or @).
  10. SSRF Firewall Execution: The script passes every returned IP address through the is_ssrf_safe filter.
  11. Packet Assembly: The script bundles the safe, converted records into a legacy Hickory DNS response packet.
  12. OS Delivery: The script sends the final packet back to the OS networking stack via UDP.
  13. Browser Connection: The browser receives the IP address from the OS and establishes a TCP connection to the destination.

This entire multi-layered pipeline happens in milliseconds, completely invisibly.


How It Works: Step-by-Step Mechanics

The resolve_kinetic function is essentially a massive state machine that processes the DNS query through multiple distinct phases of verification, fetching, and translation. Here is exactly how it executes.

Phase 1: Hickory Integration and Reserved Name Interception

-> See: kinetic-dns/src/kinetic_records.rs — Lines 15 to 80

The function signature takes a large number of arguments because it must interface deeply with the hickory_server framework. It receives the raw Request, a ResponseHandler to actually dispatch the final packet, a MessageResponseBuilder to construct the packet headers, and state variables like the Moka cache and the Daemon HTTP client.

The very first action the script takes is checking the requested apex_domain against a hardcoded list of PUBLIC_NAMES. Kinetic reserves certain names that should never hit the DHT network under any circumstances. The most critical of these is localhost. If the user queries localhost.kin or any nested subdomain like api.localhost.kin, the script short-circuits the entire resolution pipeline. It bypasses the cache, bypasses the Daemon API, and immediately constructs an A record pointing to 127.0.0.1 (or a AAAA record pointing to ::1). It then returns this immediately with a NOERROR response code. For any other reserved public names that aren’t localhost, it instantly returns an NXDOMAIN (Not Found) response code. This is a critical security mechanic: it ensures that system-level testing domains cannot be hijacked, spoofed, or impersonated by malicious nodes on the public DHT. It provides a guaranteed, hardcoded fallback for local application development.

Phase 2: Thundering Herd Mitigation via Moka

-> See: kinetic-dns/src/kinetic_records.rs — Lines 86 to 93

If the domain is a standard .kin name, the script leverages a moka::future::Cache. Crucially, it does not use the cache to store the final, parsed DNS records. It caches the raw bytes of the NameRecord payload. It wraps the Daemon API call inside cache.try_get_with(). This specific method is incredibly important for concurrency. If a web browser opens a page that requests 50 different assets from saif.kin simultaneously, the browser will fire 50 parallel DNS queries to the OS. If the script didn’t use try_get_with, it would fire 50 simultaneous HTTP requests to the Daemon, which would fire 50 simultaneous DHT lookups, instantly congesting the local node. By using try_get_with, the cache ensures that only the very first request actually executes the closure to hit the Daemon API. The other 49 requests instantly await the result of the first request, completely eliminating the “thundering herd” problem.

Phase 3: The API Stream and Memory Safety

-> See: kinetic-dns/src/kinetic_records.rs — Lines 94 to 126

When a cache miss occurs, the script acts as a standard HTTP client and sends a GET request to the Daemon API: /api/resolve/{apex_domain}. Notice that the script does not simply call .json() or load the entire response body into memory at once. Instead, it streams the response chunks asynchronously: while let Ok(Some(chunk)) = resp.chunk().await. As it reads the chunks from the stream, it keeps a running count of the total payload bytes. If the payload size ever exceeds 100 Kilobytes (100 * 1024 bytes), it immediately sets a limit_exceeded flag, breaks the loop, and aborts the connection. This prevents the memory exhaustion attacks detailed in the Threat Model section.

Phase 4: Cryptographic Deserialization and Integrity

-> See: kinetic-dns/src/kinetic_records.rs — Lines 131 to 146

Once the raw payload bytes are successfully fetched and the size is verified, they are deserialized back into a kinetic_core::types::NameRecord. At this exact moment, the script has a record in memory, but it does not trust it. It executes: domain_record.verify_signature(kinetic_core::constants::NETWORK_ID). This single line performs an ed25519 signature verification against the public key embedded inside the record. It proves mathematically that the data was signed by the true owner of the domain. It also ensures the signature is valid specifically for the current NETWORK_ID, which prevents replay attacks where an attacker takes a valid record from a testnet and broadcasts it on the mainnet. If the signature fails this cryptographic check, the script actively rejects it and returns a SERVFAIL (Server Failure) response code to the OS. The OS assumes the DNS server is temporarily broken, perfectly shielding the user from the tampered data.

Phase 5: The End-to-End Identity (KID) Authentication

-> See: kinetic-dns/src/kinetic_records.rs — Lines 150 to 223

This is where Kinetic’s decentralized identity system merges with standard DNS. The script parses the verified payload into a DnsZone. It then explicitly checks the apex domain (@) for a special Kinetic-only record called KID(did). If this record exists, it represents a strict rule: “This domain requires End-to-End authorization. Only the cryptographic keys actively listed in this specific KID document are legally allowed to sign this domain.”

To enforce this rule, the script suspends resolution and fires a second HTTP request to the Daemon: /api/resolve-kid/{did}. It downloads the decentralized identity document for that DID in JSON format. It then extracts the base64-encoded public_key from the NameRecord (the exact key that was just used to pass the signature check in Phase 4). It iterates through every key listed in the controller_keys array inside the KID JSON document. If the signing key is found in the array, the domain is fully authenticated. If the key is NOT found in the array, it means the identity has revoked the key, or an attacker has hijacked the domain using an obsolete key. The script immediately logs an E2E Auth Failure and aborts the resolution entirely, returning SERVFAIL.

Phase 6: Subdomain Mathematics and Routing

-> See: kinetic-dns/src/kinetic_records.rs — Lines 226 to 246

DNS queries are highly specific. The browser rarely asks for just the apex saif.kin; it frequently asks for nested subdomains like api.production.saif.kin. The script needs to figure out which string to look up in the internal DnsZone hash map. If the queried domain matches the apex domain exactly, the target subdomain is @. Otherwise, it performs exact string subtraction. It takes api.production.saif.kin, and trims .saif.kin off the end of it, leaving exactly api.production. It then checks the DnsZone map for the api.production key. If api.production does not exist in the zone, the script gracefully falls back and checks if a wildcard * record exists. This fallback allows domain owners to route all unspecified, random subdomains to a default server IP address.

Phase 7: The SSRF Firewall Execution

-> See: kinetic-dns/src/kinetic_records.rs — Lines 265 to 316

Once the script locates the correct list of records for the requested subdomain, it begins looping through them to construct the final response packet. This is where the Server-Side Request Forgery (SSRF) firewall is actively enforced. For every single A (IPv4) and AAAA (IPv6) record, the script extracts the IP address and passes it through kinetic_core::net::is_ssrf_safe. If the IP address is classified as local, loopback, link-local, or part of a private intranet subnet, the script silently drops the record entirely and logs a warning. It applies the exact same rigorous check for CNAME records. It ensures the CNAME target string does not equal localhost, does not end with .local, and does not resolve to a private IP address string. This guarantees, at an architectural level, that .kin domains can only ever route the user to public internet infrastructure.

Phase 8: Record Downgrade and Packet Assembly

-> See: kinetic-dns/src/kinetic_records.rs — Lines 326 to 391

Finally, the script must map Kinetic’s rich, custom decentralized record types into the legacy, primitive types that Hickory and the OS network stack can actually understand. Standard A, AAAA, and CNAME records are passed through directly (assuming they survived the SSRF firewall). However, Kinetic supports custom metadata records like PeerId, KID, and IPFS. Since standard DNS has absolutely no concept of a “PeerId record”, the script dynamically downgrades them into legacy TXT records. A PeerId("12345") record is converted into a standard TXT record containing the literal string "peerid=12345". An IPFS("QmHash") record is converted into a TXT record containing "ipfs=QmHash". This is a brilliant architectural bridge because it allows legacy applications (like standard web crawlers or terminal scripts) to extract Kinetic-specific decentralized metadata simply by performing a standard, ubiquitous TXT lookup on the domain.

Once all records are processed and converted, the MessageResponseBuilder compiles them into a valid byte-aligned DNS packet, sets the response code header to NOERROR, and dispatches it back to the operating system via UDP.


Anatomy of a Record Conversion

Because this file acts as a translator, it is important to understand exactly how the kinetic_core::types::DnsRecord enum variants map to Hickory’s hickory_proto::rr::Record types.

  • DnsRecord::A(ip) -> Maps to RData::A. Requires the query type to be RecordType::A. Passes through the SSRF filter.
  • DnsRecord::AAAA(ip) -> Maps to RData::AAAA. Requires the query type to be RecordType::AAAA. Passes through the SSRF filter.
  • DnsRecord::CNAME(target) -> Maps to RData::CNAME. Interestingly, CNAMEs are returned regardless of what the user asked for (A, AAAA, TXT). The OS resolver receives the CNAME and is responsible for recursively following it.
  • DnsRecord::TXT(txt) -> Maps to RData::TXT. Returned for both TXT and ANY queries.
  • DnsRecord::PeerId(pid) -> Custom Kinetic type. Maps to RData::TXT with the format peerid={pid}.
  • DnsRecord::KID(kid) -> Custom Kinetic type. Maps to RData::TXT with the format kid={kid}.
  • DnsRecord::IPFS(cid) -> Custom Kinetic type. Maps to RData::TXT with the format ipfs={cid}.

Key Pieces

Here is a structured breakdown of the most critical moving parts in this process file.

resolve_kinetic

  • What it is: The massive asynchronous function that handles the entire lifecycle of a single .kin DNS query.
  • Where it lives: kinetic-dns/src/kinetic_records.rs, Lines 15-418
  • Why it matters: It is the primary entry point for all resolution. Every single translation, security check, and network request originates here. Without it, the DNS server is just an empty shell.

The Chunking API Stream Loop

  • What it is: The while let Ok(Some(chunk)) = resp.chunk().await loop that enforces the 100KB payload limit.
  • Where it lives: kinetic-dns/src/kinetic_records.rs, Lines 99-105
  • Why it matters: It prevents network-based memory exhaustion attacks, ensuring the DNS server remains stable regardless of what malicious data the DHT serves.

The Signature Verification Block

  • What it is: The strict cryptographic enforcement call: domain_record.verify_signature(NETWORK_ID).
  • Where it lives: kinetic-dns/src/kinetic_records.rs, Lines 133-146
  • Why it matters: This enforces the fundamental security promise of the entire Kinetic network. The OS does not know how to verify the DHT; this script acts as the cryptographic bodyguard on behalf of the OS.

The KID E2E Matcher

  • What it is: The authorization logic that matches the domain’s signing key against the identity’s controller_keys array.
  • Where it lives: kinetic-dns/src/kinetic_records.rs, Lines 165-177
  • Why it matters: This is how we prove that the person who mathematically signed the domain is actually legally authorized by the KID document they are claiming to represent. It ties infrastructure directly to identity and prevents hijacking via revoked keys.

The Hickory MessageResponseBuilder

  • What it is: The struct used to construct the final outgoing DNS packet (builder.build(...)).
  • Where it lives: kinetic-dns/src/kinetic_records.rs, Lines 382-389
  • Why it matters: It formats the disparate data back into a strict byte structure that complies with decades-old internet RFCs, allowing the operating system to parse it natively.

How This Connects to the Rest of Kinetic

This file sits at the absolute edge of the Kinetic architecture, serving as the bridge to the outside world.

  • Receives from OS: It receives raw DNS queries via the external hickory_server framework.
  • Calls to Daemon: It acts as an HTTP client, making synchronous requests to the local Kinetic Daemon (/api/resolve and /api/resolve-kid). It never speaks to the P2P network directly.
  • CROSS-CRATE: NameRecord and DnsZone — defined and explained in the kinetic-core crate.
  • CROSS-CRATE: is_ssrf_safe — defined and explained in the kinetic-core/net module.

The final output of this script is completely consumed by the operating system’s DNS resolver, which then hands the translated IP addresses up to web browsers, terminal applications like curl, or any other legacy network software.


Quick Reference

When you need to remember exactly how the .kin resolution pipeline operates, here is the fast summary table:

  • Localhost Override: localhost.kin always resolves locally to 127.0.0.1. It never hits the network or the daemon API.
  • Data Source: Fetches from /api/resolve/{domain} on the local daemon. The Moka cache is checked first to prevent redundant API calls via try_get_with.
  • Size Limit: API payloads are strictly capped at 100KB to prevent memory exhaustion and buffer bloat.
  • Validation 1 (Crypto): The NameRecord signature must be mathematically valid for the current NETWORK_ID.
  • Validation 2 (Identity): If a KID record exists at the apex, the signing pubkey must exactly match one of the keys in the KID document’s controller_keys array.
  • Validation 3 (SSRF): A, AAAA, and CNAME records must not point to local, private, or loopback IP addresses.
  • Custom Records: PeerId, KID, and IPFS records are dynamically downgraded into standard TXT records so legacy applications can still read them.
  • Failures: Invalid cryptography yields SERVFAIL. Blocked SSRF attempts are silently dropped. No matching records yields NXDOMAIN.

Open Questions / Things to Revisit

  • CNAME Recursive Lookups: Currently, if a .kin domain has a CNAME record, this script returns the literal CNAME string back to the OS. The OS resolver is then responsible for doing a second lookup to resolve that CNAME into an IP address. We should verify if legacy OS resolvers correctly follow CNAMEs that point to other decentralized .kin domains, or if they drop them because they are not standard global TLDs.
  • Subdomain Edge Cases: The string manipulation used to extract subdomains (domain_name.trim_end_matches) works perfectly for standard queries, but might behave unpredictably if a malicious user queries an extremely malformed string with trailing dots, duplicate apexes, or illegal characters. We should consider using a more robust DNS label parser.
  • Cache Invalidation: The Moka cache currently holds the resolved payloads for a set duration based on time. If a user updates their domain on the DHT, the DNS server will serve stale data until that cache naturally expires. We may want the daemon to actively push cache invalidation events to the DNS server via a webhook or unix socket, rather than relying on passive time-to-live expiration.
  • API Error Handling Granularity: If the Daemon API is down or returns a 500 Internal Server Error, the script currently returns a generic SERVFAIL to the OS. We might want to differentiate between “The DHT could not find this record” and “The Daemon HTTP server crashed” in our local logging for easier debugging during local development.