antithesis-sdk-rust
Rust SDK for the Antithesis autonomous testing platform: define test-property assertions, request platform-controlled randomness, and mark lifecycle milestones for fault-injection runs.
Repository Health
Technical Analysis
antithesis_sdk is the official Rust client for the Antithesis autonomous software testing platform. It gives Rust programs three kinds of hooks into that platform: assertion macros (always!, sometimes!, assert_unreachable!, and related guidance macros) that define test properties evaluated against a triage report, randomness functions that ask Antithesis for structured or unstructured random values instead of seeding a local PRNG, and lifecycle functions (setup_complete, send_event) that tell the Antithesis environment when initialization has finished and fault injection can begin.
The crate is designed to work identically whether or not it is actually running inside Antithesis. A layered handler chain tries to load the native Antithesis instrumentation library first, falls back to writing JSONL output to a local file when ANTITHESIS_SDK_LOCAL_OUTPUT is set, and falls back again to a no-op handler when neither is available — so the same assertion macros can stay in production code paths and regular test suites without requiring the Antithesis runtime. Disabling the crate’s full feature strips out the catalog-registration machinery entirely, compiling assertions down to just their condition/details evaluation with no instrumentation overhead.
What You Get
- Assertion macros —
always!,sometimes!,always_or_unreachable!,assert_unreachable!, and reachability variants that register a named test property once and re-emit it on each pass/fail transition. - A compile-time assertion catalog — macros register themselves into a
linkmedistributed slice at compile time, so every assertion in the binary is known to Antithesis even if the code path is never executed. - Structured and unstructured randomness —
random::get_random()andrandom::random_choice()pull values from the Antithesis platform’s controlled randomness instead of a local RNG, plus anAntithesisRngadapter that plugs into therand0.8/0.9/0.10 ecosystems via feature flags. - Lifecycle signaling —
lifecycle::setup_complete()tells Antithesis when the system under test has finished initializing (triggering a snapshot and fault injection), andlifecycle::send_event()emits structured log events for later triage. - A three-tier output handler — native Voidstar library, local JSONL file (
ANTITHESIS_SDK_LOCAL_OUTPUT), or silent no-op, selected automatically at startup with no configuration required. - Feature-gated footprint — the
fullfeature (on by default) can be disabled to compile assertions down to bare condition evaluation with zero catalog or handler overhead.
Common Use Cases
- Defining test properties for autonomous fuzzing — a Rust service under Antithesis testing declares
always!(condition, "message")at key invariants so the platform’s fault-injecting fuzzer can search for inputs that violate them. - Running the same assertions in normal CI — because the macros work outside the Antithesis environment (writing to a local file or doing nothing), teams reuse the same assertion calls in
cargo testwithout a separate assertion library. - Coordinating fault-injection timing — a distributed system calls
lifecycle::setup_complete()once all nodes have finished bootstrapping so Antithesis knows exactly when to start injecting faults, rather than guessing from wall-clock time. - Structured randomness for deterministic replay — a workload generator calls
random::get_random()at each decision point instead of seedingrand::thread_rng(), so Antithesis can control and reproduce the exact sequence of decisions across simulation runs. - Debugging triage reports with custom events —
lifecycle::send_event("name", &details)drops structured JSON breadcrumbs that show up in the Antithesis triage report timeline when a bug is found.
Under The Hood
Architecture
The crate is organized around three public modules — assert, random, and lifecycle — that all funnel output through a single internal dispatch point (internal::dispatch_output) backed by a lazily-initialized, trait-object handler (LibHandler) chosen at first use: a native Voidstar shared-library handler is tried first, falling back to a local-file JSONL handler when ANTITHESIS_SDK_LOCAL_OUTPUT is set, and finally a no-op handler. Assertion macros use linkme::distributed_slice to register an AssertionCatalogInfo entry into a compile-time-assembled catalog (ANTITHESIS_CATALOG) the first time each call site is compiled, and a TrackingInfo atomic pass/fail counter per assertion ID ensures only the first pass and first fail of each named assertion are actually re-emitted after the initial catalog registration. Disabling the full feature compiles the entire catalog/handler/tracking machinery away via #[cfg(feature = "full")], leaving only condition evaluation — a deliberate two-tier build that keeps the crate usable as a near-zero-cost dependency.
Tech Stack
The crate targets Rust 2021 edition with a minimum supported version of 1.62.1. Core dependencies are serde/serde_json for the JSON event protocol, once_cell for lazy statics, libloading and libc for dynamically loading the native Voidstar instrumentation library, and linkme for compile-time distributed-slice registration of the assertion catalog. Randomness integrates with the rand ecosystem through three mutually-selectable feature flags (rand_v0_8, rand_v0_9, rand_v0_10), each pulling in the matching rand_core version so AntithesisRng can implement the right trait (RngCore for 0.8/0.9, TryRng for 0.10) without forcing a single rand version on downstream consumers. A companion simple/ crate in the same repo exercises the SDK as a runnable example.
Code Quality
The library ships inline #[cfg(test)] mod tests blocks in assert/mod.rs and lifecycle.rs covering TrackingInfo counters and AssertionInfo construction, plus a separate lib/tests/ integration-test directory with ten scenario files (assertion guidance variants, event sending, SDK info, setup-complete with/without details). Public functions and macros carry substantial doc comments with runnable doctest examples showing expected JSONL output. A GitHub Actions ci.yml workflow runs on pushes; error handling favors silent-but-documented failure (output errors from the local-file handler are deliberately swallowed, with the rationale spelled out in a code comment) over panics, appropriate for a library that must never crash the host program it’s instrumenting.
API Design
The public surface is small and consistent: three modules, a prelude re-export for one-line imports, and a single antithesis_init() entry point recommended for main(). Macros use Rust’s compile-time const message requirement to catch a whole class of misuse (non-literal assertion messages) at build time rather than at runtime, and the fallback-to-no-op design means adopting the SDK never requires an if antithesis_enabled conditional in application code. The main ergonomic cost is the full vs. minimal feature split and the three parallel rand_v0_* flags, which require consumers to understand Antithesis’s own versioning story rather than just calling a function.