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 cp or tar; 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 kind tag plus its payload. Adding new variants is a backward-compatible schema change.
  • Cross-checked. covenant verify runs 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.

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 | jq

Filter 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.jsonl

Trust 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