
AGENT OPERATIONS
What Is an Agent Audit Trail?
NIST already specifies what an audit record must establish. Agents break exactly one of the six elements, and AWS and Google both ship the field that fixes it.
TL;DR
An agent audit trail is the record NIST already specified, with exactly one of its six elements broken. SP 800-53 control AU-3 says an audit record must establish what type of event occurred, when it occurred, where, the source, the outcome, and the "identity of any individuals, subjects, or objects/entities associated with the event." Five of those are easy. The sixth now has two correct answers at once.
AU-10, non-repudiation, names the case precisely: irrefutable evidence that "an individual (or process acting on behalf of an individual) has performed" an action. An agent is that process. A schema with one identity column cannot tell the agent apart from the person whose authority it borrowed.
The usual instinct is that more logging closes this. It doesn't. The two largest cloud audit logs solved
the identity half years ago, in fields most teams never populate: CloudTrail's sourceIdentity and
onBehalfOf, Cloud Audit Logs' serviceAccountDelegationInfo.
We are not arguing for a new logging product either. OCSF, now a Linux Foundation project, publishes an
API Activity class with uid 6003 whose required attributes already include actor, api, src_endpoint
and time. The schema exists.
Three things to do today: add the acting-user field, record the authorization decision on denied calls and not only on allowed ones, and check whether the data-access logs you assume are running are switched on. Google's are off by default.
Overview
An agent audit trail is a record of what an AI agent did. Not what it was configured to do, not what it was allowed to do: what it actually did, action by action, in the systems it touched.
Take a data-analytics agent that queries a customer warehouse on a Tuesday afternoon. Six months later
someone asks whether that query was allowed. The warehouse log shows a connection from a shared service
account and a SELECT, which is
aact:audit-granularity/none-distinguishable.
It does not name the analyst who asked, record that a scope check ran, or say what
the check decided. The query is logged. The event is not.
That gap is unusually well specified, which is the useful part. One control says what an audit record must establish, a second covers the delegated case, and two cloud providers already ship the delegation fields. This post is about what makes a record an audit trail rather than a log, against those references. Designing the schema field by field is covered in audit logging best practices, and live telemetry in what is agent visibility.
What a Standard Requires
NIST SP 800-53 Revision 5 puts the definition in control AU-3, Content of Audit Records. An audit record has to establish six things: "What type of event occurred; When the event occurred; Where the event occurred; Source of the event; Outcome of the event; and Identity of any individuals, subjects, or objects/entities associated with the event."
Read that against an ordinary application log line and four of the six are usually present. Type, time, place, source. Outcome is patchy, because plenty of services log the request and not the result, and a record without an outcome cannot tell an attempt from an act. Identity is where agents break the control, and AU-3(1) is the escape hatch: "Generate audit records containing the following additional information".
AU-10, Non-repudiation, names the agent case without ever mentioning agents. It asks for "irrefutable evidence that an individual (or process acting on behalf of an individual) has performed" some organization-defined set of actions. The parenthesis is the whole subject here. Its first enhancement, AU-10(1), asks you to "bind the identity of the information producer with the information", which for an agent means binding two producers, because both are real: the process that issued the call and the person whose authority made it permissible.
A record that answers "who did this" with one name is not incomplete by accident. Where a process acts for a person, the honest answer has two parts, and a schema with one identity column forces you to throw one of them away.
AU-9 comes at it from the other direction: protect audit information "from unauthorized access, modification, and deletion", alert on any of the three, and at the High baseline implement "cryptographic mechanisms to protect the integrity of audit information and audit tools". A trail an administrator can quietly edit is evidence of nothing.
Beyond Application Logs
Most enterprises already have logs. Whether those logs can reconstruct an agent's behaviour is a different question, and the reason they usually can't has nothing to do with volume.
An application log is written from the perspective of the service handling the request. It knows the endpoint, the caller's credential, and a status code. Right granularity for debugging a microservice, wrong one for an autonomous actor, because the facts that matter about an agent's action are not properties of the service. They are properties of the decision: who it was for, what allowed it, what came of it.
MCP's own security guidance makes the point from the attacker's side. Under "Token Passthrough", the
2026-07-28 specification lists accountability among the harms: a server that forwards tokens it was not
issued leaves the downstream resource server with "logs may show requests that appear to come from a
different source with a different identity". Nothing about that entry looks wrong to its owner. It is
confidently naming the wrong actor, which beats a gap for damage, because a gap announces itself during an
investigation and a wrong name does not.
What It Captures
A useful entry ties identity, intent, authorization and outcome together for one action, which is
aact:audit-granularity/per-action-with-principal-chain. The list below is
AU-3's six elements with the AU-3(1) additions agents specifically need.
- The agent: which agent initiated the action, and which version of it.
- The acting user: the human the agent was acting for, if any, held separately from the agent's own identity rather than in place of it.
- The action: what was attempted, named as the operation rather than as an HTTP verb.
- The tool or system: the destination, whether a SaaS platform, an internal API, or a database.
- The data: what was read or modified, at enough resolution to judge sensitivity and blast radius.
- The authorization decision: whether the scope check and the action check passed, which one failed if the call was refused, and the specific permission that was evaluated.
- The outcome: succeeded, failed, or denied, plus the reference the target system returned.
Figure 1 — One action produces one connected event, not a scattering of facts across three systems. The event is what gets queried later, not the systems it touched.
Serialized, a single entry for a procurement agent might read like this:
{
"timestamp": "2026-07-22T14:08:31Z",
"agent": "procurement-assistant@v3.2",
"acting_for_user": "j.okafor@company.com",
"action": "create_purchase_order",
"tool": "erp.purchasing.api",
"data": { "vendor": "Northwind Supply", "amount_usd": 18400 },
"authorization": { "permission": "po.create:under_20k", "granted": true },
"outcome": "success",
"reference": "PO-2026-04471",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}Anyone reading that six months later sees not only that a purchase order was created but by which agent, for
which requester, against which evaluated permission, to which result. The trace_id is a 32-hex-character
W3C Trace Context trace ID, which costs nothing and is what joins this record to whatever the ERP wrote on
its own side.
The Second Identity
The delegated-identity problem is neither new nor unsolved. Both large cloud audit logs model it, and the fields have existed long enough that the interesting question is why so few agent implementations fill them in.
AWS CloudTrail carries the acting identity in userIdentity. On a call made with temporary credentials from
an assumed role, sessionContext.sessionIssuer describes the role and sessionContext.sourceIdentity
"identifies the original user identity making the request, whether that user's identity is an IAM user, an
IAM role, a user authenticated by using SAML-based federation, or a user authenticated by using OpenID
Connect (OIDC)-compliant web identity federation". Through IAM Identity Center, userIdentity.type is
IdentityCenterUser and an onBehalfOf block carries userId and identityStoreArn. Where a third party
acts with delegated access, invokedByDelegate.accountId names the account.
Google Cloud Audit Logs splits the same information across authenticationInfo and authorizationInfo.
principalEmail is the caller, serviceAccountDelegationInfo carries the delegation chain, and
authorizationInfo records the permission checked plus a granted boolean, which is the closest thing in
either provider to a first-class authorization-decision field.
| The question | AWS CloudTrail | Google Cloud Audit Logs |
|---|---|---|
| Which identity issued the call | userIdentity.arn, userIdentity.type | authenticationInfo.principalEmail |
| On whose behalf | sessionContext.sourceIdentity, onBehalfOf.userId | authenticationInfo.serviceAccountDelegationInfo |
| What was attempted | eventName, requestParameters | methodName, resourceName |
| Was it allowed | errorCode, errorMessage | authorizationInfo[].permission, .granted |
Table 1 — The same two-identity question, answered by two providers' own field names.
Two things stand out. AWS needs configuration to produce the field that matters: sourceIdentity appears
only after an administrator sets STS up to require it, so a default install logs the role and loses the
person. And only Google models the authorization decision as data. On AWS you infer it from an errorCode,
so a denial and a timeout are distinguishable only by reading message text.
Agent (procurement-assistant@v3.2)
-> Authorization check: permission evaluated
|-- allowed --> ERP ------------
|-- denied --> no call made ----> Audit record:
agent + acting user + decision + outcome
Both branches write a record. The denied branch is the one usually missing.
-->
Figure 2 — Both branches write a record. The denied branch is the one most schemas drop, and it is the one an investigator asks for first.
Borrow a Schema
The temptation here is to design a field list. That is the wrong first move, because a maintained schema already exists and a private one costs you every integration you will later want.
The Open Cybersecurity Schema Framework describes itself as "an extensible framework for developing schemas,
along with a vendor-agnostic core security schema", hosted and governed by the Linux Foundation since
19 November 2024 under Apache 2.0. It sorts events into six categories, including Identity & Access
Management as category 3 and Application Activity as category 6. Inside category 6 sits API Activity, class
uid 6003, whose required attributes are activity_id, actor, api, category_uid, class_uid,
metadata, severity_id, src_endpoint, time and type_uid. The actor object is "the user, process,
or service that initiated the activity on the host", and it composes a Process and a User object rather than
a single string, which is the shape the delegated case needs.
The argument is not elegance. OCSF records are already ingestible by the tools a security team runs, so an
agent emitting class 6003 with the acting user in actor.user inherits search, correlation and retention it
did not have to build. Where a field is genuinely missing, extend it. That is what AU-3(1) and OCSF's
extension model are both for.
Where the Record Goes Quiet
Four failure modes turn a healthy-looking trail into one that cannot answer a question. Each is a default rather than a bug, which is why they survive reviews.
The logs were never switched on. Google's documentation is blunt: "Except for BigQuery, Data Access audit logs are disabled by default." Admin Activity logs are always written, so a console check looks reassuring while the reads and writes an agent actually performs go unrecorded. The symptom is a trail complete for configuration changes and empty for data.
Only the successful path is written. A denied call is often the most informative entry in the store, because it is evidence a boundary held and a burst of them is evidence of something trying. If the record is written by the code that handles a response, the denial path returns earlier and writes nothing.
The permission recorded is too coarse to mean anything. MCP's security guidance lists this under scope
minimisation as a direct cost: "Audit noise: single omnibus scope masks user intent per operation." An entry
saying the call was permitted by files:* tells a reviewer nothing the absence of an error would not have.
The same page tells servers to "log elevation events (scope requested, granted subset) with correlation IDs",
which is worth lifting verbatim into a schema.
The identity is present but wrong. The token-passthrough case above, and the only one of the four you cannot find by auditing your own records, because nothing about the entry looks unusual. The defence is upstream: MCP states that servers "MUST NOT accept any tokens that were not explicitly issued for the MCP server".
Integrity and Retention
Two properties decide whether a trail is still evidence when someone finally reads it: whether it can be altered, and whether it still exists.
AU-9(3) asks for cryptographic protection of audit integrity without prescribing a mechanism. CloudTrail's log file integrity validation is a readable implementation of one. It uses "SHA-256 for hashing and SHA-256 with RSA for digital signing"; every hour CloudTrail writes a digest file referencing the previous hour's log files with a hash of each, signs it, and stores digests under a separate prefix so the two can carry different access rules. The detail that makes it a chain rather than a pile: "Each digest file also contains the digital signature of the previous digest file if one exists." That is what lets you assert not only that a file is unmodified but that none was deleted.
Retention diverges by jurisdiction and by system, so the numbers are worth keeping in one place.
| Obligation | What it requires | Where it comes from |
|---|---|---|
| EU AI Act Article 12 | Automatic recording of events "over the lifetime of the system"; for Annex III point 1(a) systems, the start and end time of each use, the reference database, the matching input data and the persons verifying results | Article 12 |
| EU AI Act Article 26(6) | Deployer keeps logs under its control "for a period appropriate to the intended purpose ... of at least six months" | Article 26 |
| HIPAA 45 CFR 164.316(b)(2)(i) | Retain required documentation "for 6 years from the date of its creation or the date when it last was in effect, whichever is later" | 45 CFR 164.316 |
Google Cloud _Required bucket | 400 days, not configurable | Cloud Logging quotas |
Google Cloud _Default bucket | 30 days by default, custom retention configurable from 1 to 3,650 days | Cloud Logging quotas |
Table 2 — Retention floors and platform defaults. The last two rows are the ones that quietly decide the others.
Read the bottom two rows against the top three. A deployer under Article 26(6) needs six months. A default Google project keeps data-access logs, once enabled, for thirty days. Nothing warns you about the arithmetic, and it surfaces only when a request arrives for a window that no longer exists.
The AI Act's high-risk obligations do not bite yet. The Digital Omnibus moved them to 2 December 2027 for Annex III systems and 2 August 2028 for Annex I. Material still citing 2 August 2026 is out of date.
In Practice
Consider an IT-operations agent doing routine remediation across a fleet. One morning a batch of production hosts is found degraded and several teams are pointing at the agent. With a usable trail the on-call engineer pulls its activity for the window in minutes: twelve service restarts under a scoped remediation permission, each matched to a health-check failure, one retried after a failure, and nothing at all against the three hosts that actually degraded. Those were changed by a deployment pipeline. The investigation closes on evidence.
Without one, the team knows only that the agent's permissions allowed it. Suspicion lingers, the agent is paused as a precaution, and a working automation sits idle for a week. Three traits separate the two versions: a record for every action, denied and failed included; the context at the moment of the action rather than a pointer to state that may have changed; and queryable by agent, acting user, tool and time, because a trail nobody can search is storage.
Where Fabriq Fits
Agentic Fabriq holds credentials per user in a vault and attaches them server-side at the moment of the call, so the agent never handles the secret. Every call carries two identities, the agent and the acting user, and produces an audit record, which is the AU-10 shape rather than a single-identity log. An agent's effective tool list is the intersection of the user's scopes and the agent's own, so the permission recorded is the one actually evaluated rather than the widest either party holds. Records are queryable in the console with filters; export is API-only, as NDJSON.
What it does not do is reach into the provider on your behalf. Clearing a vault copy of a credential is not the same act as revoking a grant upstream, so the call to the provider's revocation endpoint remains yours to make, and the audit record should say which of the two happened.
Frequently Asked Questions
What is an agent audit trail? A structured, queryable record of each action an agent took, carrying the agent's identity, the person it acted for, the permission evaluated, and the outcome. AU-3 defines the minimum: type, time, place, source, outcome, and identity of the parties involved.
What is the difference between an audit trail and a log? A log records that a system event happened. An audit trail records a decision, written to be read by someone who was not present, which is why outcome and identity are mandatory. Volume is not the difference.
What fields does an agent audit record need? AU-3's six elements, with identity split into the agent and
the acting user, plus the evaluated permission and its result. For a list someone else maintains, OCSF's API
Activity class (uid 6003) requires activity_id, actor, api, metadata, severity_id, src_endpoint,
time and the two uid fields.
Should prompts and model outputs go in the audit trail? Usually not verbatim. AU-3(3) tells you to "limit personally identifiable information contained in audit records to the following elements identified in the privacy risk assessment", and prompt text is the least bounded field an agent produces. Store a reference and a hash, and keep the content under its own access rules.
How long do agent audit logs have to be kept? It depends on the system, not the agent. A deployer of a
high-risk AI system keeps logs at least six months under EU AI Act Article 26(6), and those duties start
2 December 2027 for Annex III and 2 August 2028 for Annex I. HIPAA documentation runs six years under
45 CFR 164.316(b)(2)(i). Check the platform default against whichever applies: Google's _Default log
bucket keeps 30 days.
Do denied actions need to be logged? Yes, and they are the entries most often missing. A denial is evidence that a check ran and held, which is the only way to show a control works rather than assert it exists. Record the permission evaluated and the reason, not just a boolean.
How do we prove the trail was not tampered with? Hash each record or batch, chain each digest to the previous one, and sign them with a key the log's writers cannot use. CloudTrail's digest files are a working example, and the chain is what turns "we see no edits" into "an edit would be detectable".
Trust Requires Evidence
Most of what makes an agent audit trail is not new. AU-3 lists what a record must establish, AU-10 covers a
process acting for a person, AU-9 asks you to protect the result, and OCSF publishes a class whose actor
object holds more than one party.
What is new is that the identity field has two answers, and every layer below the agent was built to record one. That is the whole gap. It closes with a column and the discipline to write a record on the denied path as well as the allowed one.
The test we would apply before trusting a trail: pick an action from last quarter and see whether the record alone names the agent, the person, the permission evaluated, and the result. If any of the four needs a second system or someone's memory, it is a log.
For enterprise AI, trust requires evidence. The audit trail is where that evidence lives.
Sources
All URLs read 2026-09-29.
- Agent Access Control Taxonomy — the audit-granularity dimension, four terms running from logs that cannot tell an agent from a human to a record carrying the full principal chain.
- NIST SP 800-53 Rev 5, AU-3 Content of Audit Records — the six elements an audit record must establish, AU-3(1) additional information, and AU-3(3) on limiting personally identifiable information.
- NIST SP 800-53 Rev 5, AU-10 Non-repudiation — irrefutable evidence that "an individual (or process acting on behalf of an individual)" performed an action, and AU-10(1) on binding producer identity.
- NIST SP 800-53 Rev 5, AU-9 Protection of Audit Information — protection from unauthorized access, modification and deletion, and AU-9(3) cryptographic integrity at the High baseline.
- CloudTrail userIdentity element, AWS —
sessionContext.sourceIdentityand what it identifies,IdentityCenterUserwithonBehalfOf, andinvokedByDelegate. - CloudTrail record contents, AWS —
eventName,requestParameters,errorCode,errorMessage,readOnly,eventID,requestIDandsharedEventID. - Validating CloudTrail log file integrity, AWS — SHA-256 hashing with SHA-256/RSA signing, hourly digest files, and each digest carrying the previous digest's signature.
- Cloud Audit Logs overview, Google Cloud —
authenticationInfo.principalEmail,serviceAccountDelegationInfo,authorizationInfo.permissionand.granted, and Data Access logs being disabled by default outside BigQuery. - Cloud Logging quotas and limits, Google Cloud — 400-day default retention on
_Required, 30 days on_Default, and the 1 to 3,650 day custom range. - Understanding OCSF — the six event categories with their uids, and the Actor object composing Process and User.
- OCSF API Activity class — class uid 6003, the required attributes, and
type_uidcomputed fromclass_uidandactivity_id. - Open Cybersecurity Schema Framework — "an extensible framework for developing schemas, along with a vendor-agnostic core security schema", a Linux Foundation project since 19 November 2024 under Apache 2.0.
- MCP Security Best Practices, 2026-07-28 — token passthrough producing logs that "appear to come from a different source with a different identity", the MUST NOT on foreign-audience tokens, omnibus scopes as audit noise, and logging elevation events with correlation IDs.
- EU AI Act Article 12: Record-keeping — automatic recording of events over the lifetime of the system, and the Annex III point 1(a) minimum log contents.
- EU AI Act Article 26: Obligations of deployers — paragraph 6 and the at-least-six-months retention floor for deployers.
- 45 CFR 164.316, HIPAA Security Rule documentation — retention of required documentation for six years from creation or last effective date.
- W3C Trace Context — the
traceparentheader and the 16-byte trace-id used as the correlating key in the worked example.