Skip to main content
AI runs act on the real world — they spend money, read secrets, write files, call tools. Most of them leave nothing behind that a reviewer can trust: a chat log is editable, a dashboard is a vendor’s word, a green CI tick says only that something exited zero. Run receipts is the pattern that fixes this, and it is deliberately not a product: any pipeline, framework or runtime can implement it.

The pattern

A run deserves two documents, one on each side of the execution:
1

Before — a reviewable contract

A static, diffable statement of what the run would do: the task graph and its order, which models and secrets it needs, what it may read, write and reach on the network, and an honest cost bound — a ceiling when priceable, an explicit unknown when not (never a silent $0). A human can approve it in review; a machine can gate on it in CI. Nothing has executed yet.
2

During — bounded execution

The runtime enforces the contract instead of trusting the prose: effects outside the declared boundary fail, spend past the bound stops the run. The contract is load-bearing, not documentation.
3

After — a verifiable record

An append-only journal of what actually happened: every task’s settle, output digest, spend and timing, each entry carrying a hash of the previous one. Anyone holding the file can recompute the chain and detect tampering — trust lives in the artifact, not in whoever hands it over.

What a receipt answers

The minimal shape

Nothing about the record requires a platform. Newline-delimited JSON, one event per line, each line carrying the hash of the line before it:
Grep-able, git-trackable, verifiable offline with a for-loop and a hash function. That is the whole trick — the value is in committing to it, not in any engine.

Nika as one implementation

Nika ships the pattern end to end, which is why this page lives here — but each half maps to a plain command you can inspect:
  • The contract is nika check: the static audit that prints the plan, the cost bound, the secret flows and the permits boundary before a single token is spent, and exits non-zero when the file breaks its own contract.
  • The record is the flight recorder: every run journals to .nika/traces/ as hash-chained NDJSON, and the run prints its chain head on close.
  • The re-proof is nika trace verify — it recomputes the chain and either confirms the head or names the first altered link (the full trace verb family is in the CLI reference). It never starts a run and never proves the business result was correct.
  • The exit is nika trace export: the journal projects to OTLP/JSON for Jaeger, Grafana or Langfuse — the receipt is portable, not a lock-in surface.
From TypeScript (@supernovae-st/nika@0.120.2), the same split is check() (the contract), run() then run.result() (execution), and traceVerify() (the re-proof of existing evidence). A succeeded run can still be unsealed on a keyless machine. In CI, the same split powers Nika × GitHub Actions: pull requests get the contract as a sticky comment, and nothing executes.

Adopt it without adopting Nika

The pattern is worth stealing. If your pipeline is LangGraph, plain Python or a Makefile, you can still ship receipts: declare the boundary in a reviewable file, journal settles as append-only NDJSON, chain the hashes, and teach CI to show the contract on every change. Your reviewers get the same two documents; no dependency required. If you later want the enforced version, how Nika compares is the honest map — including when not to switch.