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


crate: kinetic-kid stage: 4 reading_time: 15 mins depends_on: [01_did.md, 02_document.md, 03_manifest.md]

What Is This?

This file (kinetic-kid/src/error.rs) defines the comprehensive error handling system for the entire kinetic-kid crate. It introduces the KidError enum, which strictly categorizes every single way that parsing, validating, or verifying a Kinetic Identity Document (KID) or a Capability Manifest can fail. Instead of using generic Rust errors or raw strings, Kinetic defines 16 highly specific failure modes with stable protocol error codes. The file also provides automated error conversion rules for third-party libraries (like serde_json and base64). Finally, it maps these protocol-level errors to appropriate security severity levels, ensuring the network daemon knows how to react to failures. This is the single source of truth for all identity-related failures in the Kinetic ecosystem. When anything breaks in kinetic-kid, it returns a KidError.

Why Kinetic Needs This

In a decentralized peer-to-peer network, error handling is not just about developer debugging; it is a critical security boundary. When a Kinetic node receives a KID document from an untrusted peer on the network, it must process it carefully and deterministically. If the document is invalid, the node needs to know exactly why it is invalid before taking action.

Consider the difference in intent between two failures. If a document fails because a JSON bracket is missing or a timestamp field is misspelled, that might just indicate a buggy client or an outdated peer broadcasting malformed data. But if a document fails because the cryptographic signature is actively invalid (InvalidSignature), or it contains too many keys (TooManyKeys), that changes things entirely. That failure could indicate an active Denial of Service (DoS) attack designed to exhaust node memory. It could be an attempted cryptographic forgery. It could be a malicious actor trying to hijack an identity they do not own. By explicitly categorizing errors into 16 distinct variants, Kinetic can safely apply different severities to different failures, protecting the node.

Furthermore, we need stable error codes (like KIN-KID-015). Cross-language clients (like an Android app, a web dashboard, or a third-party wallet application) must reliably handle network rejections. They cannot rely on parsing fragile error strings, which might change in a future Rust update. A stable error code allows the frontend application to immediately show a localized, user-friendly message. It also allows automated bots to trigger specific recovery flows, relying on a guaranteed, unchanging API contract that bridges the Rust backend and external systems.

How It Works

The error system is built around the thiserror crate, which is the standard ecosystem pattern for defining custom library errors in Rust. Let’s break down how the file is structured and how the different features interlock to provide a robust boundary.

1. The KidError Enum Definition

-> See: kinetic-kid/src/error.rs — Lines 4 to 56

The core of the file is the pub enum KidError definition. -> See RUST_CONCEPTS.md for an explanation of the #[derive(Error)] macro (from the thiserror crate).

The #[error("...")] attributes attached to each variant define exactly how the error should be formatted when converted to a string. This is what shows up in the node’s server logs or terminal output when something fails.

The 16 variants are carefully designed to cover every possible failure in the lifecycle of a KID:

  • Formatting & Parsing:
    • InvalidDidPrefix, InvalidDidFormat, InvalidDidHexLength, InvalidDidHexCharacters.
    • These ensure the DID string strictly matches our required format before the CPU even attempts expensive cryptographic operations. It acts as a cheap, early filter.
  • Serialization & Encoding:
    • JsonParseError, CanonicalizationError, Base64Error.
    • These handle the mechanics of moving between raw network bytes, base64url strings, and structured JSON objects in memory.
  • Cryptography & Security:
    • InvalidSignature, MissingSignature, KeyParseError, UnauthorizedManifestSignature.
    • These represent core security failures where ML-DSA-65 post-quantum verification fails entirely, or the keys themselves are completely malformed.
  • Protocol Bounds & State:
    • TooManyKeys (which acts as DoS protection preventing massive JSON files from crashing the parser).
    • InvalidValidFrom, ManifestExpired (which handle time-based validity failures, ensuring old manifests cannot be replayed).
  • Identity Lifecycle & Authority:
    • DidKeyMismatch (genesis binding failure when a new identity is first created, ensuring the DID hash matches the key).
    • UnauthorizedKidUpdate (update authorization failure when modifying an existing document without permission).

2. Automatic Error Conversion (The ? Operator Magic)

-> See: kinetic-kid/src/error.rs — Lines 58 to 68

-> See RUST_CONCEPTS.md for an explanation of the ? operator and impl From<T> for U.

This standard trait implementation keeps the main validation logic clean by automatically translating serde_json::Error and base64::DecodeError into our custom KidError variants.

3. Stable Protocol Codes

-> See: kinetic-kid/src/error.rs — Lines 81 to 100

The code() method uses a match statement to assign a hardcoded, static string prefix to every single variant of the enum. -> See RUST_CONCEPTS.md for an explanation of match and exhaustive pattern matching.

4. Severity Mapping for Network Operations

-> See: kinetic-kid/src/error.rs — Lines 107 to 117

The severity() method maps errors into either Severity::Warning or Severity::Error. Notice the specific syntax used here: Self::InvalidSignature | Self::UnauthorizedManifestSignature | ... => Severity::Error. This is advanced pattern matching at work, grouping multiple conditions together into a single logical branch. It declares “if the internal error state matches any of these specific, security-critical variants, return an Error severity level.” For absolutely everything else (which is handled by the _ => wildcard catch-all at the very bottom), it gracefully returns a Warning. This explicit separation is vital for building a resilient, autonomous network architecture. It allows the higher-level network layers to easily distinguish between a harmless client typo (which just needs a retry) and a serious cryptographic breach (which requires immediate isolation). Without this, the node would have to parse strings to guess if the error was dangerous.

Key Pieces

The KidError Enum

  • What it does: The central error registry containing 16 specific failure modes for identity operations.
  • File/Line: kinetic-kid/src/error.rs — Lines 4-56
  • Why it matters: It forces the developer to explicitly account for every specific way identity validation can fail, rather than relying on lazy, generic “it broke” strings that provide absolutely no context.

The impl From<T> for U Trait Implementations

  • What it does: Standard Rust trait implementations that automatically convert external, third-party errors into internal KidError types.
  • File/Line: kinetic-kid/src/error.rs — Lines 58-68
  • Why it matters: This trait is the hidden machinery that powers the ergonomic ? operator. It makes the core validation logic concise by completely hiding the error translation boilerplate.

The Severity Enum

  • What it does: A simple binary categorization enum (Warning, Error) used to flag the danger level of KidError variants.
  • File/Line: kinetic-kid/src/error.rs — Lines 70-77
  • Why it matters: Provides an immediate, high-level signal to the caller (like the networking daemon or the peer-to-peer layer) about whether an error represents a malicious security threat or just malformed data.

The KidError::code() Method

  • What it does: Returns a static string prefix like “KIN-KID-015” deterministically based on the specific error variant.
  • File/Line: kinetic-kid/src/error.rs — Lines 81-100
  • Why it matters: These alphanumeric codes form the permanent API contract with external clients. They are guaranteed not to change, ensuring frontends do not unexpectedly break even if the internal Rust logic completely shifts.

How This Connects to the Rest of Kinetic

CROSS-CRATE: kinetic-network and kinetic-node When the kinetic-node or kinetic-daemon receives a gossip message containing a KID document from another peer, it will invoke the validation functions found elsewhere in this crate. Those functions will invariably return a Result<(), KidError>. The node will then inspect the error’s severity by evaluating the output of err.severity(). If it encounters a Severity::Error (like InvalidSignature or TooManyKeys), the node instantly knows the peer sending the document is either severely compromised or actively malicious. In response, the network layer can immediately drop the peer TCP connection and heavily penalize their network reputation score. This aggressive pruning protects the local node from further abuse and isolates bad actors. Conversely, if it receives a Severity::Warning (like a JsonParseError), it might just ignore the message and send a soft rejection. It assumes the peer is merely running outdated software or experiencing a bit flip, rather than acting maliciously.

FORWARD DEPENDENCY: JSON-RPC and REST API Error Responses The error_type_uri() and user_message() methods are specifically designed for the JSON-RPC or REST API boundary exposed to developers. When an external application, such as a command-line interface or a web wallet, submits an invalid identity update via the Kinetic API, the daemon will catch the resulting KidError. It will then serialize these string fields into a standard RFC 7807 Problem Details JSON HTTP response. This provides the external developer with exact, actionable frontend feedback—like “The DID method-specific ID is malformed”—rather than dumping a raw, incomprehensible Rust stack trace to their console.

Quick Reference

Here are the critical stable protocol codes generated by this module and what they represent in practice:

  • KIN-KID-001 to KIN-KID-004: DID formatting failures (wrong scheme prefix, invalid hex characters, incorrect length).
  • KIN-KID-005 to KIN-KID-006: Serialization and canonicalization failures (unable to parse JSON, or unable to serialize to JCS).
  • KIN-KID-007 to KIN-KID-008: Core signature verification failures or missing signature fields entirely.
  • KIN-KID-009 to KIN-KID-010: Base64url decoding failures or ML-DSA-65 cryptographic key parsing errors.
  • KIN-KID-011: A capability manifest was explicitly signed by a key that isn’t authorized in the parent KID document.
  • KIN-KID-012: DoS protection triggered (too many keys or endpoints embedded in a single document, exceeding hard memory bounds).
  • KIN-KID-013 to KIN-KID-014: Time-based failures (the manifest valid_from timestamp is in the future, or the manifest has already expired).
  • KIN-KID-015: Genesis binding failure (the DID hash does not mathematically match the primary controller key at creation time).
  • KIN-KID-016: Unauthorized identity update (the new document version was not signed by a key specifically authorized in the previous version).

Open Questions / Things to Revisit

  • Retry Logic and Transience:
    • Currently, the is_retryable() method always strictly returns false (Line 120).
    • This behavior makes perfect sense for strict cryptographic validation (if a signature is mathematically bad, trying it again won’t magically fix it).
    • However, as the Kinetic network evolves, will we ever introduce transient KID errors into the system?
    • For example, if validating a document eventually requires checking a decentralized revocation registry that might be temporarily unreachable, we might genuinely need a distinct Retryable error state to instruct the client to try again later without permanently failing the operation.
  • Extensibility for Cryptographic Agility:
    • If we add new signature schemes (like an Ed25519 fallback) in the future, we might need significantly more granular key parsing errors.
    • Currently, the KeyParseError variant assumes an ML-DSA-65 post-quantum failure.
    • As we expand, we may need to distinguish between ML-DSA failures, Ed25519 failures, and ECDSA failures explicitly for better developer debugging.
  • Validation Granularity in Parsing:
    • Should we attempt to expose the exact JSON field that failed parsing in the JsonParseError variant?
    • Currently, the error completely relies on serde_json’s raw string output.
    • While this is helpful for backend developers reading logs, it is not always perfectly structured for programmatic frontend handling where a UI might want to highlight a specific invalid text input box.