Navian Open Source · module 01 · deterministic simulation testing

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 add navian-dst
Independently verified · catches the planted bug on 497/500 seeds
a failure becomes a reproducer
$ 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
Where it fits

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 fitsFindsSetupDeterministic replayAuto-shrink
unit testslogic you thought ofnone
property-based (proptest)input edge caseslibraryseeded inputsyes
fuzzingcrashes on bad inputharness + corpusinput, not schedulepartial
loommemory-ordering racesrewrite under loomexhaustive
navian-dstconcurrency + crash/recoveryinject traitsyes, from a seedyes
The CLI · navian-dst-cli

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.

find the leaks, gate CI, wire the seams
$ 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>
  1. Install one binarycargo install navian-dst-cli. No platform, no service to run.
  2. 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.
  3. 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."
  4. 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.
  5. 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.

Evidence

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.

497/500
seeds catch a planted durability bug — reproduced independently by a second agent
100k
seeded runs of a small model sweep in under a second on a laptop — the harness itself adds almost no overhead
Apache-2.0
149 tests, MSRV 1.87, CI green — on crates.io and docs.rs

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.