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, covenant audit verify checks the local hash-chain sidecar, 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 during normal writes. The file is opened for append on event record. Operator-driven retention purge rewrites the retained rows and the sidecar together.
- Locally chained. The daemon writes
$COVENANT_HOME/audit/events.chain.jsonlwith a SHA-256 hash chain over retained event rows. - One event per line. Compatible with
tail -F,jq, and other JSONL-aware tooling. - 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 four audits over a rolling window:- memory ↔ audit — every memory record has a matching
IntentDispatched. - memory parent references — every parent id resolves in the memory store.
- capability ↔ audit — every granted capability has a matching
CapabilityGranted. - memory ↔ receipts — memory writes and settlement receipts pair by
memory_record_id, with legacy count fallback.
- memory ↔ audit — every memory record has a matching
Reading the log
Last few events
covenant audit recent --limit 5 --json
# Or via HTTP:
curl -s 127.0.0.1:8421/audit/recent?limit=5 | jqVerify local chain
covenant audit verify
curl -s 127.0.0.1:8421/audit/verify \
-H "Authorization: Bearer $COVENANT_OPERATOR_TOKEN" | 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 local hash-chain detects retained-row edits and sidecar mismatch after anchoring, and covenant verify surfaces cross-reference drift. This is not public signing or immutable storage.
Deployments where the operator is not the sole writer to the host should either sign individual events or stream the log to an append-only system with the appropriate trust model. Both approaches integrate against the existing AuditLog trait.
Related
- Audit integrity — local hash-chain verification and its limits.
- Capability tokens — where grants and checks originate.
- CLI —
verifyand its drift-check rules. - Security model — what the local-trust assumption costs you.