This guide walks you through building a simple increment/decrement counter app where the state machine is formally verified in Dafny, compiled to JavaScript, and wired into a React component. The counter’s invariant — that the value is always non-negative — is proved at compile time and preserved by every state transition, including undo and redo.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/metareflection/dafny-replay/llms.txt
Use this file to discover all available pages before exploring further.
Prerequisites: Dafny installed and available in
PATH, Node.js and npm, and the dafny2js tool from the repository.Architecture overview
Before diving in, here is how the pieces fit together:App.Dispatch, App.Undo, and App.Redo; everything below that line is verified.
Steps
Understand the Replay framework
The
Replay.dfy file (already in the repository at kernels/Replay.dfy) defines two abstract modules you will refine:Domain— the interface your app must implement:Model,Action,Inv,Apply,Normalize, and two lemmas.Kernel— the generic history manager proved once; providesDo,Undo,Redo, and all correctness theorems.
Create the Dafny domain
Create The file has three modules:
MyAppDomain.dfy alongside kernels/Replay.dfy:MyAppDomain— your domain:Model = int, invariantm >= 0,Applyfor transitions,Normalizeto clamp negatives back to zero, and theStepPreservesInvlemma (the body is empty because Dafny can verify it automatically for this simple case).MyAppKernel— wires your domain into the generic kernel.AppCore— the public React-facing API:Init,Dispatch,Undo,Redo,Present,CanUndo,CanRedo.
Verify the Dafny code
Inv(Init())holds (the initial value0satisfiesm >= 0)- For every
manda,Inv(Normalize(Apply(m, a)))holds
Create the React app with Vite
From the repository root:Dafny compiles integers to
BigNumber, so bignumber.js is a required runtime dependency.Configure Vite
Edit This ensures Vite pre-bundles
my-app/vite.config.js:bignumber.js correctly alongside the CommonJS Dafny output.Compile Dafny to JavaScript and generate app.js
The The generated
dafny2js tool generates the integration layer — it reads your AppCore module and emits a clean ESM wrapper that handles all type conversions between JavaScript and Dafny.app.js provides:- Proper ESM imports for the Dafny runtime
- Type converters (
toJson/fromJson) for all datatypes - Action constructors that accept JS values and convert to Dafny types
- All functions from
AppCorewrapped for direct calling
Do not edit
MyApp.cjs or app.js directly. Regenerate them with the commands above after any change to your .dfy file.Write the React component
Replace The pattern is straightforward:
my-app/src/App.jsx:useStateholds the DafnyHistoryobject (past, present, future).- Every user action calls an
App.*function and replaces the history with the result. App.Present(h)extracts the currentModelvalue for rendering.App.CanUndo(h)andApp.CanRedo(h)drive the disabled state of the history buttons.
Run the app
http://localhost:5173 in your browser. You have a running React app with a Dafny-verified state machine:- Click Inc to increment — the value increases.
- Click Dec to decrement — the value decreases, but never below zero (the invariant is enforced by
Normalize). - Click Undo and Redo to move through history — the invariant holds at every step.
What you proved
By completing this guide you discharged two proof obligations:| Obligation | What it means |
|---|---|
Inv(Init()) | The initial state 0 satisfies m >= 0 |
Inv(m) ⇒ Inv(Normalize(Apply(m, a))) | Every transition, after normalization, preserves non-negativity |
Dopreserves the invariant — every new action lands in a valid state.UndoandRedopreserve the invariant — history navigation cannot produce an invalid state.- Every state in
pastandfuturesatisfies the invariant — the full history is clean.
Next steps
Domain obligations
Learn the full contract for plugging a domain into any of the five kernels.
dafny2js tool
Explore all CLI options and the generated bundle structure.
React integration guide
Patterns for wiring the verified kernel into more complex React apps.
Kanban app
A non-trivial domain: ordered columns, drag-and-drop, WIP limits, and undo/redo.