Audit log
Every state-changing surface in Covenant emits an AuditEvent to the append-only log at $COVENANT_HOME/audit/events.jsonl. The log is the ground truth — operators read it directly, covenant verify cross-checks it against the other state files, and the covenant audit/recent route reads from the same file.
Event envelope
AuditEvent {
id: uuid, // unique per event
timestamp_ms: u64, // unix milliseconds
issuer: AgentId, // the daemon's local identity
kind: AuditKind // tagged variant — see below
}Variants
IntentDispatched
{
"kind": "intent_dispatched",
"intent_id": "uuid",
"intent_text": "…",
"matched_agent": "research@local" | null,
"result_hash_hex": "…",
"status": "ok"
}IntentIgnored
{
"kind": "intent_ignored",
"intent_id": "uuid",
"intent_text": "…",
"matched_pattern": "**/*.pem"
}CapabilityCheck
{
"kind": "capability_check",
"agent_id": "research@local" | "tool:echo",
"required_actions": ["tool.web_search"],
"missing_actions": [],
"passed": true
}CapabilityGranted
{
"kind": "capability_granted",
"subject_display": "user@local",
"action": "tool.web_search",
"granted_by_display": "user@local",
"signature_b58": "4qXP…8tF1"
}Properties
- Append-only on disk. The file is opened for append; the daemon never rewrites prior lines. Backup with
cportar; rotate with care (rotation is your problem, not the daemon's). - One event per line. Read it with
tail -F,jq, or any JSONL-aware tool. - Deterministic schema. Each variant serialises with a stable
kindtag plus its payload. Adding new variants is a backward-compatible schema change. - Cross-checked.
covenant verifyruns three audits over a rolling window:- memory ↔ audit — every memory record has a matching
IntentDispatched. - capability ↔ audit — every granted capability has a matching
CapabilityGranted. - memory ↔ receipts — memory writes and settlement receipts pair 1:1.
- memory ↔ audit — every memory record has a matching
Reading the log
Last few events
covenant tools call audit_recent --args '{"limit":5}'
# Or via HTTP:
curl -s 127.0.0.1:8421/audit/recent?limit=5 | jqFilter for capability checks that failed
tail -F ~/.covenant/audit/events.jsonl \
| jq -c 'select(.kind.kind == "capability_check" and .kind.passed == false)'Find every dispatch for a specific agent
jq -c 'select(.kind.kind == "intent_dispatched"
and .kind.matched_agent == "research@local")' \
~/.covenant/audit/events.jsonlTrust model
The audit log is local. A user with write access to $COVENANT_HOME can rewrite history. The daemon does not sign individual events today; the protection is the OS file-permission model and the covenant verify drift check that catches out-of-band edits if they break the cross-references.
For environments where the operator is not the only writer to the host, sign individual events or stream the log to an append-only system with the right trust model. That layer plugs into the existing AuditLog trait.
Related
- Capability tokens — where grants and checks originate.
- CLI —
verifyand its drift-check rules. - Security model — what the local-trust assumption costs you.