Why we use proptest and Kani in kcore

posted Originally published at dev.to 2 min read

Rust’s borrow checker and type system rule out memory errors and a lot of API misuse. They do not prove that Nix snippets we emit are safe, that database invariants hold under randomised workloads, or that a path validator rejects every traversal attempt up to a bound. For those classes of bugs we add two complementary tools: proptest for property-based testing at normal cargo test speed, and Kani for bounded proofs on small, dependency-light crates.

What ordinary tests still miss
Integration tests are scenarios: they show one or a few paths work. Properties say “this must hold for every input matching a strategy” (proptest) or “for every input up to this size” (Kani). That is the difference between “we tried a few strings” and “we tried to break the assumption systematically.”

proptest: cheap, fast randomised coverage
We use proptest where we want high throughput and easy co-location with existing unit tests: gRPC validation, controller database invariants on in-memory SQLite, node registration, and similar. It is not a proof; it is a strong statistical search for counterexamples, and it runs in the same CI jobs as the rest of the suite.

Request and manifest validation paths (crates/controller/src/grpc/validation.rs and related).
Database CRUD and consistency checks (crates/controller/src/db.rs test module).
Node agent and registration flows where protobuf and identifiers need round-trip and boundary behaviour.
Kani: exhaustive within bounds, isolated in small crates
Kani model-checks Rust functions with bounded input sizes. Pointing it at the full controller binary is impractical: transitive dependencies (gRPC, TLS, SQLite, …) explode build time. We keep proofs in crates/kcore-sanitize (minimal dependencies) for security-sensitive string and path handling used when generating Nix and touching the filesystem.

Harnesses are gated so normal builds and clippy stay unchanged; CI runs Kani in dedicated jobs with pinned tool versions so results are reproducible across merges.

Why both, not one or the other
proptest scales to large modules and finds real bugs quickly with little setup. Kani upgrades specific pure functions from “we didn’t find a counterexample in N random tries” to “no counterexample exists within these bounds” — the right trade-off for injection and path-safety code where a single missed edge case is unacceptable. Together with TLA+ at the protocol level, they form a layered story: design models, then implementation properties, then proofs on the hottest pure code.

What this means in CI
Property tests run with the normal Rust test matrix. Kani runs under the formal verification workflow alongside TLA+ checks so a regression in any layer fails the same quality gate. For more detail on scope and roadmap, see the docs/formal-methods-and-verification.md document in the kcore repository.

https://kcorehypervisor.com

More Posts

Bridging the Silence: Why Objective Data Outperforms Subjective Health Reports in Elderly Care

Huifer - Jan 27

Why Email-Only Contact Forms Are Failing in 2026 (And What Developers Should Do Instead)

JayCode - Mar 2

Why We Bet on CSV over APIs

Pocket Portfolioverified - Feb 17

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

Why most people quit AWS

Ijay - Feb 3
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!