kinetic-types — Stage 1, Topic 02: Error Taxonomy & Crate Entry Point
Crate: kinetic-types
Stage: 1 of N
Estimated reading time: 12 minutes
Depends on: Topic 01 (KineticTime / clock subsystem) — helpful but not required
What Is This?
error.rs defines Severity — a four-level enum that every domain error type in Kinetic
uses to classify how bad a problem is. It is the single source of truth for log filtering,
UI alert levels, and automated node decision-making across the entire workspace.
lib.rs is the crate’s front door. It declares every subsystem module — clock, dns,
name_record, governance, identity, proxy, cdn, vdf — and documents what the crate is for.
Together these two files establish the crate’s contract with the rest of Kinetic.
-> See: kinetic-types/src/error.rs — Lines 1 to 34
-> See: kinetic-types/src/lib.rs — Lines 1 to 29
Why Kinetic Needs This
Kinetic is a distributed network. At any given moment a node is processing DNS lookups, verifying VDF proofs, handling governance votes, streaming CDN chunks, and maintaining heartbeat liveness proofs — all concurrently, all of which can fail in different ways.
Without a shared severity taxonomy, every subsystem would invent its own language for “this is bad.” One module might log a string “fatal”, another might return an integer 2, a third might panic. When a node’s supervisor loop — the code that decides whether to retry an operation, send an alert, or halt cleanly — reads these results, it has no common vocabulary to reason with. It cannot distinguish a transient DNS cache miss (retry in 5s) from a cryptographic signature forgery (halt and alert the operator immediately).
Severity gives the whole network one answer to the question: how urgently does this need
a response? Because it lives in kinetic-types — the zero-dependency type hub — every
crate that imports kinetic-types gets access to it without pulling in consensus engines,
networking stacks, or any heavy machinery.
lib.rs matters for a complementary reason: it is the declaration that kinetic-types
exists as a coherent unit. Without it, Rust does not know that clock, dns, error,
and the other modules belong to the same crate. Every use kinetic_types::error::Severity
statement anywhere in the workspace resolves through the pub mod error; line in lib.rs.
How It Works
The Severity Ladder (error.rs Lines 13-22)
Severity is an enum with exactly four variants, ordered from least to most urgent:
Info — Something happened and it is fine. A DNS record was fetched from cache. A
heartbeat arrived on schedule. No action is needed by the caller. Nodes log these at trace
level and move on.
Warning — Something is degraded but not broken. A VDF proof took longer than expected.
A DNS record is close to its TTL expiry. The correct response is to monitor, maybe retry,
but not to alert anyone. The node can continue normal operation.
Error — A specific operation failed and the caller must handle it. A name record
signature did not verify. A CDN chunk was unavailable. This needs attention from the client
or from the node’s retry logic, but it does not threaten the node’s overall health.
Caution
Critical— A protocol-level, cryptographic, or safety violation occurred. A governance message was signed with a revoked key. A VDF reveal did not match its committed value. A replay-protection counter went backwards. When a node seesCritical, it should stop trusting the source of that error and possibly alert the operator. Continuing blindly past a Critical severity is how security vulnerabilities propagate through distributed systems.
The ladder is intentional. Kinetic is designed so that node operators — and the automated
supervisor loops that monitor node health — can filter on Severity without reading the
full error message. A monitoring dashboard can count Critical events in a time window and
page an operator when that count crosses a threshold, without understanding anything about
VDF math or governance opcodes.
How Domain Errors Plug Into Severity
Every domain-specific error enum in Kinetic — DnsError, GovernanceError, VdfError,
and others — implements a severity() method that returns a Severity variant for each
of its own variants. The signature looks like this:
#![allow(unused)]
fn main() {
fn severity(&self) -> Severity { ... }
}
This means a caller can always ask any Kinetic error “how bad are you?” without knowing
what kind of error it is. The Severity type is the bridge between domain-specific error
detail and network-wide operational policy. The same supervisor loop code can handle a
failed DNS lookup, a VDF rejection, and a bad governance signature — because they all speak
the same severity language.
FORWARD DEPENDENCY: DnsError, GovernanceError, VdfError and their severity() methods
are defined in other modules of kinetic-types and documented in later topics. When you
read those files, look for how each variant maps to a Severity level — the mapping is a
design decision that encodes protocol intent directly into the type system.
The Display Implementation (error.rs Lines 24-33)
Severity implements std::fmt::Display, which means when you write {severity} in a
format string or log statement, Rust calls this code and prints INFO, WARNING, ERROR,
or CRITICAL. This is not automatic — Rust does not know how to turn your enum into a
human-readable string unless you explicitly implement it.
-> See: kinetic-types/src/error.rs — Lines 24 to 33
The implementation uses a match block that exhaustively covers all four variants. Rust
enforces exhaustiveness at compile time: if someone adds a fifth variant to Severity and
forgets to update the Display implementation, the build fails with a clear error. This
makes the type system itself a safety net for protocol evolution — you cannot silently omit
a case.
The uppercase string format ("CRITICAL" not "Critical") is a deliberate convention. It
matches standard syslog severity formats and makes Kinetic log output grep-friendly. A
DevOps engineer monitoring Kinetic nodes with standard log tooling will instantly recognize
the format.
The Derive Macro Line (error.rs Line 12)
The line above the enum definition reads:
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
}
This single annotation instructs the Rust compiler to automatically generate implementations of eight different traits.
-> See RUST_CONCEPTS.md for a full breakdown of #[derive(...)].
The Crate Entry Point (lib.rs Lines 20-28)
-> See: kinetic-types/src/lib.rs — Lines 20 to 28
These nine pub mod declarations are Rust’s way of saying “these source files are part of
this crate and their public items are accessible from outside.” The keyword pub before
mod means other crates in the workspace — kinetic-kid, kinetic-verify, the browser
extension bridge — can import from these modules. A mod without pub would be private,
visible only within kinetic-types itself.
The module list is also an architectural statement: every concern in Kinetic that produces
shared data types has its own module. Clock lives separately from DNS. DNS lives separately
from governance. This separation means that kinetic-kid can import only
kinetic_types::vdf::VdfProof without being forced to compile or depend on DNS type code.
Rust compiles only what you use, and module boundaries make that precise.
Zero-Dependency Philosophy (lib.rs Lines 1-6)
Important
The
lib.rsdoc comment callskinetic-typesa “zero-dependency, lightweight type hub.” This is a load-bearing architectural decision, not a style preference. Any crate in the workspace that defines or consumes shared types — including offline tools, browser extensions, hardware wallets — must be able to importkinetic-types. Ifkinetic-typesdepended on a networking library, every offline tool would be forced to compile that library too. If it depended on a consensus engine, it could never run in a browser WebAssembly context where networking is sandboxed.
The only external dependencies kinetic-types may carry are serialization (serde) and
cryptographic primitives — things so fundamental that every possible consumer needs them
anyway. This is why Severity derives Serialize and Deserialize from serde rather
than implementing a custom wire format: serde is already a cost every consumer accepts.
Key Pieces
Severity — kinetic-types/src/error.rs Lines 13-22
The four-level classification enum. Every error in the Kinetic system answers to this type.
Choosing the right variant for an error is a protocol design decision: it tells operators
and automated systems how to respond without requiring them to understand the domain. Getting
this wrong — tagging a signature forgery as Warning instead of Critical — is a
security decision embedded in code, invisible until something goes wrong in production.
impl std::fmt::Display for Severity — kinetic-types/src/error.rs Lines 24-33
Converts a Severity variant to an uppercase string for logs and error messages. The match
is exhaustive — Rust will refuse to compile if a variant is missing from the arm list. The
uppercase format matches syslog conventions for grep-friendly log tooling.
pub mod error; — kinetic-types/src/lib.rs Line 23
The one line that makes Severity importable as kinetic_types::error::Severity from
anywhere in the workspace. Without this declaration, error.rs would be an orphaned file
that the compiler ignores entirely.
Nine pub mod declarations — kinetic-types/src/lib.rs Lines 20-28
The module declarations that constitute the crate’s public surface. Each one corresponds to
a source file or directory under kinetic-types/src/. The ordering roughly follows
dependency direction: error is declared before governance because governance errors
reference Severity.
How This Connects to the Rest of Kinetic
Every other crate in the workspace that can produce an error uses Severity from this
module. That makes kinetic-types::error the most imported error module in the codebase.
It is infrastructure as fundamental as the clock subsystem — arguably more so, because
errors happen everywhere.
CROSS-CRATE: Severity — this is the canonical definition. All other crates import it from
here. It has no definition anywhere else in the workspace.
FORWARD DEPENDENCY: kinetic-kid — the node identity and key management crate — will
import Severity to classify key-loading and signature errors. When a KID (Key Identifier)
cannot be loaded, that error’s severity() method will return a variant defined right here.
FORWARD DEPENDENCY: kinetic-verify — the proof verification engine — uses Severity to
signal whether a failed VDF proof is a transient computation error (Warning) or a
deliberate submission of an invalid proof (Critical). The distinction matters because a
Critical here triggers blacklisting logic at the network layer, not just a retry.
FORWARD DEPENDENCY: The browser extension proxy (proxy module declared in lib.rs Line 26)
serializes errors over an IPC channel back to the browser. Because Severity derives
Serialize/Deserialize, the browser receives a structured severity level it can use to
display the right UI alert color without parsing a raw string.
FORWARD DEPENDENCY: The governance module (governance, lib.rs Line 24) produces
GovernanceError variants that each carry a Severity. A governance message with an
invalid binary opcode gets Warning; a message with a forged signature gets Critical.
Node operators can configure severity-gated alerting rules that fire on Critical
governance errors specifically, independently of other error streams.
Quick Reference
| Item | File | Lines | One-line purpose |
|---|---|---|---|
Severity enum | error.rs | 13-22 | Four-level error classification |
Severity::Info | error.rs | 15 | Benign — no action needed |
Severity::Warning | error.rs | 17 | Transient — retry or monitor |
Severity::Error | error.rs | 19 | Operation failed — caller must handle |
Severity::Critical | error.rs | 21 | Safety/crypto violation — halt or alert |
Display for Severity | error.rs | 24-33 | Converts to uppercase string for logs |
#[derive(...)] on Severity | error.rs | 12 | Enables debug, copy, compare, serialize |
pub mod error; | lib.rs | 23 | Exposes error module to workspace |
Nine pub mod declarations | lib.rs | 20-28 | All subsystem modules declared public |
| Zero-dependency rule | lib.rs | 1-6 | No heavy crates — stays WASM-compatible |
Severity decision flow for node supervisors:
| Severity | Decision Flow |
|---|---|
Info | log at trace level, continue normally |
Warning | log, schedule a retry or set a monitor interval, continue |
Error | log, return error to caller, let caller’s policy decide |
Critical | log at error level, alert operator, consider blacklisting the source |
Open Questions / Things to Revisit
1. No severity ordering trait.
Severity does not implement PartialOrd or Ord, so you cannot write
if severity >= Severity::Error. A supervisor loop that wants to handle “anything Error or
worse” must use a match arm or a helper function. The ordering is obvious (Info < Warning <
Error < Critical) and the omission may be intentional — forcing explicit match arms prevents
accidentally skipping a case — but it is worth revisiting if supervisor logic grows verbose.
2. No KineticError trait definition.
error.rs describes (in comments) that domain errors implement a severity() method, but
there is no Rust trait that formally requires this. Each domain error type adds severity()
by convention, not by compiler enforcement. A future trait KineticError { fn severity(&self) -> Severity; } would make this a compile-time guarantee rather than a code review concern.
3. Wire format for Severity is unspecified.
Serialize/Deserialize on Severity means serde knows how to handle it, but the actual
wire format (JSON string “Critical”, integer, MessagePack byte) depends on the serializer
each subsystem chooses. If the proxy subsystem uses JSON and the VDF subsystem uses
MessagePack, a Severity crossing that boundary may need a translation step. Worth
confirming all subsystems agree on a single encoding.
4. lib.rs module order is not documented as intentional.
The nine pub mod declarations follow a rough dependency order (error before governance,
which uses it) but this is not documented anywhere in the file. If a new subsystem is added,
the author needs to know where to insert it. A short comment in lib.rs explaining the
ordering rule would prevent future confusion.