navian-dst — deterministic simulation testing for Rust.
Reproduce concurrency and crash-recovery bugs from a seed, and shrink each failure to a minimal trace. A Rust library plus a CLI, in your own cargo test — no platform, no rewrite.
$ cargo test --test catches_planted_bug ── DST FAILURE ───────────────────────── seed: 7 invariant: acked_writes_are_durable broke @ 12 minimal: CrashRestart @ step 12 ← from 40 re-run: SEED=7 cargo test catches_planted_bug ──────────────────────────────────────── $ SEED=7 cargo test --test catches_planted_bug # reproduces the exact failure, every time
Between property testing and a full simulation platform.
DST controls the clock, randomness, the network, and task order through small injected traits, then sweeps seeds. Each seed replays a different, repeatable mix of crashes, delays, and reorderings; a failing seed replays the same failure on demand, and ddmin shrinks it to a minimal failing trace.
| Where it fits | Finds | Setup | Deterministic replay | Auto-shrink |
|---|---|---|---|---|
| unit tests | logic you thought of | none | — | — |
| property-based (proptest) | input edge cases | library | seeded inputs | yes |
| fuzzing | crashes on bad input | harness + corpus | input, not schedule | partial |
| loom | memory-ordering races | rewrite under loom | exhaustive | — |
| navian-dst | concurrency + crash/recovery | inject traits | yes, from a seed | yes |
You don't wire this in by hand — the CLI and your agent do.
One binary. scan finds every source of nondeterminism and tiers it by confidence; migrate auto-wires the seams it can prove safe; and what's left ships as a machine worklist your AI coding agent — Claude Code, Codex, Cursor — finishes. CI-ready, and it never breaks your build.
$ cargo install navian-dst-cli # the tools — the `navian-dst` command $ cargo add navian-dst # the library your code will use $ navian-dst scan --deny [high DST-TIME-001] src/ledger.rs:21 SystemTime::now() [high DST-RAND-001] src/ids.rs:41 thread_rng() [med DST-ENV-001 ] src/cfg.rs:8 env::var() [adv DST-ITER-001] src/map.rs:33 HashMap order 4 leaks · 2 high 1 med 1 adv · GATE: FAIL $ navian-dst migrate # auto-wire safe seams Ledger → self.time.now_ms() + with_time() cargo check --all-targets: PASSED 3 skipped → navian-dst explain <ID>
- Install one binary — cargo install navian-dst-cli. No platform, no service to run.
- scan — tiered & CI-ready. ~30 rules across 8 categories, each with a confidence and a stable id. --deny gates on high only (so it won't fail CI on false positives); emits SARIF for GitHub code scanning, and baseline lets you turn the gate on across an existing repo.
- migrate — safe by construction. Auto-wires the seams it can prove type-safe behind a cargo check --all-targets gate that rolls back if anything won't compile. It never breaks your build; everything else is labeled "manual / agent needed."
- Your agent finishes the rest. scan --json is a machine worklist — the seam to inject, whether it's auto-fixable, a fix hint — and explain <ID> is the recipe. Point your coding agent — Claude Code, Codex, or Cursor — at that worklist and it wires the remaining seams in one pass.
- Prove it's deterministic. check runs your seeded test twice and flags any divergence — catching what escaped the seams.
Honest scope: scan is a name-based heuristic — the confidence tiers keep the CI gate free of false positives. migrate is a deliberately conservative codemod (the time family today), not a general rewriter.
Measured, not asserted.
Extracted from the harness we use to harden Navian Pulse, our real-time engine — exercised on a real system, with reproducible numbers.
Honest about scope (navian-dst)
- Determinism reaches only as far as the seams you inject — nondeterminism you don't route (a dependency's clock, OS thread order) stays outside the simulation.
- Rust, in-process, single-component — not a whole-system or multi-language platform.
- A "crash" is a modeled recovery event, not a killed OS process.
- Durability comes from your own test model — no storage simulator yet.
Start with one module and one invariant.
Add deterministic tests to a single struct — every seam you add ships to production unchanged.
← All tools · next: navian-memcheck →