Name and KID Resolution API
Crate: kinetic-daemon Stage: 9 Reading time: 15 minutes Depends on: docs/learn/core/01_overview.md, docs/learn/network/01_overview.md
What Is This?
This module (kinetic-daemon/src/api/resolve.rs) defines the REST API endpoints that the Kinetic daemon uses to resolve human-readable .kin names and Kinetic Identifiers (KIDs). It acts as the HTTP bridge between standard web clients and the underlying Kinetic peer-to-peer network.
Instead of forcing a web application or mobile app to implement the complex Kinetic DHT protocol, this module allows them to simply make standard HTTP GET requests to a running Kinetic daemon. The daemon takes the query, translates it into a network lookup, waits for the peer responses, and returns clean JSON.
The primary function of this module is data translation and network abstraction. It hides the complexity of:
- Cryptographic hash routing.
- Peer discovery and connection management.
- Protocol timeouts and retry logic.
- Data validation and authorization checks.
To the outside world, querying a name like saif.kin looks exactly like querying a traditional centralized web API. But under the hood, this module is orchestrating a decentralized search across the DHT and applying specialized fallback logic. Without this API, any interaction with the Kinetic network would require raw socket programming and binary packet parsing. This file abstracts away all of those networking primitives, providing a seamless HTTP layer on top of the P2P infrastructure.
Why Kinetic Needs This
The Kinetic network operates on cryptographic proofs, byte slices, and Distributed Hash Table (DHT) routing. Standard web browsers, frontend React applications, and command-line tools like curl do not speak “Kinetic Network Protocol”—they speak HTTP and expect JSON data.
If this module did not exist, the developer experience for building on Kinetic would be miserable. Every application that wanted to resolve saif.kin or look up a user’s KID would need to:
- Bundle the entire Kinetic networking stack.
- Manage long-lived connection pools to DHT peers.
- Implement complex asynchronous polling mechanisms.
- Parse raw binary payloads into structured data.
When building a modern web application, frontend developers are used to calling straightforward endpoints and getting back a JSON blob. They do not want to negotiate connections with random peers across the globe or compute cryptographic hashes just to see who owns a name.
By providing this REST abstraction, the Kinetic daemon allows the network to be easily integrated into:
- Traditional web frontends (React, Vue, Svelte) via standard
fetch()calls. - Backend services written in other languages (Node.js, Python, Go) that need to query identity data.
- Simple shell scripts using
curlfor automation or monitoring.
Furthermore, this module acts as a critical safety net for data retention. The DHT is a distributed system. Data is stored on volatile peers who can go offline at any time. Records can occasionally drop or propagate slowly due to network partitions.
The resolution API implements a crucial local fallback mechanism. If the network says a name doesn’t exist, but the daemon has a local backup cache of that name on its disk, it serves the local copy. This rescues users who might have momentarily lost connectivity or whose DHT records expired before their daemon could successfully republish them. Without this specific file, temporary DHT instability would result in hard application failures.
How It Works
This file exposes two primary Axum route handlers. They both operate asynchronously and interact closely with the network and storage components provided via the ApiState.
1. Resolving a .kin Name
When a user requests a name resolution, the handle_resolve_name function is triggered. This is the entry point for turning a string like saif.kin into a full NameRecord JSON object.
-> See: kinetic-daemon/src/api/resolve.rs — Lines 19 to 83
Step 1: Normalization
The requested name is passed through kinetic_core::types::normalize_name. This ensures that capitalization differences (e.g., SAIF.kin vs saif.kin) do not cause hash mismatches on the DHT.
Step 2: The DHT Lookup
The daemon asks the network layer to resolve the fully qualified domain name (FQDN) using state.network.resolve_redundant_payload(&fqdn). This function sends lookup queries out to connected peers, hunting for the node closest to the hash of the requested name. This is an asynchronous operation that blocks the HTTP request until the network responds.
Step 3: Payload Parsing
If the network peers return the requested bytes, the daemon attempts to parse the payload as a kinetic_core::types::NameRecord using serde_json::from_slice.
- If parsing succeeds, the record is wrapped in an Axum
Jsonresponse and returned to the caller with an HTTP 200 OK status. - If parsing fails, it means the network returned corrupted data, and an HTTP 500 error is thrown.
Step 4: The Local Fallback Rescue
If the network layer returns a ResolutionError::NotFound, the daemon does not immediately return a 404 error to the HTTP client. Instead, it checks its local state.storage database.
- It constructs a lookup key using
DB_PREFIX_REVEAL + fqdn. - If a cached record is found locally, it means the user previously registered or revealed this name using this specific daemon instance.
- The daemon serves the local backup and logs a message:
Recovered {} from local daemon storage backup!. - This local fallback prevents total failure if the DHT drops the record.
Step 5: Error Mapping
If the user’s network is disconnected, the function intercepts ResolutionError::Offline and maps it to a 503 Service Unavailable response, letting the frontend know to try again when the connection is restored.
Step 6: Constructing the Final HTTP Response Once all operations are complete, whether they succeed or fail, this handler converts the internal Kinetic network outcome into a clean HTTP response.
- Successes return an Axum
Jsonresponse, which serializes the Rust struct into a standard JSON string. - Errors are mapped to specific status codes (e.g.,
404 Not Foundor500 Internal Server Error) and return a JSON payload containing an{"error": ...}message. - This ensures that frontend developers consuming this API do not need to understand Rust
Resulttypes; they simply parse standard REST errors.
2. Resolving a KID and its Manifest
When an application needs to look up the public keys or capabilities associated with a Kinetic Identifier (KID), the handle_resolve_kid function is triggered.
-> See: kinetic-daemon/src/api/resolve.rs — Lines 90 to 150
Step 1: Fetching the KID Payload
Similar to name resolution, the daemon queries the DHT for the raw payload associated with the did (the KID string). It applies the same error mapping for NotFound and Offline scenarios.
Step 2: Legacy Support Parsing
The daemon attempts to parse the returned payload as an AuthorizedKid (the modern network standard, which wraps the document in cryptographic signatures).
- If this fails, the code includes a fallback mechanism: it tries to parse it as a raw
KidDocument. - This allows backward compatibility with older identity data structures that might still exist on the network.
Step 3: Calculating the Manifest Key A KID often has an associated “Manifest” that defines its capabilities, service endpoints, or linked data. This manifest is stored separately on the DHT to keep the core KID document lightweight.
- The daemon calculates the DHT key for this manifest by taking the SHA256 hash of the string
"{did}#manifest". - It encodes this hash into a hex string to query the network.
Step 4: Stitching the Response The daemon then makes a second, independent DHT lookup for the manifest key.
- If the manifest is found, the daemon seamlessly stitches it into the final JSON response under the
manifest_documentkey. - If the manifest is not found, it simply omits it, returning only the
kid_documentwithout throwing any HTTP error. The frontend receives a unified JSON object containing everything the network knows about that identity.
Key Pieces
handle_resolve_name
- Location:
api/resolve.rs— Lines 19-83 - Role: The Axum HTTP handler for resolving human-readable names.
- Why it matters: It implements the vital DHT-to-local-storage fallback logic. Without this function, naming on Kinetic would be unstable during network churn, and lightweight web clients would not be able to interact with
.kinnames easily.
handle_resolve_kid
- Location:
api/resolve.rs— Lines 90-150 - Role: The Axum HTTP handler for resolving a user’s decentralized identifier (KID).
- Why it matters: It abstracts away the complexity of resolving a KID and its associated capability manifest. It executes multiple DHT lookups and aggregates the results into a single clean JSON response, reducing the workload for application developers.
Axum Extractor: State<ApiState>
- Location: Function signatures (e.g., Line 20)
- Role: Safely pulls the shared daemon context into the isolated request handler.
- Why it matters: This is the idiomatic way Axum handles concurrency and dependency injection. It allows the HTTP handlers to remain stateless themselves, while still accessing the persistent daemon infrastructure (like references to the network pool and the local database). It guarantees thread-safe access without manual locking in the handler.
Axum Extractor: Path<String>
- Location: Function signatures (e.g., Line 21)
- Role: Automatically extracts the dynamic portion of the URL directly into a local Rust variable.
- Why it matters: If a user queries
/name/saif.kin, this extractor automatically pulls"saif.kin"into thenamevariable. It prevents the need for manual URL parsing or regex matching, making the routing code exceptionally clean and error-free.
The Fallback Key Formatting
- Location:
api/resolve.rs— Line 38 - Role:
format!("{}{}", kinetic_core::constants::DB_PREFIX_REVEAL, fqdn) - Why it matters: This precise string formatting determines where the daemon looks for emergency backups. It relies on the local storage system maintaining a mirrored cache of data the user has revealed to the network.
How This Connects to the Rest of Kinetic
- CROSS-CRATE:
kinetic_core::types::NameRecord— Defined in thecorecrate, this is the exact data structure the API expects to retrieve from the DHT and serialize into the final HTTP JSON response. - CROSS-CRATE:
kinetic_core::error::ResolutionError— Used by this module to determine if a failure was due to the network beingOfflineor if the record was simplyNotFound. The handler translates these internal Rust errors into standardized HTTP status codes. - CROSS-CRATE:
kinetic_kid::KidDocumentandCapabilityManifest— Defined in thekidcrate, these define the identity schema. This module is responsible for fetching these raw bytes from the network and parsing them into these structured types. - CROSS-CRATE:
ApiState— The shared state structure that holdsstate.network(for DHT interactions) andstate.storage(for local database fallbacks).
Quick Reference
- Name Lookup Flow:
- Query the DHT via
resolve_redundant_payload. - If successful, parse and return the
NameRecord. - If not found, check the local daemon database using
DB_PREFIX_REVEAL + name. - Return the local backup if it exists, otherwise return a 404.
- Query the DHT via
- KID Lookup Flow:
- Resolve the core identity document first.
- Independently hash
did#manifestusing SHA256. - Attempt to resolve and append the manifest to the response JSON.
- Response Format: Returns standard Axum
Json<T>which automatically serializes the Rust structs and sets the correctContent-Type: application/jsonheaders for the browser. - HTTP Error Mapping:
404 Not Found: The requested name or KID does not exist on the DHT and is not cached locally.503 Service Unavailable: The daemon is fully disconnected from the Kinetic peer-to-peer network.500 Internal Server Error: The DHT returned data, but the byte payload was corrupted or failed to parse into the expected JSON structure.
Open Questions / Things to Revisit
- Manifest Lookup Performance: The
handle_resolve_kidfunction blocks on a second network lookup if it tries to resolve the capability manifest. Since these two items (the KID and the Manifest) are separate records on the DHT, could these two network queries be spawned concurrently usingtokio::join!to reduce the overall API latency? - Legacy Structure Fallbacks: The code handles falling back to parse raw
KidDocumentandCapabilityManifestbyte payloads if the modernAuthorizedKidparsing fails. At some point in the network’s lifecycle, this backwards compatibility might need to be removed to enforce strict cryptographic authorization wrappers on all records. - Storage Redundancy: The local fallback relies on
DB_PREFIX_REVEAL. If the local storage is cleared, or if the daemon is running in a fresh state without historical caches, the fallback cannot rescue a failed DHT lookup. Is there a need for a secondary fallback mechanism?