AI Agent Audit Log: Immutable Trails, Compliance, and Forensic Investigation
An AI agent audit log is an immutable, append-only record of every action an agent takes, written before execution completes, cryptographically attributed to a specific agent and timestamp, distinct from observability data in that it proves what happened to an auditor or regulator rather than showing current system health. Three compliance standards require it: SOC 2 Type II CC7.2 (1-year minimum retention), GDPR Article 30 (records of processing activities), and OWASP LLM06:2025 (immutable audit trail as one of four mandatory mitigations).
An AI agent audit log is an immutable, append-only record of every action an agent takes: every tool call, every state change, every credential access, written before execution completes, attributed to a specific agent_id and timestamp, and protected from modification by the agent or any downstream process, so that compliance auditors, security investigators, and regulators can independently verify what an agent did, when it did it, and with what parameters, without relying on the agent's own account.
Audit Log vs. Observability: Two Different Evidence Standards
What Observability Data Cannot Prove
Observability answers the question "is the system healthy right now?" Audit logs answer "what exactly did agent X do on March 15 at 14:32, with what parameters, and what was the outcome?" These are different evidence standards with different storage requirements, different retention periods, and different legal standing.
Observability data is typically sampled (not every event), aggregated (individual events are rolled up into metrics), and short-retained (30–90 days is standard for metrics and traces). It tells you that the p99 latency was 2.3 seconds last Tuesday, not that agent researcher-01 called http_request with url=https://api.external.com/export?user_id=4821 at 14:32:07 UTC.
Audit logs must be individual events (not aggregated), complete (every call including failed and rejected ones), and long-retained (SOC 2 requires a minimum of 1 year; GDPR records commonly retained for the contract duration plus applicable limitation periods).
The most common audit failure in production agent deployments: teams present observability dashboards (Datadog, Grafana) to SOC 2 auditors as evidence of CC7.2 compliance. Auditors reject these because observability dashboards cannot show individual event records, cannot prove completeness, and cannot prove immutability. SOC 2 CC7.2 requires individual event records that the agent cannot modify.
The Four Properties of an Audit-Quality Log Record
Pre-execution: the log entry is written before the action completes. The timestamp predates the outcome. This matters forensically: an agent that can write log entries after the fact can also write false entries or omit entries for actions it wants to conceal. Pre-execution logging means the entry exists regardless of whether the action succeeds, fails, or is interrupted.
Immutability: the record cannot be modified or deleted by the agent, by the operator, or by any automated process. WORM (Write Once, Read Many) storage or cryptographic hash chains provide immutability at the infrastructure layer. Immutability at the application layer ("the agent is instructed not to delete logs") is insufficient for compliance purposes; auditors require infrastructure-layer immutability.
Completeness: every call must be recorded, including failed calls, rejected calls, rate-limited calls, and calls that returned errors. A log that only records successful actions is incomplete for forensic purposes; the forensic question after an incident is often "what did the agent try to do that failed?" The rejected call log is the primary evidence of prompt injection attempts.
Attribution: agent_id, session_id, tool_call_id, and request timestamp must be cryptographically bound at write time and not mutable metadata that can be changed later. Attribution after the fact does not satisfy GDPR Article 30's requirement that records identify the controller and processor responsible for each processing activity.
Compliance Requirements: SOC 2, GDPR, and OWASP LLM06
SOC 2 Type II CC7.2: 1-Year Minimum Retention
SOC 2 Type II Common Criteria 7.2 (System Monitoring) requires that system activity be logged and that logs are available for review over the audit period. For AI agent systems:
Individual event records required: each tool call must be a separate log entry with agent_id, timestamp, tool name, full parameters, and result. Aggregated metrics (100 tool calls in the last hour) do not satisfy CC7.2; individual event records are required so auditors can inspect specific events.
Minimum 1-year retention: the audit period for SOC 2 Type II is typically 6 or 12 months. Logs must cover the full audit period. Best practice is to retain for 1 year plus a buffer (13–14 months) to handle audit windows that don't align with calendar years.
Agents cannot modify their own records: SOC 2 auditors specifically check that the entities being audited cannot modify their own audit records. An agent that can call a log-deletion API fails this control. OpenLegion's Zone 2 audit trail is written by the mesh infrastructure, not by agent code; the agent cannot access the log write path.
Evidence of regular review required: CC7.2 also requires evidence that logs are reviewed: an automated anomaly detection layer plus periodic human review documented in a review log.
GDPR Article 30: Agent Actions on Personal Data
GDPR Article 30 requires that data controllers and processors maintain records of processing activities. For AI agent systems, every agent operation that touches personal data is a processing activity that must be documented.
What must be recorded for GDPR Article 30 compliance:
- Agent_id and controller identity: who is responsible for this processing activity?
- Purpose of processing: why did the agent access this personal data?
- Categories of personal data: what types of data did the agent access?
- Recipients: which external systems or agents received the data?
- Legal basis: which GDPR legal basis applies?
- Retention period or criteria: when will the data be deleted?
Records must be available to supervisory authorities on request. The fine for failure to maintain Article 30 records is up to €10 million or 2% of global annual turnover, whichever is higher.
The most frequent compliance gap: agent audit logs record tool names and raw parameters but do not identify which parameters contain personal data. An Article 30 record requires that identification, along with purpose and legal basis.
OWASP LLM06:2025: Mandatory Immutable Audit Trail
OWASP LLM06:2025 (Excessive Agency) lists an immutable audit trail as one of four mandatory mitigations for autonomous agents:
Every tool call including rejected ones: rejected calls, blocked by the tool allowlist, by HITL gate, or by an open circuit breaker, must appear in the audit trail. Rejected calls are the primary evidence of prompt injection attempts and safety control effectiveness.
Written before execution completes: pre-execution. Post-execution logging allows an agent to skip logging for actions it performs and then terminates before the log write.
Full arguments recorded: the audit entry must include full tool call arguments, not just the tool name. This is the field that security forensics teams use to answer "what URL did the agent call?" and "what data was passed to the external API?"
Immutability: agents must not be able to delete or modify their own records. This is the control that makes the audit trail useful for forensic investigation; if the agent could modify the trail, post-incident investigation could not rely on it.
The audit trail is a detective control, not a preventive control. It does not prevent unauthorized actions. It ensures unauthorized actions are discoverable afterward, enabling incident response and regulatory reporting.
Audit Log Schema: What to Record and When
OpenTelemetry OTLP Log Schema for Agent Audit Records
OpenTelemetry OTLP proto v1.0 (July 2023) provides the standardized structure for agent audit records. Using OTLP enables SIEM integration without custom parsers and distributed trace correlation via TraceId and SpanId.
Required OTLP fields for agent audit records:
{
"TimeUnixNano": "1719100800000000000",
"ObservedTimeUnixNano": "1719100800050000000",
"SeverityNumber": 9,
"SeverityText": "INFO",
"Body": {
"event_type": "tool_call",
"agent_id": "researcher-01",
"session_id": "sess_a3f9b2c1",
"tool_call_id": "tc_7e8d4f12",
"tool_name": "http_request",
"arguments": {
"url": "https://api.example.com/data",
"method": "POST",
"headers": {"Authorization": "$CRED{api_key}"},
"body_hash": "sha256:a4f3b2c1..."
},
"result_summary": "HTTP 200, 1842 bytes",
"execution_status": "success",
"rejection_reason": null,
"duration_ms": 312,
"cost_usd": 0.0042,
"personal_data_fields": [],
"credential_handles_used": ["api_key"]
},
"Attributes": {
"agent_id": "researcher-01",
"session_id": "sess_a3f9b2c1",
"tool_call_id": "tc_7e8d4f12",
"tool_name": "http_request",
"authorization_level": "L2",
"deployment_env": "prod"
},
"TraceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"SpanId": "00f067aa0ba902b7",
"Resource": {
"agent_type": "researcher",
"agent_version": "0.4.2",
"deployment_env": "prod"
}
}
Three field notes critical for compliance:
ObservedTimeUnixNano: the time the log was received by the log pipeline, distinct from TimeUnixNano. The difference detects clock skew and retroactive log injection; a log entry with ObservedTimeUnixNano earlier than TimeUnixNano was written before the event it describes, which is physically impossible and indicates tampering.
arguments.headers with $CRED{} handles: the audit log records the credential handle ($CRED{api_key}), not the resolved credential value. The raw API key never appears in the log.
personal_data_fields: an array identifying which argument fields contain personal data for GDPR Article 30 compliance. The log pipeline checks this field to apply appropriate retention rules and to generate Article 30 records.
PII Scrubbing Before Log Write
GDPR Article 17 (right to erasure) creates a compliance tension: audit logs must be retained for 1+ years (SOC 2) but personal data must be deleted on subject request. The resolution is pseudonymization: replace personal data in log records with a deterministic hash before writing to the immutable log.
import hashlib
PSEUDONYMIZATION_SALT = "$CRED{pii_salt}" # Resolved from vault; never in code
def pseudonymize_args(args: dict, pii_fields: list[str]) -> dict:
"""Replace PII fields with HMAC-SHA256 pseudonyms before log write."""
sanitized = dict(args)
for field in pii_fields:
if field in sanitized:
value = str(sanitized[field])
pseudonym = hashlib.sha256(
f"{PSEUDONYMIZATION_SALT}:{value}".encode()
).hexdigest()[:16]
sanitized[field] = f"pii:{pseudonym}"
return sanitized
The deterministic pseudonym preserves forensic correlation: all log entries involving the same user produce the same pseudonym, enabling forensic queries without raw identifiers. On GDPR erasure request, rotate the salt; all existing pseudonyms become permanently unlinkable to the original data subject, satisfying Article 17 without deleting audit records.
Safe default retention: 3 years. This covers SOC 2 12-month audit periods (with buffer), typical GDPR limitation periods, and most contractual retention requirements.
Tamper-Evidence and Immutable Storage
Cryptographic Hash Chains
A hash chain provides tamper-evidence at the log sequence level: if any record is modified or deleted, the chain breaks and the modification is detectable.
import hashlib, json
class AuditLogChain:
def __init__(self, previous_hash: str = "genesis"):
self.previous_hash = previous_hash
def append(self, record: dict) -> dict:
"""Append a record to the chain, returning the sealed entry."""
record_with_prev = {**record, "previous_hash": self.previous_hash}
serialized = json.dumps(record_with_prev, sort_keys=True).encode()
current_hash = hashlib.sha256(serialized).hexdigest()
sealed = {**record_with_prev, "record_hash": current_hash}
self.previous_hash = current_hash
return sealed
Each record includes the hash of the previous record. Modifying any record changes its hash, cascading to invalidate all subsequent hashes. An auditor verifies the chain by recomputing hashes from record 1 forward; any break indicates a modification and its position.
Hash chains detect modification but do not prevent it. An adversary who controls the log storage can reconstruct a valid chain over modified records by recomputing all hashes from the modification point. Prevent this with WORM storage.
WORM Storage: AWS S3 Object Lock
AWS S3 Object Lock with Compliance mode prevents objects from being modified or deleted during the retention period, not even by the root account or AWS support. This is the infrastructure-layer immutability that SOC 2 auditors and forensic investigators require.
Cost estimate: a moderately active agent produces approximately 55GB of log data per year. At $0.023/GB/month on S3 Standard:
- 1-year retention: 55GB × 12 months × $0.023 = ~$15/year per agent
- 3-year retention: 55GB × 36 months × $0.023 = ~$46/year per agent
AWS CloudTrail costs $2.00 per 100,000 management events; at $10–40/day per agent for high-frequency loops, direct-to-S3 logging is cheaper than CloudTrail for most agent workloads.
Immutable log storage configuration:
# Create bucket with Object Lock enabled (cannot be disabled after creation)
aws s3api create-bucket \
--bucket agent-audit-logs-prod \
--object-lock-enabled-for-bucket \
--region us-east-1
# Set default retention: Compliance mode, 3 years
aws s3api put-object-lock-configuration \
--bucket agent-audit-logs-prod \
--object-lock-configuration '{
"ObjectLockEnabled": "Enabled",
"Rule": {
"DefaultRetention": {
"Mode": "COMPLIANCE",
"Years": 3
}
}
}'
SIEM Integration and Real-Time Analysis
Routing Agent Audit Logs to Your SIEM
SIEM integration serves two purposes: real-time anomaly detection (detecting prompt injection attempts in progress) and forensic investigation (querying historical records after an incident). The OTLP schema enables both.
| SIEM | Ingestion method | Agent log volume cost (est.) |
|---|---|---|
| Splunk | OTLP Collector → Splunk HEC | $1.5–3/GB ingested |
| Elastic SIEM | OTLP Collector → Logstash | $0.10–0.50/GB (self-hosted) |
| Microsoft Sentinel | OTLP Collector → Log Analytics | $2.46/GB ingested |
| Datadog | OTLP Collector → Datadog Agent | $0.10/GB (standard) |
| OpenSearch | OTLP Collector → OpenSearch | Free (self-hosted) |
Three real-time detection rules to configure at SIEM ingestion:
Rule 1: Out-of-allowlist tool call: alert when tool_name in an audit record does not appear in the agent role's registered tool list. Fires even on rejected calls; a rejected out-of-allowlist call means injection was blocked, but the attacker's intent is visible.
Rule 2: Credential handle anomaly: alert when credential_handles_used contains a handle not in the agent role's authorized list. Primary detection signal for credential pivot attacks.
Rule 3: Rejection rate spike: alert when more than 20% of tool calls in a 5-minute window have execution_status: rejected. Indicates an active prompt injection campaign being blocked by safety controls.
Legal Hold and eDiscovery
When an agent-related incident triggers legal proceedings or regulatory investigation, a legal hold prevents log deletion or modification for the duration of the matter.
In S3 Object Lock, apply an explicit retention extension:
import boto3
from datetime import datetime, timedelta
s3 = boto3.client('s3')
def apply_legal_hold(bucket: str, prefix: str, hold_until: datetime):
paginator = s3.get_paginator('list_objects_v2')
for page in paginator.paginate(Bucket=bucket, Prefix=prefix):
for obj in page.get('Contents', []):
s3.put_object_retention(
Bucket=bucket,
Key=obj['Key'],
Retention={
'Mode': 'COMPLIANCE',
'RetainUntilDate': hold_until
}
)
# Apply 5-year hold on logs from the incident date
apply_legal_hold(
bucket='agent-audit-logs-prod',
prefix='2026/03/15/',
hold_until=datetime.now() + timedelta(days=1825)
)
eDiscovery production: export log records within a date range and agent_id scope as JSON-L with a manifest file that includes S3 Object Lock retention metadata as provenance evidence.
Forensic Investigation After Agent Incidents
The Forensic Timeline: Reconstructing What the Agent Did
After a security incident, the forensic investigation reconstructs the exact sequence of agent actions. Standard forensic timeline query pattern:
from datetime import datetime
def build_forensic_timeline(
agent_id: str,
session_id: str,
start_time: datetime,
end_time: datetime,
log_store
) -> list[dict]:
timeline = []
for record in log_store.query(
filter={
"agent_id": agent_id,
"session_id": session_id,
"time_range": (start_time, end_time)
}
):
timeline.append({
"timestamp": record["TimeUnixNano"],
"tool": record["Body"]["tool_name"],
"status": record["Body"]["execution_status"],
"args_summary": record["Body"]["arguments"],
"cost_usd": record["Body"]["cost_usd"],
"rejection_reason": record["Body"].get("rejection_reason"),
"record_hash": record.get("record_hash"),
})
return sorted(timeline, key=lambda r: r["timestamp"])
Four questions a forensic timeline must answer:
What did the agent do? The tool_name sequence, including rejected calls. Rejected calls show what the agent attempted that safety controls blocked.
What data did it access? The arguments fields. For each tool call involving external services, what parameters were passed? If personal_data_fields is populated, what personal data was involved?
What credentials were used? The credential_handles_used field. If a handle appears in an unexpected context, such as the researcher agent using the publisher's GitHub token, this is the forensic finding.
What did external systems return? The result_summary field. If the agent exfiltrated data, HTTP 200, N bytes received is the evidence of successful exfiltration.
Chain-of-Custody Documentation
For forensic evidence to be admissible in legal proceedings, it must maintain chain-of-custody documentation. In S3, enable S3 Access Logging for the audit log bucket; every GET request creates an access log entry with the requester's IAM identity and timestamp. This is the chain-of-custody record for the audit logs themselves.
When exporting logs for external parties (regulators, auditors, opposing counsel), use S3 presigned URLs with limited expiry (24–48 hours) rather than IAM credential sharing. The presigned URL creates a time-bounded access record of specific objects accessed and when.
OpenLegion's Take
The audit log is the most under-resourced compliance control in production agent systems. Teams invest in tracing, metrics, and dashboards, valuable for operations, but defer the audit log as "something to add later." Later arrives when a SOC 2 auditor asks for 12 months of individual event records, or when a regulator requests Article 30 documentation for an agent that processed customer data six months ago.
OpenLegion's blackboard is append-only by design: every write is versioned, timestamped, and attributed to an author agent_id. Zone 2 logs every tool call before execution completes. Neither is accessible to agent code for modification. OpenLegion deployments have the pre-execution, immutable, attributed log structure that compliance requires from day one.
The cost argument for early investment: retrofitting an immutable audit log into a production agent system requires changes to the tool call pipeline, storage infrastructure, and log format, typically 2–4 weeks of engineering. Building it correctly at deployment adds one to two days. S3 Object Lock with 3-year retention costs approximately $46/year per agent. The cost of a SOC 2 audit failure caused by an insufficient log is the loss of the audit certification, which typically blocks enterprise customer sales for 6–12 months.
The PII pseudonymization pattern solves the GDPR/SOC 2 retention tension at low complexity cost. It is worth implementing from the first agent that touches personal data; retrofitting pseudonymization into an existing log with raw PII is more complex than building it correctly initially.
For the AI agent security controls that generate the audit events this log captures, rejected tool calls and prompt injection attempts are the most forensically valuable entries. For the AI agent governance framework that determines retention policies and review cadences, the audit log is the evidentiary foundation.
Get Started
Deploy AI agents with built-in append-only audit trails, OTLP-structured records, and S3 Object Lock immutability.
Frequently Asked Questions
What is an AI agent audit log and how is it different from observability data?
An AI agent audit log is an immutable, append-only record of every action an agent takes, written before execution completes and protected from modification. Observability data (metrics, traces, dashboards) answers "is the system healthy now?" and is typically sampled, aggregated, and retained 30–90 days. Audit logs answer "what exactly did this agent do at 14:32 on March 15?" and must be complete individual event records retained for 1+ years. SOC 2 CC7.2 and GDPR Article 30 require audit-quality records; observability dashboards do not satisfy either standard.
What does SOC 2 Type II CC7.2 require for AI agent audit logs?
SOC 2 CC7.2 (System Monitoring) requires individual event records (not aggregated metrics) for every tool call, with agent_id, timestamp, full parameters, and result. Minimum 1-year retention covering the full audit period is required. The audited entity must not be able to modify its own records; auditors specifically check this. Automated anomaly detection evidence and periodic review documentation are also required. Observability dashboards fail this control because they cannot show individual events, prove completeness, or prove immutability.
What does GDPR Article 30 require for agents that process personal data?
GDPR Article 30 requires records of processing activities for every agent operation on personal data: agent_id, purpose, categories of personal data accessed, recipients, legal basis, and retention period. Records must be available to supervisory authorities on request. Fines for failure reach €10 million or 2% of global annual turnover. Every tool call that includes personal data in its parameters, such as user emails, customer IDs, or financial records, requires an Article 30 record identifying those fields as personal data along with purpose and legal basis.
What fields must every agent audit log entry contain?
At minimum: agent_id, session_id, tool_call_id, timestamp (pre-execution), tool name, full arguments (with credential handles as $CRED{} opaque tokens and PII pseudonymized), execution status (success/failure/rejected), rejection reason if rejected, duration, cost, credential handles used, and any personal data fields. The stable OTLP Log specification provides the standard structure with TimeUnixNano, ObservedTimeUnixNano (for tamper detection), TraceId and SpanId (for distributed trace correlation), and a structured Body object.
How do I make an audit log immutable without preventing legitimate operations?
Two mechanisms in combination: cryptographic hash chains (each record includes the hash of the previous record, making modification detectable by cascading hash invalidation) and WORM storage at the infrastructure layer (AWS S3 Object Lock with Compliance mode prevents deletion or modification even by the root account during the retention period). Normal log reads and new appends are permitted; modification of existing records is not. Create the S3 bucket with object lock enabled (cannot be disabled after creation) and set default retention to Compliance mode for 3 years.
How do I handle GDPR right-to-erasure requests when audit logs must be immutable?
Use pseudonymization: replace personal data fields with deterministic HMAC-SHA256 hashes (using a stored salt from the credential vault) before writing to the immutable log. The pseudonym is stable, enabling forensic correlation across log entries for the same data subject. On erasure request, rotate the salt; all existing pseudonyms become permanently unlinkable to the original data subject, satisfying Article 17 without deleting the audit records. The audit log is preserved intact; only the ability to link pseudonyms back to real identities is destroyed.
What S3 Object Lock configuration should I use for agent audit logs?
Use Compliance mode (not Governance mode). Compliance mode prevents modification or deletion by any account, including root; Governance mode allows authorized IAM users to override the lock. A moderately active agent generates approximately 55GB/year of log data, costing roughly $15/year for 1-year retention or $46/year for 3-year retention at $0.023/GB/month on S3 Standard. Enable S3 Access Logging on the audit bucket so that investigator access is itself logged as chain-of-custody documentation.
How do I build a forensic timeline from audit logs after an agent incident?
Query the audit log store by agent_id, session_id, and time range. Sort records by TimeUnixNano. For each record, extract: tool name, execution status (including rejections), argument summary, credentials used, and result summary. Verify hash chain integrity by recomputing hashes from record 1 forward; any break indicates tampering and its position. Four questions the timeline must answer: what tools did the agent call (including rejected calls)? What data did it access? Which credential handles appeared in unexpected contexts? What did external systems return? Export as JSON-L with S3 Object Lock metadata as provenance for regulatory submissions.