Audit logs, decision logs, and provenance for agents

Emit append-only action logs and policy decision logs that make an incident reviewable and an audit passable.

๐Ÿ”Ž Module 7 9 min read Not started

Cross-ref (Microsoft stack): Microsoft implements this natively via Entra sign-in + audit logs (stamped with idtyp=agent / idtyp=user and blueprint / agent-user relationships), Purview activity, and OpenTelemetry spans emitted by the Agent 365 SDK. See Lesson 10.8 (OTel packages + span design) and Lesson 10.2 ยง6 (object-to-log claim mapping). The audit schema in this lesson is what you fold Microsoft logs into.

Why this matters

If you can't reconstruct exactly what an agent did, why it was allowed to, and what data it touched โ€” at minute granularity, going back N years โ€” you can't pass a security audit, an EU AI Act review, or an internal post-mortem. Agent audit logging is not "we ship logs to S3"; it's an end-to-end discipline.

Learning objectives

  1. Define the minimum-viable audit record for agent actions.
  2. Capture decision logs (authz outcomes), not just action logs.
  3. Build a tamper-evident audit trail.
  4. Provide replay + lineage for any agent action.
  5. Map audit requirements to SOC 2 / ISO 27001 / EU AI Act controls.

1. Three log types you need

  1. Application logs โ€” debug / operational, what programmers normally write. Useful, but never the source of truth for audit.
  2. Decision logs โ€” every policy evaluation (allow / deny + inputs + reason + rule id). Produced by OPA / Cedar / your broker.
  3. Audit logs โ€” append-only, integrity-protected record of security-relevant events: authentications, token issuances, approvals, tool calls with side effects, policy changes, secret accesses, admin actions.

Separate retention, separate storage, separate access. Auditors look at #3 and sometimes #2; #1 is for engineers.


2. Schema for an agent audit record

A pragmatic minimum (extend as needed):

json
{
  "event_id": "evt_01HZ9XAQ2K8K3W3W4P4XGN1ZBA",
  "ts": "2026-06-17T10:34:12.482Z",
  "tenant": "acme",
  "subject": { "type": "user", "id": "alice@acme.com" },
  "actor":   { "type": "agent", "id": "agent.helpdesk-v1", "instance": "pod-7-9d2", "session_id": "sess_92f...", "model": "claude-opus-4.7", "system_prompt_version": "v17" },
  "delegation_chain": ["agent.helpdesk-v1"],
  "action": "tickets.update",
  "resource": { "type": "ticket", "id": "TKT-1029", "tenant": "acme" },
  "args_hash": "sha256:9fd9...",
  "args_redacted": { "status": "closed", "note": "<redacted>" },
  "scope": ["tickets:write"],
  "approval": { "required": true, "approval_id": "ap_4f...", "decided_by": "alice@acme.com", "decided_at": "...", "binding_message": "Close ticket TKT-1029?" },
  "capability_jti": "cap_4f...",
  "policy_decision": { "engine": "opa", "bundle": "policies@v2026.06.10", "rule": "mcp.allow_tickets_write", "decision": "allow", "duration_ms": 4 },
  "outcome": { "status": "success", "http": 200, "downstream_ids": ["tkt_update_8821"], "duration_ms": 312 },
  "tracing": { "trace_id": "ab12cd34...", "span_id": "ef56..." },
  "ip": { "src": "10.0.4.12", "geo": "DE" },
  "request_id": "req_01HZ9X...",
  "schema_version": "agent.audit/1.0"
}

Key design rules:

  • Args hashed so audit logs don't themselves become a leak surface.
  • Args redacted version for human review, with PII removed.
  • Subject AND actor, always โ€” supports act-based audit.
  • Policy decision embedded so you can re-run the same input through the policy bundle later.
  • Schema version so consumers can evolve.
  • Stable event_id (ULID/UUIDv7) for deduplication.

Pick one canonical schema for the org. Tools like the OCSF (Open Cybersecurity Schema Framework) give you a vendor-neutral starting point โ€” add an agent extension.


3. What events to log

Mandatory:

  • AuthN: login success/failure, MFA, step-up, passwordless.
  • Token issuance: client credentials, code exchange, token exchange, refresh, CIBA decision.
  • Token revocation, key rotation.
  • Agent registration / update / deprecation.
  • Each tool call with side effects (writes, sends, deletes, payments).
  • Each policy decision (allow/deny) for sensitive actions.
  • Each approval request + decision.
  • Secret access (broker โ†’ vault).
  • Privileged admin actions (policy changes, role grants, agent registration changes).
  • Schema / config changes.

Optional but valuable:

  • All read tool calls (sampled; full for regulated tenants).
  • Prompt / completion hashes per LLM call (for incident replay).
  • Model + prompt version per tool call.

Never log: raw secrets, raw tokens, full prompts containing PII unless the tenant explicitly consents.


4. Decision logs โ€” OPA / Cedar

OPA's decision log is a built-in feature:

yaml
# opa-config.yaml
decision_logs:
  service: audit-sink
  reporting:
    min_delay_seconds: 1
    max_delay_seconds: 5
services:
  - name: audit-sink
    url: http://audit-sink:8080

Each decision is shipped with the input, the result, the bundle version, and timing. Persist to your audit store + index for search.

For Cedar, the SDK exposes decision().diagnostics() โ€” same idea, log it.

These decision logs are how you prove policy was followed. The audit log proves the action happened. Together they cover "what" + "why".


5. Tamper-evidence

For high-assurance environments, append-only is not enough. Use:

  • Hash-chained logs: each record includes prev_hash; gaps or rewrites detectable.
  • Append-only stores: AWS QLDB (now in maintenance mode โ†’ use S3 Object Lock + signed manifests), Google Cloud Logging with retention locks, Azure Monitor immutability policies.
  • Sigstore-style transparency log for very-high-stakes deployments (e.g., Rekor model).
  • WORM storage (S3 Object Lock in compliance mode) for retention guarantees that survive insider risk.

Independent verification: a daily job recomputes hashes; mismatch โ†’ alert.

Cost: tamper-evidence adds storage + write latency. Reserve for events that matter (admin actions, approvals, financial actions); for high-volume operational events the standard log pipeline is fine.


6. Replay + lineage

Given an event_id, you should be able to answer:

  • Which agent did this? Which session? Which model + prompt version?
  • Which tokens were used? Issued by which IdP, via what flow?
  • Which retrieval chunks were in the LLM context? (Hash IDs.)
  • Which OPA bundle was active? Which rule fired?
  • What was the upstream user message that triggered it?
  • What downstream IDs were created/modified?

This is "agent lineage" โ€” borrowed from data lineage. Encode it explicitly:

json
"lineage": {
  "session_id": "sess_92f...",
  "user_message_hash": "sha256:..",
  "retrieval_chunks": ["chunk_..1", "chunk_..2"],
  "prior_event_ids": ["evt_..A", "evt_..B"],
  "policy_bundle_sha": "..",
  "model": "claude-opus-4.7",
  "system_prompt_version": "v17"
}

Together with the prompt + completion trace (LangSmith / Langfuse) you can replay the entire decision. This is invaluable for incidents.

OpenTelemetry GenAI semantic conventions are converging on agent + LLM spans โ€” use them as your tracing schema so observability tools speak the same language.


7. PII + retention

Audit logs themselves can be regulated. Apply:

  • Field-level classification: tag PII fields; redact at write time when not needed.
  • Per-tenant retention policy (e.g., 7 years for finance; 30 days default).
  • Per-tenant access controls on the audit pipeline.
  • DSAR support: be able to extract or delete a user's audit events (with caveats โ€” many regulations forbid deleting audit records; cryptographic erasure via per-user keys is sometimes the compromise).

Document the retention + access policy. Auditors always ask.


8. Operational pipeline

Typical shape:

Service emits structured JSON โ†’
  Kafka (topic per category: audit, decision, application) โ†’
    Real-time SIEM (Datadog Cloud SIEM, Splunk, Wazuh, Elastic) for alerts โ†’
    Cold storage (S3 with Object Lock) for retention โ†’
    Search backend (OpenSearch, ClickHouse) for investigations โ†’
    Anomaly detection (UEBA-style; rule + ML) โ†’
    Dashboards

Tooling shortcuts: Datadog, Splunk, Elastic, Sumo Logic, Grafana Loki + Tempo, AWS Security Lake, Google Chronicle. Pick by your existing observability stack.

For agent-specific telemetry, Langfuse / LangSmith / Helicone / Arize Phoenix capture prompt / completion / tool spans; export to the same SIEM via OTel.


9. Alerts

A few high-signal rules to ship on day one:

  • Spike in deny decisions (ร— of baseline) for any (agent, tenant) pair โ†’ possible attack.
  • Spike in scope-elevation requests โ†’ possible jailbreak.
  • New agent instance never seen before in this region.
  • Approval rejection rate climbing โ†’ social-engineering attempt.
  • Authentication failures from agent service accounts.
  • Audit log gap (no events from a service that should be emitting).
  • Tamper-evidence verification mismatch.

Don't over-alert; every alert needs a runbook (lesson 9.1).


10. Hands-on lab (3 h)

  1. Define your agent-audit JSON schema; commit to your repo with a JSON Schema spec.
  2. Update the broker (lesson 5.1) to emit one audit record per tool call.
  3. Update the resource server to emit one decision-log record per OPA call.
  4. Pipe both into Loki (or any backend). Build a Grafana dashboard: events per minute, per agent, per outcome.
  5. Add hash chaining: each event includes prev_hash = sha256(prev event JSON). Run a verifier as a cron.
  6. Simulate an incident (agent does a bad thing). Reconstruct the timeline using only the logs in < 10 min.

11. Common pitfalls

  1. Logging prompts verbatim (PII or system prompt leakage).
  2. Logging raw tokens or Authorization headers.
  3. Only logging on success โ€” denies are the most interesting events.
  4. Per-service log schemas โ†’ no joins โ†’ investigations take days.
  5. No retention SLAs โ†’ noisy delete or unbounded growth.
  6. No verification job for tamper-evidence โ€” useless if never checked.
  7. Audit available to all engineers โ€” over-permissioned audit is its own risk.

12. Self-check

  1. Three log types + their distinct retention.
  2. Why log actor separately from subject.
  3. Hash chaining in one paragraph.
  4. Five fields no audit record should omit.
  5. EU AI Act requires what about agent action traceability?

13. References

  • OCSF (Open Cybersecurity Schema Framework).
  • AWS Security Lake / Google Chronicle / Microsoft Sentinel docs.
  • OpenTelemetry GenAI semantic conventions.
  • OPA decision-logs docs.
  • Sigstore Rekor (transparency log).
  • "Sigma rules" for SIEM detections.
  • "Distributed Systems Observability" โ€” Cindy Sridharan.
  • NIST SP 800-92 (Guide to Computer Security Log Management).

Sign in to save your progress and earn badges.