Skip to main content
You ran a workflow an hour ago. It worked (or it didn’t), and now someone asks: what exactly happened? With most AI tooling the answer scrolled away. With Nika the run itself is a file: every event the engine emitted, one JSON line each, in order, with timestamps. Save it, share it, replay it. The run is evidence, not a memory.

Record a run

nika run renders a live storyboard for humans and writes an execution journal by default to .nika/traces/<timestamp>-<id>.ndjson. The verdict line prints that path — the same file nika compile hello hello.nika then nika run hello.nika writes. Add --json and the same run also streams machine events on stdout, one NDJSON line per event — the CI and agent surface (stdout stays pure NDJSON; nothing else is printed there):
--resume, nika trace show|replay|verify and the editor read the journal directly. With no path, show, replay, and verify read the workspace’s latest journal and name it on stderr (nika trace: reading <path> (the workspace latest)). Pass a path to select another; nika trace ls lists the store. Opt out per run with --no-trace-file, or globally with NIKA_NO_TRACE_FILE (nika try never journals — the mock rehearsal is not a workspace run). A refusal before execution or a queued cancellation can leave no journal. Interruption can leave incomplete evidence. A job or receipt identity alone does not prove that a trace exists or is sealed; nika trace verify reads existing evidence and never seals it. The journal keeps task outputs verbatim so a run can be replayed and verified. Hash-chained is not confidential: .nika/traces/ inherits the sensitivity of whatever the workflow read, and on a shared or CI machine that directory is a second data-at-rest surface. Treat it like the files the run opened. A redaction posture that still verifies is a larger conversation; this page only names the consequence.

The event stream

A trace is the run’s lifecycle spine. Each line is one event with a unique id, a nanosecond timestamp, a kind, and typed fields:
  • workflow_started, then one task_scheduled per task
  • task_started when a task’s dependencies clear
  • task_completed (with duration_ms), or task_failed, or task_skipped (a when: gate said no), or task_cache_hit (a resumed run reused this task’s recorded work)
  • workflow_completed, or workflow_failed, or workflow_paused (a human gate is waiting — the trace carries the prompt payload)
Here is a historical capture (2026-07, two-task exec workflow). The kinds are still the spine; do not treat the timestamps or ids as current engine output:
The spine is not the whole vocabulary. Deeper kinds record retries (task_retrying), cancellation (task_cancelled, workflow_cancelled), money (cost_incurred), security checks (permit_checked), streaming (infer_chunk) and the agent loop (agent_tools_selected, agent_budget_checkpoint, and friends). The full event system, including the durability checkpoints that make runs resumable, is documented in Concepts · Events. Real spend also rides the spine: a priced task_completed carries a cost_usd field next to its tokens — absent for mock and local runs (unpriced is honestly absent, never a fake 0). Agent loops report the loop’s accumulated tool spend as tools_cost_usd.

Replay, never re-execute

Two commands inspect a recorded run. With no path they read the workspace’s latest journal:
replay plays the recorded events through the same renderer that drew the original run: same storyboard, same timing, same verdict card. show skips the animation and prints the summary. Both are a reading of the file — they do not walk the hash chain.
Replay = re-render, never re-execute. Replaying a trace calls no model, runs no command, touches no file. Zero tokens, zero effects. It is a projection of what already happened, safe to run on any machine, any number of times.

Tamper-evidence · every event, hash-chained

The trace is not just a log — it is a hash chain. Every event the engine emits carries a link over the one before it: task boundaries, each permit check, every tool call an agent: loop makes, the run seal at the end. Nothing the engine did is off the chain, and nothing can be edited, reordered, or dropped from the middle without breaking it.

Change one byte, and verify says where. Each line of a run’s trace carries the sha256 of the line before it. nika trace verify reads the chain back intact; in a copy with one byte of line 4 changed, it stops at line 5 with BROKEN and exit code 2. A mock/echo rehearsal captured from the real CLI; the scan and the moving hashes are illustration.

verify walks the chain and then climbs a proof ladder, reporting the highest tier honestly attained. That is the proof, distinct from show / replay: those inspect the recorded events; this command attests the sequence is intact. The tiers:
  • CHAINED — the links hold; the sequence is intact (verify prints OK — N events · chain intact and the head to compare).
  • SEALED — a signature over the whole chain verifies against a custody key (the run was signed).
  • ANCHORED — a detached sidecar verifies fully offline against a public transparency log entry + an RFC 3161 timestamp (nika trace anchor is the opt-in network act that mints it).
  • REPLAYED — you passed a separately supplied fresh journal; verify compares it. The command never starts that second run.
traceVerify / nika trace verify verifies an existing seal. It does not create one. A succeeded run can still be UNSEALED on a keyless machine. An intact chain is not proof that a human read the output, that an external API committed, or that the business goal was met. Three refusals name themselves rather than hide in a tier: a broken chain is BROKEN at line N, the first line whose recorded link no longer matches; a seal with lines appended after it is TAMPERED (forgery, never a crash); a sidecar that vouches for nothing is ANCHOR FORGED.

Why this matters

  • The run is auditable. Which tasks ran, in what order, how long each took, what it cost, which permits were checked: it is all in the file, not in someone’s scrollback.
  • The run is verifiable. Every event is hash-chained and the whole chain verifies offline (nika trace verify) — tamper-evident by construction, not by trust.
  • The run is shareable. Attach the .ndjson to a PR or an issue. A reviewer replays it locally and sees exactly what you saw, without credentials and without re-running anything.
  • The demos are honest. The replays on nika.sh (“Press play. It really ran.”) are exactly these files: recorded runs played back through the same renderer, not screenshots and not mockups.
  • The run is resumable. The same file doubles as the checkpoint: nika run --resume <trace> skips completed work with visible cache hits and re-arms a paused human gate. See Resume a run.
  • Reference · CLI: every nika run and nika trace flag, including --json and --output json (the typed outputs: export, a different surface from the event stream).
  • Concepts · Events: the full event vocabulary and the durability model behind it.
  • Concepts · Workflows: how a file becomes the executed graph the trace records.