Audit Trail
InitRunner automatically logs every agent run to a local SQLite database. Audit records capture what happened, how much it cost, and whether it succeeded — giving you a complete history of agent behavior. For distributed tracing and deeper performance analysis, see Observability.
What Gets Logged
Every agent run produces an audit record with these fields:
| Field | Type | Description |
|---|---|---|
run_id | str | Unique run identifier (12-character hex) |
agent_name | str | Name from metadata.name |
timestamp | datetime | UTC timestamp of run start |
prompt | str | Input prompt (subject to redaction) |
output | str | Agent output (subject to redaction) |
tokens_in | int | Input tokens consumed |
tokens_out | int | Output tokens consumed |
cost_usd | float | null | Estimated USD cost for the run (via genai-prices). null when pricing data is unavailable for the model/provider |
tool_calls | list | Tool invocations with name, args, and result |
duration_ms | int | Wall-clock duration in milliseconds |
success | bool | Whether the run completed without error |
error | str | null | Error message if the run failed |
trigger_type | str | How the run was initiated: prompt, cron, file_watch, webhook, autonomous |
principal_id | str | null | Identity of the trigger source (e.g., telegram:12345, discord:67890, webhook:github). Independent of agent principals. |
Principal Tracking
Every audit record includes a principal_id field tracking the trigger source identity. This is independent of agent principals and is preserved across all execution paths. You can filter audit logs by principal:
# Via API
GET /api/audit?principal_id=alice
# Via CLI export
initrunner audit export --principal-id aliceWhen agent policy is enabled, delegation policy denials are logged as policy_denied audit events.
Storage
Audit records are stored in a SQLite database:
- Default path:
~/.initrunner/audit.db - Environment variable:
INITRUNNER_AUDIT_DB(overridden by--audit-db) - Custom path:
--audit-db ./custom-audit.db - Disable entirely:
--no-audit
# Default audit database
initrunner run role.yaml -p "Hello"
# Custom audit database path
initrunner run role.yaml -p "Hello" --audit-db ./my-audit.db
# Disable audit logging
initrunner run role.yaml -p "Hello" --no-auditThe same flags work with initrunner run --daemon and initrunner run --serve.
Export
Export audit records as JSON or CSV for analysis, reporting, or ingestion into external systems.
initrunner audit export| Flag | Type | Default | Description |
|---|---|---|---|
--agent | str | (all) | Filter by agent name |
--trigger-type | str | (all) | Filter by trigger type (prompt, cron, file_watch, webhook, autonomous) |
--since | str | (none) | Start date (ISO 8601, e.g. 2025-01-01) |
--until | str | (none) | End date (ISO 8601) |
--limit | int | 1000 | Maximum records to export |
-f, --format | str | "json" | Output format: json or csv |
-o, --output | str | stdout | Output file path |
Examples
# Export all records as JSON
initrunner audit export
# Export last 7 days for a specific agent as CSV
initrunner audit export --agent monitor-agent --since 2025-01-08 -f csv -o report.csv
# Export only cron-triggered runs
initrunner audit export --trigger-type cron --limit 500
# Export to a file
initrunner audit export -o audit-export.jsonTamper-Evident Chain
Since v2026.4.15, every audit record is signed with HMAC-SHA256 over the previous record's hash. The result is a tamper-evident chain: changing or removing a row in the middle of the log breaks every signature after it. Signing happens inside BEGIN IMMEDIATE, so concurrent writers serialize through SQLite's RESERVED lock instead of forking the chain.
Key Storage
The HMAC key is loaded in this order:
INITRUNNER_AUDIT_HMAC_KEYenv var (64-character hex, decodes to 32 bytes)~/.initrunner/audit_hmac.key(32 raw bytes, mode0600)- Auto-generated on first signed write and saved to the file above
A copied audit database without the key cannot be verified. Verification never auto-creates a key, so an unrecognised database fails cleanly instead of silently re-signing under a fresh chain.
Verifying the Chain
initrunner audit verify-chain
initrunner audit verify-chain --audit-db ./custom-audit.dbThe command walks every signed row and reports:
| Field | Description |
|---|---|
| Total rows | Records in the database |
| Unsigned (legacy) | Rows written before the chain feature was enabled |
| Verified | Rows whose signature matched |
| Tip id / Tip hash | Last signed row and its hash (truncated to 16 chars) |
| Pruned gaps | Holes left by audit prune (informational, not breaks) |
Exit code is 0 on success and 1 on any break or missing key. Common failure reasons:
| Reason | Meaning |
|---|---|
key_missing | No env var and no key file. Set INITRUNNER_AUDIT_HMAC_KEY or place a key at ~/.initrunner/audit_hmac.key. |
key_invalid | Env var is not valid 64-char hex. |
signature_mismatch | A row was modified after it was signed. |
prev_hash_mismatch | A row in the middle of the chain was deleted or rewritten. |
Pruning leaves pruned_gaps rather than breaks because both audit prune and verify-chain recognise gaps from id renumbering as expected.
Security Events
Since v2026.4.16, the runtime writes a separate security_events table alongside the main audit log. The table captures low-level events that do not belong in per-run records (sandbox launches, policy denials) but still need an append-only trail.
Query the table with:
initrunner audit security-events
initrunner audit security-events --event-type sandbox.exec
initrunner audit security-events --agent code-runner --limit 200| Event type | Written by | Fields |
|---|---|---|
sandbox.exec | Runtime sandbox backends (bwrap, docker) | backend, argv0, rc, duration_ms |
policy_denied | Agent policy delegation denials | principal, action, reason |
Records are attributed to the role's agent_name and signed into the same HMAC chain as regular audit rows. verify-chain covers both tables.
Pruning
Remove old audit records to manage database size.
Manual Pruning
initrunner audit prune
initrunner audit prune --retention-days 30 --max-records 50000| Flag | Type | Default | Description |
|---|---|---|---|
--retention-days | int | 90 | Delete records older than this |
--max-records | int | 100000 | Keep at most this many records (oldest removed first) |
Automatic Pruning
Configure auto-pruning via the security policy in your role YAML:
security:
audit:
retention_days: 30
max_records: 50000| Field | Type | Default | Description |
|---|---|---|---|
retention_days | int | 90 | Delete records older than this many days |
max_records | int | 100000 | Maximum audit records to retain |
Auto-pruning runs at daemon startup and periodically during long-running daemons.
Redaction
Audit logs can contain sensitive information. InitRunner supports two redaction mechanisms to sanitize records before they are written.
PII Redaction
Enable built-in PII pattern detection:
security:
content:
pii_redaction: trueThis redacts common PII patterns in both prompts and outputs before writing to the audit database:
| Pattern | Example | Redacted As |
|---|---|---|
| Email addresses | user@example.com | [EMAIL] |
| Social Security Numbers | 123-45-6789 | [SSN] |
| Phone numbers | +1-555-123-4567 | [PHONE] |
| API keys | sk-abc123... | [API_KEY] |
Custom Redaction Patterns
Add regex patterns to redact domain-specific sensitive data:
security:
content:
redact_patterns:
- "\\b[A-Z]{2}\\d{6}\\b" # internal account IDs
- "\\btoken_[a-zA-Z0-9]+\\b" # internal tokensCustom patterns are applied in addition to PII redaction (if enabled). Matches are replaced with [REDACTED].
Viewing Audit Logs
Beyond the CLI export command, audit logs are accessible through:
- Dashboard — the Audit page offers search, pagination, and CSV/JSON export
- Direct SQLite access — query
~/.initrunner/audit.dbwith any SQLite client
# Quick peek at recent records
sqlite3 ~/.initrunner/audit.db "SELECT agent_name, trigger_type, success, duration_ms FROM audit ORDER BY timestamp DESC LIMIT 10"