The trace is the UI¶
An assurance tool's most valuable interface is portable evidence, not an app. Evarness renders a graph, a run, and its verdicts into one self-contained HTML file — the event-sourced trace is the animation feed, the digest in the header is recomputable from the file alone, and the file itself obeys the product's own honesty rules. This post is the design argument, and every claim in it links to an artifact this site's build just generated and checked.
The constraint as identity¶
Evarness ships with two runtime dependencies. That is not a footnote — it is
part of the public claim: a proof you can verify offline, on an air-gapped
machine, from a wheel you built yourself. A package.json in this repo
would contradict that story before anyone finished the README.
So there is no app. When you run
evarness run graph.json --fixture happy.yaml --html replay.html
the "UI" you get is a generated artifact: one HTML file, inline SVG, vanilla JavaScript, zero external requests. Save it, email it, open it offline — portability is the point. An interface you must install answers "how do I look at this run?"; a file you can hand to a reviewer answers "how do I show a third party?" — and the second question is the one an assurance tool exists for.
The blocked run is the product in one picture¶
Open the blocked run → — generated by this
site's build, from the packaged governed_email_assistant pattern.
A hostile scenario is stopped by the governance layer. The interceptor node glows red. Everything downstream of it — including the LLM node — stays dim, because it was never reached.
"A blocked run never reaches the model" is a temporal contract in this
repo (no-model-call-after-block). In most tools that sentence would be a
log line you have to trust. Here it is something you can see — dim
means never lit, even in a static screenshot — and then check: the digest
in the header, c1:sha256:66d4021c…, is the canonical digest of the
blocked run's event stream, and the
end-to-end walkthrough shows the command that reproduces it
on your machine.
The happy path replay is the same file format
with a different verdict: scrub the playhead and watch nodes go dim → glow
→ done as the canonical events replay, c1:sha256:e0513e74… in the header.
The artifact obeys the honesty rules¶
The render is not a separate product surface with separate ethics. Every rule the library enforces shows up in the page:
- The digest is recomputable from the file alone. The data island embeds the canonical events verbatim — not a prettified copy. Extract them, recompute, and the header digest either reproduces or the file is lying. Two renderings of one source cannot drift; a screenshot and a log always can.
- Evidence and judgment stay separated. Events render in the replay pane; invariant verdicts render in their own pane, from their own data key. Checking a run never alters its digest, and the page layout says so.
- The footer states what was not established. A paused run says "invariants never evaluated". A graph with zero contracts says "nothing asserted". The most dangerous kind of pass is a green checkmark over nothing — so the artifact refuses to render one.
XSS is the threat model, not an afterthought¶
Who opens these files? Auditors. Reviewers. CI systems attaching them to build results. That makes a rendered artifact an attack surface: fixture content is data, and data can be hostile.
The page ships a Content-Security-Policy of default-src 'none' — it can
request nothing, run nothing external. Every < in the data island is
emitted as the JSON escape \u003c, because the obvious defense (escaping
just </) loses to the script-data double-escape trick. Everything the JavaScript writes into the
DOM goes through textContent, never innerHTML. And none of this is a
convention: each rule is enforced by a test that greps the generated
output. A safety property you don't test is a safety property you used to
have.
A regression-testable UI¶
Deterministic runs produce byte-identical artifacts — canonical events
only, no wall clock, layout derived from the same topological order as the
determinism contract. So the renderer is pinned the way runs are pinned:
under a golden hash, in the test suite, with a version (r3) that is
deliberately outside the c1 digest contract.
That rule earned its keep almost immediately. During review, an automated suggestion changed the rendered bytes — accessibility labels, a genuinely good change — without bumping the version. Same version, different golden: exactly the drift the digest machinery exists to prevent, arriving through the UI door. The rule is now absolute: any change to rendered bytes bumps the r-version, and the golden pin moves only with the version. The pin travels by rule, not by habit.
The bundle becomes a page¶
Open the proof browser → · raw bundle
Phase two of the same thesis: if the artifact is the UI, then the proof
bundle — the product's central object — deserves the same treatment.
evarness render proof.json produces one page: the tri-state verdict badge
(HOLDS / PENDING / FAILED), one viewer per scenario with its own playhead
and reproduction status, and the bundle's not_proven section rendered
verbatim as the footer. Three rules govern the page:
- The canvas never lies about identity. A graph is drawn only when
its hash matches the bundle's pinned
graph_sha256. No match — honest omission, never substitution. A canvas drawn from an unproven graph would be the trace lying about what ran, in visual form. - The page never vouches for itself. A signed bundle is labeled
"signature NOT checked by this page". Vouching is
verify's job; a page asserting its own integrity is worth exactly nothing. - The island IS the bundle. The page embeds the entire proof bundle in
its data island. Extract the
bundlekey and runevarness verify— the proof re-checks offline, signature included, from the HTML file alone.
Read that last rule again, because it is the whole post in one sentence: the HTML file is not a report about evidence — it is a carrier of verifiable evidence. This documentation site publishes one, and the build fails if the proof inside it stops verifying.
The honest boundary¶
What this does not claim:
- Rendering is not verification. The page shows;
evarness verifychecks. Opening a pretty artifact proves nothing until you run the verifier against what it carries. - The artifact is a view over evidence, not a generator of it. If the underlying run was not proven, the page says so — it cannot upgrade a trace into a proof.
- Byte-stability is scoped. The golden pin covers deterministic, simulation-mode runs — the same scope as the digest contract itself.
What's next¶
The generated-artifact rule has a corollary the decision log states
plainly: anything interactive enough to need a build toolchain lives in a
separate repo, never here. That repo now exists — Evarness Studio, the
product's visual surface: a pattern library, a drag-and-drop graph builder
with type-checked connections, and proof-bundle import that draws a canvas
only when the bundle's pinned graph hash matches. It grows as the product
grows. On the library side, evarness export unpacks a verified bundle
into standard interchange formats — and refuses to unpack one that fails
verification, because an exporter that launders tampered bundles would
undo everything above.
Both will get their own posts, with their own receipts.
Everything here is checkable: the demos on this site are generated by the product at build time, and the build fails if their digests drift from the values this post cites. Reproducible, or it didn't happen.