Crate: kinetic-kid
Stage: 02
Reading Time: 25-30 minutes
Depends On: 01_did.md (Concept of KineticDid)
What Is This?
The KidDocument is the core cryptographic root of identity in the Kinetic network.
It is a highly structured data format, heavily inspired by the W3C Decentralized Identifier (DID) specification.
It mathematically binds a did:kin:<hash> identifier to a specific, rotating set of public keys.
Defined entirely within document.rs, it acts as the self-certifying anchor for all user interactions.
It proves, without relying on any centralized server, that “I am the owner of this identity.”
It also proves “these are my current keys, and here is how you can verify my signatures.”
Unlike traditional X.509 certificates used on the web, a KidDocument does not require a Certificate Authority to issue or validate it.
Every single node in the Kinetic network can independently and deterministically verify the authenticity of this document.
This forms the foundational layer for all peer-to-peer trust, messaging, and access control in the Kinetic ecosystem.
It allows individuals, devices, and automated agents to prove who they are with absolute mathematical certainty.
Without this core structure, the network would have to fall back on vulnerable centralized registries.
Instead, identity is sovereign, portable, and entirely controlled by the user.
Why Kinetic Needs This
In a truly decentralized, peer-to-peer network like Kinetic, there is absolutely no central database.
There is no root certificate authority, and no centralized user registry to look up who a user is.
There is no centralized service to look up what their current public key is.
When Alice wants to send a secure message to Bob, she needs his exact, current keys.
When Alice wants to authorize a sensitive transaction on the network, the network nodes need her keys to verify it.
Peers need a rigorous way to verify identity independently and deterministically without trusting a middleman or corporate server.
Kinetic solves this fundamental problem by making the identity document explicitly self-certifying.
The KidDocument contains the ML-DSA-65 post-quantum public keys required to verify cryptographic signatures.
Because the DID identifier itself is derived directly from a one-way cryptographic hash of the document’s genesis key, anyone can verify it.
Anyone can fetch the document from the network.
Anyone can hash the first key themselves.
Anyone can check that it perfectly matches the DID URI embedded in the document.
This mathematically proves that the document is authentic and originally created by the owner of that key.
Furthermore, digital identities are not static entities; they must evolve.
Users lose mobile devices, and hardware security tokens get compromised or stolen.
Network capabilities, service endpoints, and routing requirements change constantly over time.
The document provides a standardized, machine-readable format to dynamically list authorized keys.
It provides a standardized way to point to off-chain capability manifests for heavier profile data.
It provides an extremely robust mechanism to facilitate secure key rotation and recovery.
All of this is done without ever changing the underlying, permanent did:kin identifier.
This allows a user’s identity to persist securely through compromises, hardware changes, and protocol upgrades.
Important
It fundamentally ensures that the kinetic network maintains incredibly high security standards against future post-quantum computing threats. This guarantees that Kinetic identities will remain mathematically secure for decades.
How It Works
The KidDocument lifecycle involves several distinct operational phases.
Each phase heavily relies on strict cryptographic checks and bounds limits to maintain trust in a trustless environment.
Let’s break down each crucial step of how the document functions under the hood.
1. Document Structure and Bounds
#![allow(unused)]
fn main() {
Option<T>
crate::bounded::deserialize_max_20
}
The document is a JSON-serializable struct containing metadata, authorized keys, and signatures.
-> See: kinetic-kid/src/document.rs — Lines 38 to 63
-> See RUST_CONCEPTS.md for Option<T> (used here for optional fields like signature and ManifestPointer).
Warning
To prevent Denial of Service (DoS) memory exhaustion attacks, we use custom bounded deserialization (
crate::bounded::deserialize_max_20). This ensures vectors never exceed 20 items at the parsing stage, rejecting inflated payloads before they reach expensive cryptographic logic.
2. Canonicalization (Determinism)
#![allow(unused)]
fn main() {
self.canonicalize()
serde_jcs
}
Before we can cryptographically sign the document, we must have a perfectly predictable representation of its bytes.
If a JSON serializer adds a single extra space somewhere in the payload, the entire hash changes.
If it uses different indentation or line endings, the hash changes completely.
If it reorders the dictionary keys arbitrarily, the resulting hash will be radically different.
Any of these tiny variations will cause the cryptographic signature verification to completely fail.
-> See: kinetic-kid/src/document.rs — Lines 65 to 77
The canonicalize() method takes an immutable reference to the document to prepare it for signing or verification.
First, it clones the struct to create a temporary, mutable copy in local memory.
Next, it explicitly strips out the signature field by aggressively setting it to None.
This is logically required because you mathematically cannot include a signature in the very payload that is currently being signed.
Then, it uses serde_jcs (JSON Canonicalization Scheme, RFC 8785) to turn the struct into a highly deterministic JSON string.
This strict canonicalization scheme guarantees totally predictable byte output regardless of the system environment.
Whether the document was originally generated in Rust, a browser running TypeScript, or a backend Go service, the byte representation will match perfectly.
The final bytes fed into the ML-DSA-65 signing algorithm will be exactly identical bit-for-bit across all operating systems and platforms.
This prevents incredibly subtle cross-language compatibility bugs from accidentally breaking network consensus.
This exact methodology is what allows disparate, heterogeneous clients to all agree on the validity of a single identity.
3. Verification (Stateless check)
#![allow(unused)]
fn main() {
verify()
.ok_or(KidError::MissingSignature)?
kinetic-kid-v1\0
.is_ok()
Ok(())
}
When a Kinetic node receives a new KidDocument over the wire, the absolute first thing it does is a stateless verification.
This is the front-line, high-speed defense against invalid or intentionally malicious data.
-> See: kinetic-kid/src/document.rs — Lines 79 to 170
The verify() method is termed purely “stateless” because it absolutely does not look at any network history.
It does not check any ledger, DHT state, or blockchain state to figure out if the document is allowed.
It only analyzes the exact bytes of the document currently loaded in memory in front of it.
It performs a rigorous sequence of checks to ensure the document is internally self-consistent.
Warning
First, it performs extensive bounded fields validation on all internal collections. It explicitly checks that there aren’t too many controller keys. It checks that there aren’t too many revocation keys or manifest URLs. It carefully checks that string lengths do not exceed strict memory limits (e.g., public keys must be smaller than
LIMITS_KID_MAX_PUBLIC_KEY_BYTES).
Next, it performs robust signature extraction and decoding.
It safely extracts the base64url-encoded signature using the idiomatic Rust pattern .ok_or(KidError::MissingSignature)?.
It then decodes the base64url string into raw binary bytes.
It parses those raw bytes into a strongly typed ML-DSA-65 signature object native to the ml-dsa crate.
If the bytes are malformed, it throws an InvalidSignature error immediately.
Next, it reconstructs the original signed payload.
It calls self.canonicalize() to get the deterministic JSON string representation.
It deliberately prepends a kinetic-kid-v1\0 prefix to the JSON bytes before verification.
This domain-separation prefix is an absolutely critical protocol security feature.
It aggressively prevents cross-protocol signature reuse attacks.
It totally stops an attacker from tricking a user into signing a DID document when the user genuinely thought they were signing a private chat message.
Finally, it runs the core cryptographic verification loop.
It iterates sequentially through the controller_keys list.
If the document is marked as deactivated, it instead iterates sequentially through the revocation_keys list.
The closure leverages .is_ok() to gracefully and silently ignore any keys that fail verification without crashing.
It successfully returns Ok(()) the very moment any valid signature match is found in the authorized list.
Notice carefully that this stateless method does not check if the did:kin string matches the public keys.
This is an extremely intentional design decision that very often confuses new developers reading the code.
Because keys can and must be rotated over the lifetime of a long-lived identity, the keys will drift and change.
A perfectly valid, completely current document in year 5 might not contain the original genesis key from year 1 anymore.
This strict separation of stateless structure checks and stateful logic is core to Kinetic’s architecture.
4. Genesis Binding (The Anchor)
#![allow(unused)]
fn main() {
verify_genesis()
did:kin:<hex(SHA256(primary_key_bytes))>
}
If verify() intentionally doesn’t check the DID string, how do we know a DID belongs to this document in the first place?
How do we mathematically stop Alice from simply claiming Bob’s DID by uploading a document?
This is exactly where the genesis check acts as the foundational root anchor for the entire network.
-> See: kinetic-kid/src/document.rs — Lines 172 to 211
The verify_genesis() method is the absolute anchor of the whole decentralized identity system.
It is strictly called only once when the document is first published to the network.
It extracts the very first key in controller_keys, which is permanently designated as the primary genesis key.
It hashes its raw public bytes using the industry-standard SHA-256 cryptographic algorithm.
It systematically formats the resulting 32-byte digest as a lowercase hex string.
Finally, it rigorously verifies that the resulting string exactly matches the suffix of the did:kin identifier in the document.
Important
This creates an unbreakable, mathematical binding at the very moment of creation. You cannot claim or squat on a DID unless you demonstrably possess the private key that generates the exact public key that hashes to that exact DID. Because SHA-256 is universally collision-resistant, it is practically impossible to find two different keys that hash to the exact same DID.
This brilliantly ensures a globally unique identity namespace without relying on any central registrar or DNS server. Once the genesis check passes, the DID is irrevocably bound to that user’s cryptosystem forever.
5. Authorized Updates (The Chain of Trust)
#![allow(unused)]
fn main() {
is_authorized_update(&self, previous_doc)
.iter().any(...)
}
When Alice wants to selectively rotate her keys because she bought a new laptop, she publishes a new KidDocument.
This totally new document contains the newly generated key in the controller_keys list.
How does the peer network know this isn’t an active attacker trying to hijack her DID by publishing a fake update?
-> See: kinetic-kid/src/document.rs — Lines 213 to 261
The is_authorized_update(&self, previous_doc) method checks the new document’s signature against the authorized keys stored in the old, proven document (previous_doc).
-> See RUST_CONCEPTS.md for Closures & Iterators (used here via .iter().any(...) to efficiently verify the signature).
This guarantees that the new document was signed by an entity already recognized as an authorized controller in the previous state, maintaining an unbroken chain of trust back to the genesis key.
Key Pieces
This section deeply breaks down the core data structures and methods that define the identity subsystem. These are the foundational building blocks you will interact with most frequently when working on Kinetic identity features.
KidDocument (Struct)
#![allow(unused)]
fn main() {
KidDocument
}
-> See: kinetic-kid/src/document.rs — Line 38
This is the primary main identity container that wholly defines a Kinetic user.
It safely holds the immutable DID string identifier for the user.
It conservatively holds the Unix creation timestamp to formally establish the document’s age.
It contains the strictly ordered lists of controller and revocation keys.
It cleanly holds the Base64url signature that definitively authenticates the entire package.
It is the absolutely fundamental data model actively passed around the Kinetic peer-to-peer network to establish digital identity.
The vital deactivated boolean flag is also located here.
It explicitly marks an identity as permanently burnt or compromised, permanently disabling standard signing operations forever.
ControllerKey (Struct)
#![allow(unused)]
fn main() {
ControllerKey
"ML-DSA-65"
}
-> See: kinetic-kid/src/document.rs — Line 9
This crucial struct tightly represents a specific public key authorized to act on behalf of the DID.
It explicitly includes an id field, which is a fragment URI like #key-1 heavily used for relative referencing within the document itself.
It includes the key_type field, which currently must absolutely always be set to the exact string "ML-DSA-65".
It securely includes the raw Base64url-encoded public key bytes that will actively be used for mathematical verification.
ManifestPointer (Struct)
#![allow(unused)]
fn main() {
ManifestPointer
CapabilityManifest
}
-> See: kinetic-kid/src/document.rs — Line 21
Identities in the Kinetic network aren’t just used for basic signing; they possess vast network capabilities.
They have complex profile data, avatars, and custom network routing endpoints.
To fiercely keep the base KidDocument extremely small and cheap to verify across the network, these heavy operational details are removed.
They are completely pushed out to a separate, much larger CapabilityManifest document.
This struct safely provides a secure cryptographic hash of that external manifest payload.
It also provides a reliable array of network URLs (like HTTPS or IPFS) to securely locate and download that off-chain manifest data.
By deliberately separating the lightweight identity anchor from the heavy capability manifest, we ensure the core identity layer remains incredibly fast and cheap.
verify() (Method)
#![allow(unused)]
fn main() {
verify()
self.deactivated
revocation_keys
}
-> See: kinetic-kid/src/document.rs — Line 87
This critically important method performs the rigorous internal consistency check of the document structure and signature.
Note
Crucial note for Saif: Pay particularly close attention to exactly how this method checks the
self.deactivatedflag. If the document is officially marked as revoked, standard controller keys are immediately stripped of their signing authority. From that exact point forward, only dedicated, high-securityrevocation_keyscan authorize any further operations or final updates. This intelligently ensures that if your daily-driver device is totally compromised, the attacker still cannot maliciously un-revoke the identity.
verify_genesis() (Method)
#![allow(unused)]
fn main() {
verify_genesis()
}
-> See: kinetic-kid/src/document.rs — Line 188
This fiercely enforces the mandatory, one-time initial binding of the DID string to the precise SHA-256 hash of the primary key.
This mathematically prevents “squatting” on DIDs by early adopters or attackers.
It absolutely guarantees that you cannot claim an identity string that rightfully belongs to another user on the network.
is_authorized_update() (Method)
#![allow(unused)]
fn main() {
is_authorized_update()
}
-> See: kinetic-kid/src/document.rs — Line 222
This is the absolute most structurally crucial method for all complex state transition logic in Kinetic.
It ensures that any and all changes to an identity’s keys or manifest are cryptographically authorized by the immediately previous state of that identity.
It forms an unbroken, verifiable chain of trust spanning all the way back to the original genesis key.
sign() (Method)
#![allow(unused)]
fn main() {
sign()
SigningKey
}
-> See: kinetic-kid/src/document.rs — Line 268
This is a highly developer-friendly utility designed to seamlessly sign the document during initial creation or later update.
It fully automates the complex JCS canonicalization of the struct.
It rapidly signs the resulting bytes using the provided ML-DSA-65 SigningKey.
It then neatly injects the resulting encoded Base64url string back into the struct’s signature field.
It conveniently returns the fully signed, ready-to-publish document directly to the caller.
How This Connects to the Rest of Kinetic
This file absolutely does not exist in a vacuum; it has profound and critical dependencies across the broader Kinetic codebase. CROSS-CRATE DEPENDENCIES:
kinetic-did: The vitalkidfield internally uses the robustKineticDidstruct from thekinetic-didcrate. Thedocument.rsmodule safely assumes that the provided string has already been strictly validated as a structurally correctdid:kinURI at parsing time. This avoids performing redundant string regex validations during heavy cryptographic operations.ml-dsa: This entire crate heavily relies on the post-quantumml-dsacrate for absolutely all cryptographic operations. It specifically utilizes standard cryptographic traits likeSignerandVerifierfrom the broader Rust crypto ecosystem. These specific traits define the generic, highly standardized interface for digital signatures in Rust. This smart abstraction fundamentally allows us to seamlessly swap algorithms in the future if a critical vulnerability in ML-DSA is ever discovered.- Storage Layer (Forward Dependency): The stateful
is_authorized_updatemethod strongly implies a complex future storage layer is being built somewhere else. This could eventually be a Distributed Hash Table (DHT), a global verified blockchain, or a local verified database. That external storage layer is entirely responsible for securely fetching the proven canonicalprevious_docand passing it directly to this method. TheKidDocumentitself is purely functional and purely stateless; it absolutely does not possess the network context to know how to fetch previous states from remote peers.
Quick Reference
Here is a highly scannable, rapid summary of the strict rules enforced by KidDocument:
- Algorithms Supported: Exclusively ML-DSA-65 (NIST Post-Quantum standard).
- Max Keys Limits: Hardcoded protocol safety limit of exactly 20 controller keys.
- Max Revocation Limits: Hardcoded protocol safety limit of exactly 20 revocation keys.
- Max Location Limits: Hardcoded protocol safety limit of exactly 20 manifest locations.
- Prefix Isolation: Signed data is always forcibly prepended with
kinetic-kid-v1\0to absolutely prevent replay attacks across entirely different cryptographic subsystems. - Genesis Binding Formula:
did:kin:<hex(SHA256(primary_key_bytes))> - Serialization Standard: RFC 8785 JSON Canonicalization Scheme (JCS).
- Encoding: All binary keys and raw signatures are strictly Base64url encoded with NO padding characters whatsoever.
Open Questions / Things to Revisit
The current KidDocument implementation is incredibly robust, but there are several rough edges and deep architectural questions that critically need to be revisited as the network radically scales.
- Algorithm Agility Implementation: The current code heavily hardcodes
"ML-DSA-65"string checks widely throughout the entire module. If we ever urgently need to migrate to a totally new post-quantum standard, we will need to introduce massive algorithm agility here. We will have to rigorously handle versioning extremely carefully to thoroughly prevent catastrophic downgrade attacks where an attacker forces a weak algorithm. This will likely require a v2 document schema to cleanly implement without breaking existing nodes. - Key Rotation Race Conditions: If a user rapidly publishes two perfectly valid, conflicting updates to their document simultaneously, they might accidentally branch the entire chain of trust. The external storage and consensus layers will desperately need strict monotonic ordering or robust conflict resolution rules to precisely determine which specific update is mathematically canonical. Without this ordering, the network could split geographically on which key is the true controller.
Warning
- Manifest Fetching Guarantees: The document securely points to a manifest via URLs, but absolutely does not actively fetch it itself. We critically need to strongly ensure that the external network layer that actually fetches the manifest strictly verifies the downloaded payload against the exact
hashprovided inManifestPointer. If the network layer accidentally forgets to check this hash, the entire critical separation of identity and capabilities is fatally compromised, opening the door to devastating spoofing attacks.
- Revocation Semantics: What exactly mathematically happens to historical signatures and capabilities permanently after a document is formally deactivated? The higher application layer needs exceptionally clear rules on whether past historical signatures remain definitively valid if a key is much later revoked. We must urgently decide if revocation retroactively invalidates all past data, or only prevents future new signatures from being formed. There is currently no explicit timestamp signaling when a revocation formally occurred.
- Key Expiry: Currently, the defined controller keys have absolutely no expiration date natively embedded in them.
We may need to rigorously evaluate whether controller keys should optionally support intrinsic
valid_untilUnix timestamps. This feature would automatically and systematically force regular key rotation best practices for all users across the entire network, significantly reducing the blast radius of old leaked keys. - State Proofs: As the continuous chain of document updates grows significantly over years, verifying a single identity from genesis to present could become extremely computationally expensive. We may actively need to aggressively investigate SNARKs, STARKs, or other zero-knowledge state proofs to radically compress this massive verification chain in the near future. This will become extremely relevant for light clients running on constrained mobile devices.
- Error Granularity: The
KidError::TooManyKeyserror variant is heavily and sloppily overloaded in theverify()method. It is bizarrely returned for too many keys, too many URLs, and even raw string lengths exceeding protocol limits. We should absolutely, urgently split this into much more explicitly granular error types (e.g.,MaxUrlsExceeded,PublicKeyTooLong,TooManyControllerKeys). This granular split will significantly aid in debugging rejected documents for downstream external developers actively building on Kinetic. If they get a vague error, they will waste hours blindly guessing what limit they violated.