Skip to content

Technical paper10 min read

What to record for every agent action: a security telemetry schema

Agent logs inherited from performance monitoring say how long and how much, not who, on whose authority, or why it was allowed. A five-band event schema mapped to OpenTelemetry GenAI conventions, with privacy rules and retention tiers.

IntertraceSecurity engineering

Field guides from Intertrace security engineering for the people who run AI programs.

July 29, 2026 · 10 min read

TelemetryDetection EngineeringOpenTelemetryAuditAI Agents
Contents
  1. 01What counts as one event?
  2. 02Who started this, and with whose credential?
  3. 03What did the call actually do?
  4. 04Why was it allowed?
  5. 05What told the agent to do it, and what happened next?
  6. 06Doesn't OpenTelemetry already define this?
  7. 07Won't this log become a second copy of our most sensitive data?
  8. 08How do we know the schema is complete enough?
  9. 09What Intertrace writes down
  10. 10Monday's test: rebuild one session from the log alone

Picture the first hour of an investigation: a customer's records were deleted through an agent's database tool. The log has a timestamp, a tool name, an HTTP 200 and a model ID. It does not say which employee started the session, which credential the agent used, whether the statement was a DELETE or a SELECT, which policy allowed it, or whether a retrieved document told the agent to do it. Every one of those facts existed at decision time. None was written down.

Agent telemetry fails this way because it was inherited from performance monitoring, which asks how long and how much. Security asks who, on whose authority, and why. The schema below answers those and is deliberately boring: flat fields, stable names, enumerated values, no free text where an enum will do. Each section is a question an investigator, an auditor or a privacy reviewer will ask of it.

Five bands of one security eventFig. 1
EVENT · agent.decisionONE PER DECISIONWhouser_idagent_idkey_idsession_idWhattoolnormalized_actionconsequence_tierargs (redacted)Decisionoutcomedeciding_rulelayerapproverContextinput_provenanceuntrusted_spansdetectionsOutcomeupstream_statuslatency_mstrace_idcorrelate every hop of one request with the same trace_id · never write secret values into any field

Who caused the action, what it did, how it was decided, what context shaped it and what happened next. A missing band is a question an investigator cannot answer from the log.

What counts as one event?

A decision point: every model call, tool call, MCP request or agent-to-agent message a policy evaluated. An HTTP request can carry several tool calls, and a session summary cannot be joined to one action. Detection, investigation and audit all read this one event: indexed fields to aggregate, linkage to rebuild a session, and proof months later that a named rule or person authorized the action.

Who started this, and with whose credential?

A single "user" field is the most common reason agent investigations stall. Record five layers separately, as identifiers rather than display names.

  • User: the human principal the agent acts for, or an explicit marker that it is running autonomously.
  • Agent: a stable agent ID plus a version or configuration hash, so you know which build acted.
  • Runtime key: the credential's key ID or fingerprint, never its value. You revoke by it, so you must search by it.
  • Tenant: the organization, derived from the authenticated credential on the server. A tenant ID copied from the request body is a claim, not a fact.
  • Session: the conversation or run that groups related actions. Without it you have a pile of calls, not a story.

When agent A asks agent B to act, B's event should carry the ordered delegation chain and the originating user, so handoffs cannot launder authority.

What did the call actually do?

The tool name will not tell you. `run_sql` covers both a read and a table drop; `bash` covers both `ls` and `curl | sh`. Record three layers. Tool identity is the tool name, its server or connection, and for MCP a hash of the tool definition in effect. The normalized action is what the call does once its arguments are read, such as `sql.delete`, `git.push.force` or `message.send.external`, and detections should key on it. The consequence tier is an ordinal for how bad the action is if wrong (read, reversible change, external effect, privileged, irreversible), so one rule can cover "irreversible actions in production" instead of a list of tool names.

Arguments carry most of the risk and most of the sensitive data, so do not log them raw. Record their shape (names, types, lengths), detected data classes, the destination for outbound calls, and a keyed hash of each value. An HMAC with a per-tenant secret answers "was the same recipient used in both calls" without storing the recipient, and resists the dictionary attack that a plain SHA-256 of an email address invites.

Why was it allowed?

Most logs leave this band out, and it is what separates an activity log from an audit trail: an allowed action is not evidence of authorization unless the record names what allowed it.

  • Outcome: allow, flag (allowed and marked for review), ask (held for a person) or block. In monitor mode, also record what enforce mode would have done.
  • Deciding rule and layer: the rule ID and version that produced the outcome, and where it lives (organization, agent, exception, built-in safeguard). Auditors ask whether default policy or a granted exception allowed it.
  • Policy snapshot: a hash of the policy set evaluated, so the decision can be replayed against the exact rules in force.
  • Signals: detector outputs that fed the decision, each with its source, so a false positive traces to its detector.
  • Approval: for asks, the approver's identity, decision, time and the scope of any remembered approval. Without an approver ID it cannot be audited.
  • Failure reason: if the enforcer could not decide and failed closed, say so instead of recording an ordinary block.

What told the agent to do it, and what happened next?

Indirect prompt injection is a provenance problem. You do not need the injected content in the log. You need, for each input span in the model's window, its source type (user turn, system prompt, retrieved document, tool result, web page, another agent), a source identifier such as a document ID or host, a trust label and a hash. Then "the agent emailed a file after reading an external page" is a query, not a project.

The outcome band records upstream status or tool error type, latency, response size and data classes in the response. Correlation ties events together: a W3C trace and span ID, the session ID, a parent event ID for the model call that proposed the tool call, a hop index, and the provider's response ID where one exists. With that chain an investigator walks from a deleted record back to the user turn and the document behind it.

Here is one whole event. A clerk at a fictional logistics company asks ledger-bot, an illustrative finance-operations agent, to clean up duplicate vendor records. It proposes a SQL statement, and policy holds it for approval.

Sample security event for one held tool call (illustrative values)
FieldExample valueWhy it is there
actor.user_ididp|u_48213 (clerk)The person to interview.
actor.key_idkey_7Q2M (fingerprint only)The credential to revoke; the value is never logged.
tenant_idorg_logistics_eu (from the key)Scoping that a request body cannot spoof.
action.normalized / tiersql.delete (CTE) / irreversibleWhat the call does and how bad a mistake is.
args.shape / classes / hmacquery: string, 412 chars / none / h:5be0Evidence without the raw statement.
decision.outcome / modeask / enforceWhat policy did, and whether it was enforced.
decision.rule / layerorg.sql-delete-asks v3 / organizationThe authority for the outcome.
approval.by / at / scopeidp|u_10077 / 14:02:31Z / this call onlyWho accepted the risk, and how broadly.
context.untrusted_spans1 tool_result (vendor-export.csv)Whether untrusted content preceded the action.
trace_id / parent_event4bf92f35... / evt_model_6Correlation across hops and systems.

Doesn't OpenTelemetry already define this?

Partly; use it where it does. The GenAI semantic conventions are marked Development, so pin your version. They define an `execute_tool` span with `gen_ai.tool.name` required and `gen_ai.tool.call.arguments` and `gen_ai.tool.call.result` opt-in, because they may contain sensitive information [1]. Agent spans and the MCP conventions add the identity and session attributes in the table below [2][3].

Two details matter. Instrumentations should not capture instructions, inputs and outputs by default [1]; keep that default, because span attributes fan out to every observability backend. And when no conversation ID is available, the inference conventions say to leave it empty rather than fall back to a new UUID, a trace ID or a hash of request content [5]. Assign the session security needs at the gateway, in your own field.

Where the GenAI conventions cover the schema, and where security adds fields
Schema conceptOpenTelemetry attributeWhat security adds
Agent identitygen_ai.agent.id, gen_ai.agent.version [2]Delegation chain.
Tool identitygen_ai.tool.name, gen_ai.tool.call.id [1]Server identity and tool-definition hash.
Sessiongen_ai.conversation.id, mcp.session.id [3][5]A gateway-assigned session when the client sends none.
Argumentsgen_ai.tool.call.arguments, opt-in [1]Shape, data classes and keyed hashes instead of values.
User, key, tenantNot defined in the agent span conventions [2]Principal, key fingerprint, server-derived tenant.
Normalized action, tierNot definedAction read from arguments; consequence ordinal.
Decision, rule, layer, approverNot defined [2]The whole decision band.
Context provenanceNot definedPer-span source type, trust label and hash.

So a security event is a superset: a standard OpenTelemetry span plus a security namespace you own, reduced before export to general observability tools.

Won't this log become a second copy of our most sensitive data?

Only if you let it. A log holding every prompt is a second copy of your most sensitive data, with weaker access control than the original. OWASP's logging guidance lists what should usually not be recorded directly, including session identifiers (hash them if you must track sessions), access tokens, passwords, encryption keys, payment card data and sensitive personal data [4]. Agents make them more likely: users paste credentials into chats.

  1. Minimize at the source: detection needs classes, counts and hashes, rarely content. Run secret and PII detection on argument values, error messages and retrieved titles before they reach the pipeline, not in a nightly job.
  2. Hash with a key. Use a per-tenant HMAC for values you correlate, and rotate it like any other secret.
  3. Never store secret values at any tier. A fingerprint is enough to match a leaked key to its events and revoke it.
  4. Isolate content capture. If a defined purpose needs raw prompts, keep them in a separate store with its own access list, retention and deletion path.
Retention tiers for agent security eventsFig. 2
Hot · 30 daysfor detectionfull metadata, redacted argumentsWarm · 1 yearfor investigationmetadata, hashed argumentsCold · per policyfor auditdecisions and approvals onlyDETAIL NARROWS AS DATA AGES ←SECRETS NEVER STORED AT ANY TIERretention periods are examples; set them from your detection, investigation and regulatory needs

Detail decays with age: full metadata for detection, redacted arguments for investigation, and only decisions and approvals for audit. Secrets are never stored at any tier.

Tier retention by consumer (the durations are examples, not a standard). Hot storage, about 30 days, holds full metadata for detection and triage. Warm storage (about a year) keeps hashes and classes with no residual content, for investigations that start late. Cold storage, kept as long as audit policy requires, holds only decisions, approvals and identity. Telemetry retention and audit retention run on different clocks.

How do we know the schema is complete enough?

Write the detections you want. Each should be a query over guaranteed fields; one that requires parsing free text means a field is missing.

  • Injection to action: an external-effect or irreversible action shortly after an untrusted web or tool-result span entered context.
  • Credential reuse: one key fingerprint seen with several agent IDs, or from a new network origin.
  • Rubber-stamping: an approver accepting asks faster than a person could read them, or nearly every ask for one rule, which means the rule should change.
  • Exception creep: a rising share of allows decided at the exception layer, or exceptions renewed back to back.
  • Exfiltration shape: one argument hash sent to a new external destination from several sessions, or sensitive classes in a response, then an outbound message.

Six habits break them. Logging full prompts "for debugging" ships every pasted secret to tools with broad access. Recording the outcome without the deciding rule shows that something was allowed but never why. A session ID only some components set disconnects the injected document from the action it caused. A tenant field taken from the body records what the attacker claimed. Sampling drops exactly the event you need. And editing an event in place on approval erases the original decision; append a new event that references the first.

What Intertrace writes down

Intertrace's gateway emits a structured event for every decision on LLM, MCP, inference, service and agent-to-agent traffic: who acted, what the call did after action normalization read its arguments, the outcome, the rule and layer that decided it, and the evidence. Tenancy comes from the `itr_rt_*` runtime key, not the request body. Events export to your SIEM, and the OpenTelemetry fan-out is metadata only.

Monday's test: rebuild one session from the log alone

Take one session from last week on a consequential agent. Give an investigator only the security log and ask for the story: who started it, with which key, what each call did, which rule or person allowed it, and which untrusted span preceded the riskiest action. Each unanswered question is a missing field. Fix the decision band first (outcome, rule and version, layer, policy hash, approver): it is the cheapest band to add and the most expensive to lack. Then plant a canary secret in a test prompt and search every tier for it. If it turns up anywhere, the log has become part of the problem it was meant to solve.

Sources

Reporting, research and specifications this piece draws on. Links go to third-party sources; analysis and recommendations are Intertrace’s own.

  1. 01Semantic conventions for generative AI spans (execute_tool, content capture) · OpenTelemetry
  2. 02Semantic conventions for GenAI agent and framework spans · OpenTelemetry
  3. 03Semantic conventions for Model Context Protocol (MCP) · OpenTelemetry
  4. 04Logging Cheat Sheet · OWASP
  5. 05Semantic conventions for GenAI client inference spans · OpenTelemetry

About this desk

Intertrace

Security engineering

Field guides from Intertrace security engineering for the people who run AI programs.

All 22 pieces by Intertrace

Dated July 29, 2026 · added to intertrace.ai October 19, 2026

+ Runtime enforcement

See these controls on your own agent traffic.

Intertrace sits inline between your agents and the models, tools and MCP servers they call, and decides every call before it runs.

Keep reading

All insights →