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


title: CapabilityManifest & ServiceEntry crate: kinetic-kid stage: 3 reading_time: 15 mins depends_on: 01_did.md, 02_document.md

What Is This?

The CapabilityManifest is an optional, modular addition to a Kinetic Identity Document (KID). While the main KID document acts as your core identity and cryptographic root of trust, the manifest functions as your decentralized public bulletin board. It provides a structured, standardized way to advertise services associated with your identity. For example, you might list a personal website, a REST API endpoint, a decentralized storage gateway, or a messaging inbox. The network relies on DID identifiers which are effectively just cryptographic hashes. These hashes alone cannot tell a peer how to dial a WebSocket or where to make an HTTP request. The CapabilityManifest closes this gap by securely associating networking routing data with the mathematical identity. By storing this in a completely separate manifest structure, you actively avoid bloating or needlessly rotating your primary identity document. The ServiceEntry struct represents the individual, addressable services listed within this array.

Why Kinetic Needs This

In a decentralized network like Kinetic, users and nodes must have a standard, interoperable way to broadcast: “Here is exactly how you can contact me across the network” or “Here is precisely where my external data lives.” If we decided to put all of this volatile, fast-changing routing information directly into the core KidDocument, we would immediately face significant architectural and performance drawbacks. Any minor operational change—such as simply updating a domain name, changing an API port, or rotating cloud providers— would brutally force the user to issue a full identity rotation and generate a completely new core document revision. This process would unnecessarily stress the network’s consensus mechanisms, flood gossiping channels, and incur computational overhead. Without a separate manifest, Kinetic would either have to enforce rigid, unchangeable service definitions, or force users to abandon their long-term identities whenever their web hosting provider changes. Both options are terrible for user experience.

By strictly separating the CapabilityManifest from the KidDocument, we achieve a highly modular and robust architecture. The core identity remains inherently small, static, unchanging, and maximally secure. Simultaneously, the manifest effectively handles the dynamic, fast-changing, messy reality of real-world service endpoints. Crucially, the manifest remains cryptographically bound to the core identity at all times. It absolutely must be signed by one of the authorized controller keys explicitly listed in the corresponding KidDocument. This strict separation of concerns ultimately ensures that your core identity remains unconditionally stable, while your practical network capabilities remain highly flexible, scalable, and remarkably easy to update.

How It Works

The complete lifecycle workflow for creating, parsing, and verifying a manifest involves several distinct, rigorous steps:

  1. Definition of Services: The process naturally begins by building an ordered list of ServiceEntry objects. Each discrete entry explicitly specifies a unique fragment ID (like "#website"), a broad service category classification, the underlying transport protocol required to connect, and the actual, fully-qualified string endpoint URI. -> See: crates/kinetic-kid/src/manifest.rs — Lines 9 to 21

  2. Constructing the Manifest: These individual service entries are then securely bundled together into a single CapabilityManifest record. This overarching structure includes vital metadata parameters such as an incrementing version number and strict validity time periods. These specific fields are essential to proactively prevent replay attacks and intelligently manage the lifecycle of the advertised endpoints. -> See: crates/kinetic-kid/src/manifest.rs — Lines 29 to 49

  3. Canonicalization: Before applying any post-quantum cryptographic signature, the manifest must be deterministically converted into bytes. The canonicalize() method uses JCS (JSON Canonicalization Scheme) to strictly serialize the entire manifest. It intentionally, necessarily, and completely omits the signature field (since it is mathematically impossible to sign an empty signature). -> See: crates/kinetic-kid/src/manifest.rs — Lines 51 to 63

  4. Signing: The authentic document owner securely uses a valid ML-DSA-65 private key to sign the canonicalized bytes. This specific private key must correspond perfectly to a public key already authorized in their root KidDocument. We programmatically prepend a specific, hardcoded context prefix (b"kinetic-manifest-v1\0") directly to the message bytes. This aggressive prefixing strategy explicitly prevents devastating cross-protocol signature reuse attacks across the network. -> See: crates/kinetic-kid/src/manifest.rs — Lines 138 to 152

  5. Verification: When an arbitrary peer node receives the gossiped manifest, they must thoroughly verify it against the publisher’s root KidDocument. The internal verify() method runs the data through an incredibly rigorous gauntlet of uncompromising security checks. First, it firmly ensures that the manifest’s declared kid strictly matches the parent document’s decentralized identifier (DID). Next, it meticulously evaluates time constraints, guaranteeing the manifest isn’t currently expired based on local time. It also verifies that its valid_from start time isn’t impossibly far in the future, guarding against malicious clock skew injection. Then, it firmly imposes strict mathematical size bounds: a strict maximum of 50 services, and very tight byte-length limits on all internal strings. This specific defense mechanism actively prevents memory exhaustion attacks where a malicious peer intentionally uploads a gigabyte-sized manifest. Finally, it properly decodes the Base64url signature and exhaustively checks it mathematically against every valid ML-DSA-65 controller key listed in the core document. -> See: crates/kinetic-kid/src/manifest.rs — Lines 75 to 136

Key Pieces

ServiceEntry

What it does: Completely represents a single, independently addressable way to interact with the owner’s identity over the network. Where it is: crates/kinetic-kid/src/manifest.rs — Line 11 Why it matters: It elegantly structures volatile endpoint data cleanly and predictably for the rest of the application. Fields like id purposefully allow referencing specific services unambiguously via fragments. The protocol and endpoint fields directly provide the actionable routing information necessary for executing peer-to-peer connections. This explicit structural separation completely prevents ambiguous, error-prone parsing logic across different network node implementations.

CapabilityManifest

What it does: The primary root parent structure directly holding the dynamic array of services, temporal validity bounds, and the final digital signature. Where it is: crates/kinetic-kid/src/manifest.rs — Line 30 Why it matters: It purposefully acts as the definitive cryptographic envelope for decentralized network capability communication. The doc_type string strongly ensures parsers are correctly interpreting the right schema version before attempting deserialization. The monotonically increasing version integer gracefully allows seamless conflict resolution if multiple overlapping manifests are ever found gossiping in the network. The critical valid_from and expires_at fields directly provide essential temporal scoping constraints. This strict temporal scoping is absolutely critical for long-term security in a loosely connected distributed network, as it mathematically ensures that old, outdated, or maliciously compromised routing data cannot continually resurface.

CapabilityManifest::canonicalize

What it does: Securely converts the struct into a deterministically ordered, fully predictable JSON string, specifically skipping the empty signature field. Where it is: crates/kinetic-kid/src/manifest.rs — Line 57 Why it matters: All modern cryptographic signature algorithms fundamentally require an exact, byte-for-byte matching input array. Standard, naive JSON serializers will frequently reorder dictionary keys arbitrarily depending on compiler-specific internal hash map layouts. Arbitrarily reordered keys would instantly invalidate the signature mathematically for any downstream receiving peer. Using the serde_jcs crate safely enforces strict, standardized alphabetical ordering to completely eliminate this issue.

CapabilityManifest::verify

What it does: Thoroughly, safely, and completely validates all internal parameters of the manifest explicitly against its parent KidDocument. Where it is: crates/kinetic-kid/src/manifest.rs — Line 75 Why it matters: This specific method effectively represents the absolute primary defense line against invalid, malformed, or blatantly malicious data ingestion. It is actively designed to be highly paranoid by default, trusting absolutely nothing. It tightly enforces arbitrary, hardcoded limits on total authorized key counts and embedded array lengths. It meticulously checks for excessive system clock skew to proactively prevent complex time manipulation network attacks. It validates all struct string sizes individually to aggressively guard against buffer overflows or intentional memory bloat attempts. Ultimately, it securely maps the provided signature bytes back to the core identity keys for cryptographic authorization validation.

How This Connects to the Rest of Kinetic

FORWARD DEPENDENCY: The CapabilityManifest architecture is inherently, intentionally, and irrevocably linked to the foundational KidDocument. You simply cannot cryptographically verify a manifest without first successfully fetching, parsing, and verifying the corresponding parent document. The parent document strictly acts as the sole cryptographic source of truth for the necessary controller_keys required for validation.

CROSS-CRATE: In the broader ecosystem of the entire Kinetic network (such as the Kademlia DHT implementation or peering protocol layers), participating nodes will constantly, asynchronously, and frequently gossip these manifests among themselves. The underlying network transport layer relies incredibly heavily on the strict structural bounds enforced here. The hard 50 service limit and precise string constraints confidently ensure that gossiped network payloads will perpetually and comfortably fit within standard, acceptable UDP/TCP MTU (Maximum Transmission Unit) sizes. They also formally guarantee highly predictable, bounded memory usage across all network peers, protecting small constrained nodes from malicious exhaustion.

Quick Reference

-> See RUST_CONCEPTS.md for explanations of:

  • web_time::SystemTime (used for WASM compatibility)
  • #[serde(skip_serializing_if = "Option::is_none")] (used to omit empty optional fields from the canonical JSON)
  • #[serde(deserialize_with = "crate::bounded::deserialize_max_50")] (used to prevent memory exhaustion attacks by capping service arrays)

Open Questions / Things to Revisit

  • The internal time skew allowance during timestamp validation is currently hardcoded to exactly 300 seconds (5 minutes).
  • Is a 300 second threshold sufficiently robust for highly distributed global nodes with potentially wildly drifting hardware clocks?
  • Currently, the ML-DSA-65 signature verification iteratively loops linearly over all valid controller_keys in the document.
  • If a primary document has 20 unique keys, and the manifest happens to be signed by the 20th key, verification does a massive amount of unnecessary cryptographic math.
  • Should we strongly consider including a lightweight key_id hint parameter directly in the manifest to natively optimize this lookup time?
  • We presently only verify specific MlDsa65 key variants during the rigorous verification step.
  • If Kinetic successfully adds new post-quantum signature schemes later, this specific verify block will immediately need substantial algorithmic modification or abstract trait abstraction.
  • The b"kinetic-manifest-v1\0" context byte string is currently hardcoded inline within the signing methods.
  • It would definitively be significantly better architecture to formally move this to a shared, public constant inside src/constants.rs.
  • Centralizing this critical context string would definitively prevent accidental copy-paste typos in future refactoring efforts.