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

CommandPurpose
horizon audit verifyWalk the full hash chain and report broken sequences.
horizon audit statsSummary counts by category, severity, and account.
horizon audit replayFiltered event listing in sequence order.
horizon audit exportExport events to JSON or CSV.
horizon audit anchorCompute the daily anchor hash for a date.
horizon audit enforce-retentionArchive 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).
  • verify and stats are safe to run against a live DB concurrent with writes. SQLite WAL mode handles the reader-writer concurrency.
  • enforce-retention holds an internal lock while it drops and recreates the delete trigger; schedule it during a maintenance window if writes are frequent.

Related