Audit Log
The gateway records security-relevant activity as redacted, append-only SQLite rows. Audit is used for read operations, scope denials, preview/risk decisions, paper lifecycle transitions, remote auth events, sidecar forwarding, live submit/cancel/modify and bracket lifecycle events, and reconciled live lifecycle transitions.
Storage
The SQLite schema lives under src/internal/audit/migrations/. Live order
reconciliation adds a live_orders_pending backlog keyed by account id and
broker order id.
SqliteAuditWriter configures WAL journaling, writes redacted payload JSON, and
stores a chained HMAC hash for tamper-evidence across appended rows.
Review and Export
ibkr-agent audit tail --limit 100 --json
ibkr-agent audit tail --database-url sqlite:/path/to/audit.db --limit 100 --json
ibkr-agent audit export --database-url sqlite:/path/to/audit.db --limit 500 --json
ibkr-agent audit verify --json
ibkr-agent audit verify --database-url sqlite:/path/to/audit.db --hmac-secret-env IBKR_AUDIT_HMAC_SECRET --json
MCP clients use ibkr_audit_tail with ibkr:audit:read.
CLI audit tail, audit export, and audit verify are also scope-gated by
ibkr:audit:read when a runtime config is supplied, and each command appends a
redacted audit event for the audit read/verification action itself.
audit verify scans the full chained HMAC log and exits with code 2 when
the chain is broken. External database verification requires the original audit
HMAC key through --hmac-secret-env; the runtime database uses the active CLI
configuration key automatically.
Redaction
Audit payloads must not store:
- bearer tokens;
- cookies;
- credentials;
- sensitive headers;
- local secret paths;
- raw Client Portal Gateway session material;
- raw account ids.
Account and token correlation use HMAC-SHA256. Free-form audit metadata is
scrubbed by sensitive field name before persistence. Field-name matching is
case-insensitive and substring-based, so broad markers such as path and
header intentionally redact conservative matches rather than risk leaking
local paths or sensitive headers.
Denied, refused, failed, and completed operations keep a consistent correlation shape so review can reconstruct what happened without exposing broker secrets.
Live Reconciliation
Successful live submits and non-terminal live modifies are added to the
reconciliation backlog. The MCP stdio runtime calls
reconcile_live_orders_once on the configured interval
(live_trading.reconciler_interval_seconds, default 5) to poll
IbkrBackend::order_status, append live_order_lifecycle_changed events on
status transitions, and remove filled/cancelled/refused orders from the
backlog. On startup, the runtime also rebuilds the backlog from completed live
idempotency records so existing non-terminal live orders remain tracked after a
restart.
The same durable live idempotency records provide server-side frequency and session counters for live submit gates. CLI and MCP submit paths overwrite the caller context counters before evaluation.