Audit Trail
What you'll accomplish: Enable the audit trail, understand what gets logged, query and filter audit events, and verify chain integrity for compliance audits.
Overview
Hoziron maintains a tamper-evident audit log of all security-relevant operations. Every action — agent lifecycle changes, model interactions, tool invocations, policy enforcement decisions — is recorded in an append-only store with cryptographic chaining.
Enable the audit trail
The audit trail is enabled by default. To customize:
[audit]
enabled = true
max_entries = 100000 # entries retained before pruning (default: 100,000)
segment_max_size_mb = 64 # future: segment rotation threshold
retention_days = 90 # future: segment retention period
What gets logged
| Category | Events |
|---|---|
| Agent Lifecycle | Agent create, start, stop, suspend, resume, delete, competency equip |
| Model Interactions | Agent invocations (success/error, duration, token usage), model routing decisions |
| Tool Invocations | Every MCP tool call (tool name, agent, success/error, execution time) |
| Trust Enforcement | Permission denials, rate limiting, trigger mismatches, cost quota enforcement |
| System Events | Circuit breaker trips, health check failures, cron executions |
Each entry records: timestamp, actor identity, action, target resource, outcome, and a SHA-256 chain hash.
Query audit events
CLI
# Recent events
hoziron security audit --limit 50
# JSON output
hoziron security audit --limit 50 --json
API
# Recent entries
curl http://localhost:4200/security/audit?limit=50 \
-H "Authorization: Bearer hzk_..."
# Filtered: only trust enforcement events from a specific actor
curl "http://localhost:4200/security/audit?limit=100&category=trust_enforcement&actor=admin-key" \
-H "Authorization: Bearer hzk_..."
# Filtered: events since a specific time
curl "http://localhost:4200/security/audit?limit=1000&since=2026-06-01T00:00:00Z" \
-H "Authorization: Bearer hzk_..."
Audit read access requires admin or operator role.
Filter parameters
| Parameter | Description | Example |
|---|---|---|
limit | Maximum entries to return (default: 20) | 100 |
category | Event category prefix | agent_lifecycle, trust_enforcement, tool_invocation |
actor | Identity of the caller | admin-key, system |
since | Only entries after this ISO 8601 timestamp | 2026-06-01T00:00:00Z |
Verify integrity
The audit trail uses a Merkle hash chain — each entry's hash incorporates the previous entry's hash, forming a tamper-evident sequence. Any modification breaks the chain.
Self-verification
hoziron security verify
API verification (for third-party auditors)
curl http://localhost:4200/security/verify \
-H "Authorization: Bearer hzk_..."
Response:
{
"valid": true,
"entry_count": 4521,
"chain_anchor": "0000000000000000000000000000000000000000000000000000000000000000",
"tip_hash": "a3f8e1c2....",
"algorithm": "sha256(prev_hash|timestamp|identity|role|action|target|result)",
"integrity_covers": "timestamp, identity, role, action, target, result",
"integrity_excludes": "metadata (stored separately for evolvability; not hash-protected)",
"verification_note": "Each entry's hash = SHA-256 of the pipe-delimited concatenation..."
}
When audit is disabled, the response clearly indicates no verification is possible:
{
"valid": null,
"status": "disabled",
"reason": "audit disabled"
}
Independent verification by auditors
An auditor can verify without trusting the platform:
- Export all entries via
GET /security/audit?limit=999999 - Get chain metadata via
GET /security/verify - Sort entries by
idascending - For each entry, compute:
SHA-256(prev_hash|timestamp|identity|role|action|target|result) - Verify computed hash equals the entry's
hashfield - Verify first entry's prev_hash equals
chain_anchor - Verify last entry's hash equals
tip_hash
If any check fails, tampering has occurred.
Immutability guarantees
The audit store enforces append-only at the database level:
- UPDATE operations are blocked — SQLite triggers reject any modification to existing entries
- DELETE operations are blocked — only the internal retention pruning path can remove old entries
- Merkle chain detects tampering — even if database-level protections are bypassed (e.g., file-level editing), the hash chain breaks and verification fails
Storage
Audit data is stored at $HOZIRON_HOME/data/audit.db. The database uses WAL mode for concurrent read performance.
Retention and pruning
When the entry count exceeds max_entries, the oldest entries are pruned. Before deletion:
- The hash of the last pruned entry is saved as a checkpoint
- Chain verification anchors from this checkpoint (not genesis)
- The chain remains verifiable across pruning events
SIEM integration
For centralized security monitoring, forward audit events to your SIEM:
- Use JSON log format: set
HOZIRON_LOG_FORMAT=jsonor[logging] format = "json"in config - Future: native SIEM export consumer (webhook, syslog) — see roadmap
Best practices
- Keep audit enabled in production — it's enabled by default, don't disable it
- Verify chain regularly — schedule
hoziron security verifyin cron or monitoring - Export for compliance — pull audit entries to external storage for long-term retention beyond
max_entries - Monitor trust enforcement — filter by
category=trust_enforcementto detect attack patterns - Alert on verification failure — a broken chain indicates tampering or corruption
Next steps
Related: