InitRunner

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:

FieldTypeDescription
run_idstrUnique run identifier (12-character hex)
agent_namestrName from metadata.name
timestampdatetimeUTC timestamp of run start
promptstrInput prompt (subject to redaction)
outputstrAgent output (subject to redaction)
tokens_inintInput tokens consumed
tokens_outintOutput tokens consumed
cost_usdfloat | nullEstimated USD cost for the run (via genai-prices). null when pricing data is unavailable for the model/provider
tool_callslistTool invocations with name, args, and result
duration_msintWall-clock duration in milliseconds
successboolWhether the run completed without error
errorstr | nullError message if the run failed
trigger_typestrHow the run was initiated: prompt, cron, file_watch, webhook, autonomous
principal_idstr | nullIdentity 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 alice

When 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-audit

The 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
FlagTypeDefaultDescription
--agentstr(all)Filter by agent name
--trigger-typestr(all)Filter by trigger type (prompt, cron, file_watch, webhook, autonomous)
--sincestr(none)Start date (ISO 8601, e.g. 2025-01-01)
--untilstr(none)End date (ISO 8601)
--limitint1000Maximum records to export
-f, --formatstr"json"Output format: json or csv
-o, --outputstrstdoutOutput 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.json

Tamper-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:

  1. INITRUNNER_AUDIT_HMAC_KEY env var (64-character hex, decodes to 32 bytes)
  2. ~/.initrunner/audit_hmac.key (32 raw bytes, mode 0600)
  3. 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.db

The command walks every signed row and reports:

FieldDescription
Total rowsRecords in the database
Unsigned (legacy)Rows written before the chain feature was enabled
VerifiedRows whose signature matched
Tip id / Tip hashLast signed row and its hash (truncated to 16 chars)
Pruned gapsHoles left by audit prune (informational, not breaks)

Exit code is 0 on success and 1 on any break or missing key. Common failure reasons:

ReasonMeaning
key_missingNo env var and no key file. Set INITRUNNER_AUDIT_HMAC_KEY or place a key at ~/.initrunner/audit_hmac.key.
key_invalidEnv var is not valid 64-char hex.
signature_mismatchA row was modified after it was signed.
prev_hash_mismatchA 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 typeWritten byFields
sandbox.execRuntime sandbox backends (bwrap, docker)backend, argv0, rc, duration_ms
policy_deniedAgent policy delegation denialsprincipal, 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
FlagTypeDefaultDescription
--retention-daysint90Delete records older than this
--max-recordsint100000Keep 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
FieldTypeDefaultDescription
retention_daysint90Delete records older than this many days
max_recordsint100000Maximum 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: true

This redacts common PII patterns in both prompts and outputs before writing to the audit database:

PatternExampleRedacted As
Email addressesuser@example.com[EMAIL]
Social Security Numbers123-45-6789[SSN]
Phone numbers+1-555-123-4567[PHONE]
API keyssk-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 tokens

Custom 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.db with 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"

On this page