kew audit¶
Inspect the audit trail. Every governance action — dispatches, approval decisions, fleet stop/resume, CI fixes — lands as one row in the audit_events table of dispatch.db, carrying the full envelope: who (actor_type/actor_id), which repo, which endpoint, with policy_hash/prev_hash slots reserved for policy binding and hash chaining.
kew audit tail # 20 most recent events
kew audit tail --limit 50 --event approval_hold
kew audit tail --item SFD-12 # only events for one work item
kew audit tail --follow # poll for new events (Ctrl-C to stop)
kew audit tail --json # one JSON object per line
kew audit export # full trail as JSON to stdout
kew audit export -f csv -o audit.csv
kew audit export --since 2026-06-01
Note: -f is --follow on tail but --format on export (matching the top-level kew export).
Event types¶
| Event | Emitted by |
|---|---|
dispatch_completed |
every dispatch (success, failure, budget, auth failure, unreachable, exception) |
approval_hold / approval_granted / approval_confirmed / approval_declined |
the approval gate |
fleet_stop / fleet_resume |
kew stop / kew resume |
ci_fix_completed |
kew ci-fix |
circuit_breaker |
the automatic circuit breaker — a run killed for a cost anomaly or wall-clock timeout (scope=run), or a fleet stop on a daily/weekly spend breach (scope=fleet). actor=policy, named by the rule. |
claim_rollback |
the scheduler, when a claimed issue can't be submitted to the pool and is released back to ready (actor=policy) |
Who is the actor?¶
The actor is resolved per event: the KEW_ACTOR environment variable when set, otherwise the OS user. KEW_ACTOR=alice kew stop records actor_id=alice. Run rows record who dispatched; each audit event records who performed that action — the approver can differ from the dispatcher.
Endpoint is configured, not attested¶
The recorded endpoint is what was configured at dispatch time (ANTHROPIC_BASE_URL when set, else anthropic:default, for claude-code; <provider>:via-opencode from the model's provider prefix for opencode) — honest, but not proof of where traffic actually went. Credentials embedded in URL values (userinfo, query strings) are stripped before recording. Endpoint attestation from runner output is tracked separately (#251).
Where did dispatch-log.jsonl go?¶
Retired. Its three writers now write audit_events rows instead — SQLite gives the audit trail one serialized, multi-process-safe write path and adds run_id to dispatch events. Existing dispatch-log.jsonl files are left on disk untouched; the per-run .events.jsonl telemetry stream is unchanged.
kew audit verify¶
Verify the hash-chained audit log by recomputing every link from genesis —
not just the head. --anchors PATH additionally checks every in-range
anchored id against the recomputed chain, the anchored count against the live
chain (count-shrink relative to the latest anchor, bounded by
[audit] staleness_window), and the anchor chain — each anchor's prev
must bind the previous anchor. --json emits a machine verdict (including
anchors.count_shrink and anchor_chain).
The anchor-chain verdict reports how many links it actually verified —
Anchor chain: intact (12 link(s) verified). A legacy anchor file whose entries
predate the anchor chain verifies zero links, so more than one anchor with
0 link(s) verified prints a WARNING: the re-chain defence found nothing to
check rather than checking and passing (#1122). anchor_chain.checked and
anchor_chain.total carry the same numbers in --json.
Exit codes: 0 chain intact (and for an empty log) · 1 tamper detected
(broken link; an in-range anchor mismatch/absent row; count-shrink or a
stale/lagging anchor beyond the staleness window; or re-chained anchor history)
· 2 cannot run (no database, or an unreadable --anchors file).
The verdict does not depend on which directory you stand in: the database and
the default anchors file both resolve against the project root, so kew audit
verify from a subdirectory reads the same ledger as from the root. (Before
1156 it did not, and reading the wrong ledger reported a false COUNT-SHRINK¶
on a clean chain.)
See Audit log & tamper-evidence for what the guarantee does and does not cover.
kew audit anchor¶
Append the current chain head {id, entry_hash, count, timestamp, prev} to the
anchors file and print it — prev binds the previous anchor (the anchor chain).
The default is audit-anchors.jsonl at the project root (the directory
holding kew.toml), so running this from a subdirectory extends the one real
anchors file instead of dropping a stray copy beside you. A relative path passed
explicitly to --anchors still resolves against your current directory. Pure-local — no git, no network. Refuses to
extend a broken local chain or a re-chained anchor history. The scheduled
audit-anchor workflow runs this and commits/pushes the file; the Actions run
log is the witness.
kew audit policy [HASH]¶
Show the governance policy bound to dispatches, or resolve a retained policy hash.
- No argument — prints the current effective policy hash and its redacted snapshot (budgets, approval gate, routing, security patterns, limits, and the deployed security-hook digest).
HASH— prints the retained snapshot and itsfirst_seentimestamp for that policy. Exit 1 if the hash was never retained, exit 2 if there is no dispatch database.
--json emits machine-readable output. Pure-local; no network. The snapshot is
a redacted allowlist — secrets such as [alerting] webhook_url are never
included.