# Implementing Audit Logging for Agentic Decisions: A Schema-First Approach

*By the compliance engineering team at OnlineCloud — a US-based Agentic AI & MCP engineering studio*
—
## 1. Evidence, Not Transcripts: Why Chat Logs Fail Audit
Regulators do not ask what your agent *intended*; they ask what it *did* — which systems it touched, which records it read or modified, under whose authority, at what moment, and with what approval. When the SOC 2 assessor or the external auditor arrives, the most common defense I see is a database full of chat transcripts, and it collapses in the first fifteen minutes.
An unstructured chat log is a conversational artifact, not an evidentiary record. It captures what the model *said*, paraphrased for a human reader — not what it actually executed. The refund the agent processed appears in the transcript only if the model happened to mention it, and models summarize, elide, and occasionally confabulate. The tool call that mutated a ledger row, the exact arguments passed, the row-level security context under which the query ran, the approval that authorized it: none of that was ever designed to be in the transcript.
Three structural failures make chat logs unusable for audit. **Incompleteness:** an agentic run is mostly invisible machinery — planning, tool invocations, retries, aborted branches — so a transcript cannot attest to activity that was never recorded. **Non-verifiability:** transcript text cannot be cryptographically anchored, so you cannot prove it was not edited, reordered, or regenerated. **Non-reconcilability:** transcripts carry no stable identifier linking an utterance to the database transaction it caused, so you cannot trace an anomalous write back to the decision behind it.
The fix is a change of frame: stop treating agent runs as conversations to be stored and treat them as *transactions to be journaled*. Every execution should emit a structured, versioned, append-only stream of decision events — a schema that defines, up front, what an auditable decision looks like. This post is the field guide: the schema, the log anatomy, immutable storage, the control mapping, and the replay tooling that turns raw events into evidence.
## 2. The Decision Schema: A Parent-Child Tree of Execution
Schema-first means the audit contract is defined *before* the agent runs, not reconstructed after an incident. If every tool, sub-agent, and orchestration loop must emit events conforming to one versioned schema, completeness stops being a matter of developer discipline and becomes a property of the system: an event that does not conform cannot be written.
The core insight: an agentic execution is not a flat sequence of log lines — it is a **tree**. A run begins with a goal; the orchestrator plans and spawns branches; each branch invokes tools that return results, trigger retries, request approval, or spawn sub-agents; some branches abort. Linear logs destroy that structure. A parent-child event model preserves it, so an auditor can walk from the initiating user request down to the tool call that changed a row — aborted branches included, because proving what was considered and rejected is often as important as what succeeded.
Every event carries a common envelope: a globally unique `event_id`; a `run_id` grouping the whole decision; `parent_event_id` linking the event to the step that caused it; an `event_type` from a closed vocabulary; `occurred_at` in UTC with sub-second precision; the acting principal and its full delegation chain; and a monotonic sequence number for ordering within the run.
“`json
{
“$schema”: “https://schemas.onlinecloud.dev/agentic-audit/event.schema.json”,
“title”: “AgenticAuditEvent”,
“type”: “object”,
“required”: [
“schema_version”, “event_id”, “run_id”, “parent_event_id”,
“event_type”, “occurred_at”, “sequence”, “principal”
],
“properties”: {
“schema_version”: { “const”: “1.0” },
“event_id”: { “type”: “string”, “format”: “uuid” },
“run_id”: { “type”: “string”, “format”: “uuid” },
“parent_event_id”: { “type”: [“string”, “null”], “format”: “uuid” },
“event_type”: {
“enum”: [
“run_started”, “plan_created”, “sub_agent_spawned”,
“tool_invoked”, “tool_completed”, “tool_failed”,
“approval_requested”, “approval_granted”, “approval_denied”,
“branch_aborted”, “run_completed”, “run_failed”
]
},
“occurred_at”: { “type”: “string”, “format”: “date-time” },
“sequence”: { “type”: “integer”, “minimum”: 0 },
“principal”: {
“type”: “object”,
“required”: [“initiating_user”, “delegation_chain”],
“properties”: {
“initiating_user”: { “type”: “string” },
“delegation_chain”: { “type”: “array”, “items”: { “type”: “string” } }
}
},
“tool”: {
“type”: “object”,
“properties”: {
“name”: { “type”: “string” },
“arguments”: { “type”: “object” },
“argument_hash”: { “type”: “string” },
“result_status”: { “enum”: [“success”, “error”, “timeout”, “rejected”] },
“result_ref”: { “type”: “string”, “description”: “Pointer to stored result in the evidence store” }
}
},
“model”: {
“type”: “object”,
“properties”: {
“model_id”: { “type”: “string” },
“prompt_version”: { “type”: “string” },
“reasoning_ref”: { “type”: “string” },
“tokens_in”: { “type”: “integer” },
“tokens_out”: { “type”: “integer” }
}
},
“access_context”: {
“type”: “object”,
“properties”: {
“tenant_id”: { “type”: “string” },
“database_role”: { “type”: “string” },
“effective_scope”: { “type”: “string”, “description”: “RLS predicate actually enforced” }
}
}
}
}
“`
Three design rules matter in practice. First, the schema is **closed for event types but open for attributes**: add fields by bumping `schema_version`, never by overloading existing ones, so replay tools migrate deterministically. Second, every node carries its `parent_event_id` — an event without a parent means a malformed run, and the stream should alarm. Third, the schema is owned by the compliance function, not the model team — a feature that cannot emit conforming events cannot ship.
## 3. Audit Log Anatomy: Tool Inputs, RLS Context, and Reasoning
An envelope without payload is a timestamp. The evidentiary value lives in three payload classes, each with its own capture discipline.
**Tool inputs and outputs.** The most audited fact in an agentic system is: *what did the tool actually receive, and what did it return?* Capture the validated arguments *after* policy enforcement — schema-validated, scoped, sanitized — not the raw model proposal, which may contain hallucinated or injected content. Record an `argument_hash` over the canonical serialization so payloads can be integrity-checked without re-reading them, and write full results to a separate evidence store behind a `result_ref` pointer. Redact secrets, tokens, and full PII at the boundary before writing; a compliant log must not become a data breach. Record the outcome truthfully — success, error, timeout, or policy rejection — because a rejected call is an audit event too, often the most interesting one in an incident review.
**RLS context.** Row-level security is what makes agentic database access defensible, but the auditor needs proof the boundary was *actually enforced* at the moment of the call. Capture the live access context: the tenant or account ID, the database principal, the propagated delegation chain (initiating user → orchestrator → sub-agent), and the effective predicate the RLS layer appended — the `SYS_CONTEXT` values that drove an Oracle VPD policy, or the session context a Postgres or SQL Server row-security policy read. When you can show the auditor that a customer query ran under `tenant_id = 4421` with the initiating user’s delegation chain, you have converted “the agent can only see its own data” from an assertion into a demonstrated control.
**Model reasoning steps.** The uncomfortable question is whether to log chain-of-thought. My guidance: capture *decision-relevant* reasoning, not the raw hidden trace. Log the plan the model committed to, the retrieved-context identifiers that informed each step (which documents and tool results entered the window), the model and prompt versions, and the outcomes at approval gates. If you persist full reasoning tokens — and there are legitimate forensic reasons to — treat them as the most sensitive material in the estate: separately encrypted, access-controlled to the compliance role, excluded from end-user transcripts, and bound to their own retention schedule, because reasoning traces routinely contain PII that was never meant to be stored. The schema models this with a `reasoning_ref`: a pointer, not an inline dump.
## 4. Immutable Storage: Object Lock or Write-Only RDS
An audit trail you can edit is a narrative. Immutability has one job: make the record *survive the compromise it is designed to detect*. Two production patterns dominate, and both rest on the same rule — **the write path must be reachable only by credentials the agents cannot hold, and no code path in the agent runtime may delete or modify events.**
**Pattern A: S3 with Object Lock in Compliance mode.** Ship events as objects keyed by `run_id/sequence`, versioned, into a bucket with Object Lock in Compliance mode and a retention period exceeding your records schedule — align it to legal counsel and any SOX-relevant retention. In Compliance mode, not even the root account can shorten retention or delete an object before the period lapses; that is the property you are buying. Give the agent runtime’s IAM role `s3:PutObject` only, with a bucket policy denying `s3:DeleteObject`, `s3:DeleteObjectVersion`, and `s3:PutBucketPolicy` to every role the model can assume, and enforce SSE-KMS with a key the agent role cannot use. Buffer events through SQS or Kinesis so the producer never talks to the bucket directly — the queue is the only door — and alarm on any write that bypasses the conforming path. Lifecycle-tier to Glacier after the active window; the retention clock still runs on the locked object.
**Pattern B: write-only RDS.** For teams that want the trail beside the business data, use a dedicated audit schema where the application role holds `INSERT` only — `UPDATE`, `DELETE`, and `TRUNCATE` privileges simply do not exist on the role, `DROP` belongs to a separate compliance role, and a trigger rejects any mutation of the event payload. Anchor the chain cryptographically: each row stores a hash of the previous row’s canonical serialization, producing a tamper-evident hash chain that makes silent back-dating detectable on replay. A periodic integrity job recomputes the chain and pages compliance on any break.
Choose between the patterns on your threat model and cost curve — object storage is cheaper at volume and carries native WORM semantics; write-only RDS keeps events queryable in place. The non-negotiable is identical: the audit store is independent infrastructure, its credentials are provisioned outside the agent’s identity graph, and its integrity is monitored as a control, not assumed as a feature.
## 5. Compliance: Reconciling Decision Trails with SOC 2 and SOX
The schema and storage above are not abstractions; they map one-to-one onto the control frameworks your auditors will invoke.
**SOC 2 CC-series.** The AICPA Trust Services Criteria are explicit that logical access must prevent unauthorized access to systems and data (CC6), that operations must be monitored for anomalies (CC7), and that change management must be evidenced (CC8) — and an agentic deployment strains all three. CC6.1 through CC6.3 require access to be granted by role and logged: the decision schema’s delegation chain is your evidence of which principal held which capability at decision time. CC6.6 requires the audit trail itself to be protected from alteration and unauthorized destruction — that is Section 4, verbatim. CC7.2 and CC7.3 require anomaly detection and response: your malformed-event alarms and hash-chain integrity jobs are the monitoring controls. When the assessor asks how you know the agent only touched approved data under approved identities, the answer is a replayable, immutable decision tree — not a screenshot of a chat window.
**SOX.** Where agents touch financial processes — revenue recognition, refunds, journal entries, close activities, order-to-cash — the trail becomes evidence over internal control over financial reporting. Under Sections 302 and 404, management must certify, and auditors attest to, the design and operating effectiveness of controls around financial data, including the *automated* controls your agents now embody. An agent that posts a journal entry is a control activity; the question is whether its execution is auditable. The parent-child tree supplies the ITGC evidence the engagement team will demand: the initiating user’s authority (segregation of duties — the agent cannot approve its own irreversible action, because `approval_granted` events come from a human checkpoint identity), the change record (model and prompt versions pinned per event), and the data integrity chain (hash-linked events reconciling to the ledger). Retain SOX-relevant decision trails as long as the underlying financial records — commonly seven years — and design the Object Lock window accordingly, because retroactive retention is not a thing in object storage.
The reconciling rule is simple to state and hard to fake: **every control assertion about the agent must cite a decision-tree artifact.** “Access was scoped” cites RLS-context events; “changes were authorized” cites approval-checkpoint events; “nothing was altered” cites the hash chain. A control that cannot cite an event does not exist.
## 6. Forensic Replay: Rebuilding the Trajectory for Incident Review
The final layer pays for all the others: tooling that turns the event stream back into a decision. Replay takes a `run_id`, assembles the tree by resolving `parent_event_id` links, and renders the trajectory as an interactive timeline — goal, plan, each tool invocation with its validated arguments and outcome, approval gates, aborted branches, and reasoning pointers. Because every event carries the delegation chain and UTC timestamps, the tool answers the questions incident reviewers actually ask: *What did user X’s session do between 14:02 and 14:09? Which tool calls touched records outside tenant 4421? Which write operations ran without an approval checkpoint? Did this refund decision precede or follow Tuesday’s prompt-version change?*
Build replay with four properties. **Full fidelity:** include failed and rejected events, not just successes — incidents hide in branches that were denied and retried with mutated arguments. **Role-based redaction:** the replay layer re-applies the ingestion redaction policy, so a Tier-1 investigator sees arguments with secrets masked while the compliance role can expand them under a documented access reason. **Integrity verification first:** validate the hash chain before rendering; if the store shows tampering, the tool says so before it shows anything else. **Exportable evidence:** every view exports to a signed, timestamped artifact (CSV, JSON, or PDF) that reaches an auditor or opposing counsel without another engineer in the loop. The benchmark of a good replay tool is boring: an external reviewer can reconstruct what happened, in order, with citations, in an afternoon — with no access to the production agent.
## The Audit Trail Is the Product
The agentic era has not changed the first law of auditable systems: if you cannot prove what happened, from the outside, after the fact, it did not happen in the eyes of your auditors. Unstructured chat logs fail that test on day one. A schema-first decision tree — structured events, captured tool inputs, enforced RLS context, immutable storage, framework mapping, replay tooling — passes it, because it treats the audit trail as a first-class system property, not a logging afterthought. Organizations that deploy agents without it will learn the lesson in an incident review; those that ship it will hand the assessor a replayable decision tree and watch the room relax. Choose which meeting you want to be in.
—
**Designing an audit trail for agentic decisions? Talk to the OnlineCloud architects.** We are a US-based Agentic AI & MCP engineering studio — senior engineers only, no offshore handoff — and we ship evaluated, observable, audited systems into production in 6–10 weeks. From decision schemas and MCP tool surfaces to Object Lock pipelines and write-only audit stores, we build compliance into the architecture, not the slide deck. Contact us at [contact@onlinecloud.dev](mailto:contact@onlinecloud.dev) or visit [onlinecloud.dev](https://www.onlinecloud.dev/) for a free initial consultation — we respond within 24 hours.
Leave a Reply