Audit CLI
Verify the chain, list events, export, compute daily anchors, enforce retention. From the command line.
The horizon CLI exposes audit-log tools that compliance and ops touch daily. Every subcommand reads the same SQLite sink the run loop writes to, so there is no separate export pipeline.
Subcommands
| Command | Purpose |
|---|---|
horizon audit verify | Walk the full hash chain and report broken sequences. |
horizon audit stats | Summary counts by category, severity, and account. |
horizon audit replay | Filtered event listing in sequence order. |
horizon audit export | Export events to JSON or CSV. |
horizon audit anchor | Compute the daily anchor hash for a date. |
horizon audit enforce-retention | Archive and destroy expired events. |
Verify
Proves the chain is intact from genesis to the last event. Use at session close, after restore-from-backup, or scheduled nightly.
$ horizon audit verify --db /var/lib/horizon/audit.db
chain verified: 128312 events, sequences 1..128312
Non-zero exit on any break. Pipes cleanly into monit / a cron alert.
Stats
Distribution of events. Useful to spot anomalies: a spike in order.rejected or watchdog.halt tells you a week at a glance.
$ horizon audit stats --db /var/lib/horizon/audit.db
total events: 4,283
sequence range: 1..4283
time range: 2026-01-01T00:00:00+00:00 .. 2026-04-19T00:00:00+00:00
by category:
order.submitted 1,420
order.filled 1,380
risk.decision 850
...
Replay
Filtered event listing. Filters stack: --account, --market, --category, --since, --until, --limit.
$ horizon audit replay --db audit.db \
--account acc_jane \
--category order.rejected \
--since 2026-03-14T00:00:00Z --until 2026-03-14T23:59:59Z
142 2026-03-14T13:45:02+00:00 order.rejected warning acct=acc_jane market=TSLA msg=restriction: sanctions
143 2026-03-14T13:45:02+00:00 risk.decision warning acct=acc_jane market=TSLA msg=restriction: sanctions
(2 event(s) matched)
Useful for “what did we do for Jane on 2026-03-14?”-style exam questions.
Export
Bulk export for off-line analysis or handing to counsel.
# JSON (full payloads)
$ horizon audit export --db audit.db --format json --out q1_full.json \
--since 2026-01-01 --until 2026-04-01
# CSV (flat fields; no payload dict)
$ horizon audit export --db audit.db --format csv --out q1_orders.csv \
--account acc_jane
JSON preserves every field including payload. CSV flattens to a fixed header; payloads are omitted.
Anchor
Computes the daily anchor hash (SHA256(last_hash || date)). Publish this externally (notary, blockchain, immutable email archive, compliance officer’s sealed envelope) so any later tampering with the chain between anchors is detectable without trusting the chain itself.
$ horizon audit anchor --db audit.db --date 2026-04-18
date: 2026-04-18
last_seq: 128312
last_hash: 8f2a4b1c...e9
anchor_hash: 3c7e921f...ac
Cron it at session close:
0 21 * * 1-5 horizon audit anchor --db /var/lib/horizon/audit.db \
| mail -s "horizon anchor $(date +\%F)" compliance@example.com
Enforce retention
See Retention for the policy model. The CLI command archives expired events to a cold sink, destroys them from the live sink with a receipt hash, and appends to the destruction log.
# Dry-run: report what would happen, no writes
$ horizon audit enforce-retention \
--db /var/lib/horizon/audit.db \
--archive /var/lib/horizon/audit_archive.db \
--destruction-log /var/lib/horizon/destruction.jsonl \
--years 5 --operator ops@firm --dry-run
eligible: 12,483
held: 42
hold(subpoena_2026_03): 42
archived: 0
destroyed: 0
(dry-run: no writes)
# Real run
$ horizon audit enforce-retention \
--db /var/lib/horizon/audit.db \
--archive /var/lib/horizon/audit_archive.db \
--destruction-log /var/lib/horizon/destruction.jsonl \
--years 5 --operator ops@firm --reason "annual_retention_enforcement"
Exit codes
0: success.1: logical failure (chain broken, no events to anchor, etc.).2: bad arguments (missing required flag, invalid date, unknown format).
Safety
- The CLI never reaches broker venues. Read-only operations against the audit store, plus the destructive
enforce-retention(which is still an operator-invoked command, not a scheduled background process by default). verifyandstatsare safe to run against a live DB concurrent with writes. SQLite WAL mode handles the reader-writer concurrency.enforce-retentionholds an internal lock while it drops and recreates the delete trigger; schedule it during a maintenance window if writes are frequent.
Related
- Audit trail for the event model.
- Retention for the policy.
- DLQ CLI for the dead-letter queue.