An AI client can have the right logging instructions, the right MCP configuration, and a reachable audit server, and still leave you with no record of the conversation.
The model might skip a tool call. A permission policy might refuse it. A run might end before the logging call happens.
We encountered these failure modes while testing Chron. They led to a change in where capture happens.
Chron 0.1.62 introduces chron run: a wrapper that records the stdin, stdout, and stderr channels it controls before forwarding or releasing their bytes. Logging on that path does not require the model to call Chron.
Move capture into the execution path
Chron is a local-first tool for recording AI interactions and inspecting the evidence they leave behind. Its published npm package is available for end users; the source is visible for review under the project's license.
Its MCP integration remains useful. A cooperating client can record messages and structured events, including tool calls and code changes. But a request to log is not an enforcement boundary.
The wrapper introduces a different path:
Operator input -> journal write + fsync -> child stdin
Child stdout -> journal write + fsync -> terminal
Child stderr -> journal write + fsync -> terminal
The process doing the writing also controls forwarding and release. It waits for the covering write and fsync to succeed. If a capture write fails, it stops forwarding, withholds pending output, and terminates the direct child.
That ordering is the feature. Recording the same bytes after displaying them would leave a window in which the response reached the operator and the audit record did not.
Try the capture path
Install the released package:
npm install -g chron-mcp@0.1.62
chron version
Print the scope before wrapping a client:
chron run --scope
Here is a small example that needs no AI account. It checks the input/output path with a child process that echoes what it receives:
printf 'hello chron\n' | chron run -- node -e \
'process.stdin.on("data", b => process.stdout.write(b))'
For a headless Claude Code invocation that reads its prompt from stdin:
printf 'Explain this repository in three sentences.\n' | chron run -- claude -p
Client authentication and permissions still apply. This release controls inherited stdio pipes; it is not a promise of compatibility with every interactive terminal UI. Text supplied in command-line arguments is recorded as command metadata rather than as an input message.
Inspect the resulting evidence:
chron history
chron verify --journal
chron verify <session-id>
Capture first, query second
Wrapped runs go into an append-only, hash-chained local journal. Chron then projects completed runs into its session database for history and reporting.
This ordering matters when the database is unavailable. A projection failure is reported separately from capture. The journal retains the evidence, and replay can reconstruct the session later:
chron project
Replay is transactional per run and idempotent. Repeating it does not create another copy of an already projected session.
The default journal is ~/.chron/capture.jsonl; the default database is ~/.chron/chron.db. Both remain under the operator's control.
What the enforcement label means
Chron distinguishes two questions:
- Provenance: how did Chron obtain this evidence?
- Capture mode: what required recording on this path?
A client calling an MCP logging tool and a wrapper intercepting stdio can both produce witnessed evidence. They have different capture guarantees.
A fully durable wrapped run can support enforced:wrapper. Session verification checks its journal binding, run structure, and projected message content against the journal. Setting a label in database metadata is insufficient.
Reduced-durability runs are explicitly distinguished and do not qualify for the same full guarantee.
The boundary is narrower than "everything the AI did"
The wrapper controls stdin, stdout, and stderr. It does not capture file access, network requests, independent subprocess activity, or a terminal the client opens directly. Nor does it prevent someone from launching the client outside Chron.
There is another distinction to keep clear: an internally consistent hash chain is not proof that nobody rewrote it. Someone able to replace the entire unanchored journal can recompute its hashes. Chron does not currently sign journal heads.
The label therefore describes the controlled capture path. It is not a certification of every AI action or an organization-wide enforcement policy.
Test the failure paths too
The useful tests were not just "does the output appear?" They were:
- Was its covering record flushed before it appeared?
- Did a short write or failed sync stop release?
- Did an interrupted run remain distinguishable from a completed one?
- Could database projection fail without losing captured evidence?
- Did an edited projection lose its verified enforcement claim?
An independent manual regression against the published 0.1.62 package exercised capture, replay, rotation, signing, recovery, imports, and collector delivery. It also found two OTEL guidance issues: collector rejection was described as a request not reaching the collector, and retry guidance overstated deduplication guarantees. Both remain findings in that released version.
That is the standard we want for an evidence tool: its claims should be small enough to test, and its limits should be visible alongside them.
Chron is available on npm, with the public project and documentation on GitHub.
If you try the wrapper, which client and failure scenario would you test first?

Top comments (0)