Get the latest, first
arrowBlog
Prompt Injection Telemetry for Autonomous Agents: The Attribution Ceiling Decides What You Detect

Prompt Injection Telemetry for Autonomous Agents: The Attribution Ceiling Decides What You Detect

Aug 20, 2026

Shauli Rozen
CEO & Co-founder

Key takeaways

  • Why doesn't standard agent telemetry tell you which agent was compromised? The OpenTelemetry GenAI conventions define gen_ai.agent.id as the provider-assigned identifier for an agent resource, and advise against recording transient in-memory instance ids in it. Forty running copies of one agent definition therefore report one identifier. That is the right design for a debugging schema, and it removes the subject that prompt injection detection depends on.
  • What is the attribution ceiling? The lowest telemetry layer in your stack that can still resolve behavior to a single agent instance. It is set by how agents are packed into processes and pods, which is a platform engineering decision usually made on cost grounds. Every detection product you evaluate afterward inherits it.
  • How do you find out what your ceiling actually is? Run two agents from the same definition in your real runtime configuration, give one a task that reaches outside its normal behavior, and read what the resulting alert names. If it names the pod, the deployment, or the service account, your per-agent baselines are workload baselines averaged across everything packed inside.

The OpenTelemetry GenAI conventions define an attribute for agent identity. They also tell instrumentation authors not to put a running agent’s instance id in it.

The reasoning is sound. gen_ai.agent.id is meant to carry the provider-assigned identifier of an agent resource: a Bedrock agent ARN, a Vertex reasoning-engine URN, an Assistants API identifier. Instance ids are transient, so the spec advises against them. Two neighbouring rules point the same direction. Instrumentations are told to leave gen_ai.conversation.id empty rather than invent a value when no conversation exists, with trace ids and content hashes ruled out by name as fallbacks. And gen_ai.workflow.name must stay low cardinality.

So the telemetry standard the industry is converging on records what kind of agent acted. It declines, deliberately, to record which one.

That is a reasonable trade for a debugging schema and a fatal one for this attack. Prompt injection against an autonomous agent produces no unauthorized action. Every file the coerced agent opens, every endpoint it calls, every credential it presents was granted to it. The ingress path stops mattering once the payload is in context, which is why the direct and indirect distinction expires at the context window and everything downstream looks the same. What is left is a departure from how that agent has behaved before, which makes detection an act of comparing an agent to itself. You cannot run that comparison if your telemetry cannot tell two agents apart.

Your stack has a ceiling on that resolution. Here is what sets it, how to measure it, and what to do at each level.

Autonomous agents lose three correlation keys that human-triggered agents get for free

Three keys make an agent’s behavior joinable across telemetry layers, and a chat-triggered deployment gets all three without asking.

The session anchor is the boundary that says these events belong to one request. A human-triggered agent has one by construction: someone hits an endpoint, a session exists, and every downstream event can carry its identifier. An autonomous agent woken by a cron schedule, a queue message, or another agent has no such boundary, and the conventions are explicit that a fabricated one is worse than none at all.

The instance identity is what separates two running copies of the same definition. Chat deployments tend to get this by accident, because one live conversation maps to one process handling it. Autonomous fleets run forty copies of one definition under one deployment, and the identifier every layer records is the definition.

The causal parent is which task authorized the action. When an agent spawns a subagent, the subagent’s reach is legitimate only in reference to the parent task that delegated it. Strip that link and a delegated action and an injected instruction produce the same record, which is the property that lets an injected instruction propagate between agents while every hop still reads as authorized delegation.

Lose all three and your telemetry is still complete. Every syscall, every connection, every tool call lands in storage. What goes missing is the ability to assert that this sequence belongs to one actor, running one task, under one authorization. Pulling scattered signals into a single attributable sequence is the entire job when you are detecting lateral movement across an agent fleet, and the same requirement is what makes coercion detectable at all.

The obvious objection is that traces already solve this. An autonomous run produces a trace, the trace has an identifier, and every span inside it carries that identifier. True, and worth having. It answers a different question than the one detection asks. A trace resolves one run: these forty spans belong together. A baseline is built across runs, over days, out of one agent’s accumulated history, and the trace identifier is new every time. Traces also stop at the edge of the instrumented process, and kernel records and cloud audit entries carry none, so joining a syscall or a role assumption to a reasoning step gets no help from the trace. Within-run correlation you already have. Cross-run identity is what goes missing.

None of this is an OpenTelemetry failure. A schema that carried per-instance identity on every span would inflate cardinality, cost more to store, and be less comparable across vendors, which is the problem the conventions exist to solve. The GenAI conventions are also still marked development status, so the shape may change. The gap is real today and closing it was never their job.

Your deployment topology sets the attribution ceiling before you pick a detection tool

The lowest layer of your stack that can name one agent instance is a function of how agents are packed into processes and pods. No detection product changes it.

Packing topologyWhat the kernel resolvesWhat the identity plane resolvesLowest layer that names one agent instance
One agent, one pod, one processOne process tree per agentOne service account per agentKernel
N agents, one pod, N child processesN process trees, separable by PID lineageOne shared service accountKernel, if your profiler baselines per process
N agents, one process, N threads or async tasksOne process tree doing everythingOne shared service accountApplication framework
N logical agents scheduled across a worker poolOne process tree per worker, reused by unrelated agents over timeOne service account per worker poolApplication framework, plus context propagation across the scheduling boundary

Row one is the clean case and the expensive one. Row two is common and usually wasted, because most runtime profilers baseline at container or pod granularity, so resolution that exists in the kernel data gets averaged away before it reaches a behavioral profile. Row three follows from the default concurrency model of the major Python agent frameworks: one interpreter, one event loop, many concurrent runs sharing it. Kernel telemetry sees a single subject doing all of it, and it is reporting correctly.

Row four is where the ecosystem is heading. A CNCF post on whether a Pod is the right deployment unit for an agent argues the Pod remains the right execution unit while no longer being the right deployment, identity, or lifecycle unit, and kagent moved to a control plane above Kubernetes after starting with one pod per agent. Google’s Agent Substrate schedules logical Actors onto long-lived Worker pods, so one process tree serves unrelated agents across its lifetime and observability has to follow the logical agent to wherever it was scheduled.

The engineering behind that shift is sound. Agents wake for a task, run for seconds, then idle, so a dedicated always-on pod per potential agent burns money for nothing. The reason to track it is that the decision caps your detection resolution, and it is usually made without a security reviewer in the room.

The ceiling is a property of your deployment, which makes it something you control. Raising it costs no license. It costs a decision about which agent classes are worth unpacking, and a fresh observation window for the ones you split, since a profile built against the old topology does not transfer. That window is the same observation debt every agent carries on the day it ships, and repacking makes you pay it twice.

Run the two-agent test before you shortlist a detection tool

You can measure your ceiling in an afternoon.

Two preconditions. Use an agent definition that already has a converged observation window behind it, because a baseline that has not settled will not fire for anybody and the test returns a false pass. And run it in the packing topology you actually ship, since the topology is the variable being measured.

Deploy two agents from that definition. Give the first a task that stays inside its usual behavior. Give the second a task that reaches something outside it: a file path it has never opened, a host it has never resolved, a table it has never queried. Then wait for the alert and ask it one question. Does it name the second agent?

If it names the agent, your ceiling sits at or above your enforcement layer. Check where the name came from before you celebrate. If it arrived from the framework’s own context rather than from kernel or identity records, you have a triage pass and a partial evidence pass, for the reason in the next section.

If it names the pod, the deployment, or the service account, your ceiling is the workload. Per-agent baselines in that configuration are workload baselines averaged across everything packed inside, and averaging is the condition a coerced agent hides in. One agent’s unusual reach reads as normal variance for the group.

If nothing fires until an analyst stitches three consoles together by hand, you do not have a ceiling. You have an investigation procedure, and it will not close the gap between a scored signal and an executed containment at the speed the agents are working.

No converged baseline anywhere in the fleet yet? Run it forensically: trigger the event, attribute it by hand, and time yourself. That number is the floor on your incident response, and it is what to bring to platform engineering when you ask them to unpack a worker pool.

Rebuild attribution at the boundaries that survive process collapse

When the kernel cannot separate two agents, three anchors remain. Each holds at a different boundary, and each fails somewhere specific.

Process lineage. An eBPF sensor records PID, parent PID, container, and pod from outside the workload, so nothing running inside can edit the record. That makes it your strongest evidence, and it resolves cleanly down to row two of the table. It stops at the process boundary. Two async tasks in one interpreter are one process, and no amount of kernel visibility changes that. The same boundary is why kernel-level enforcement needs application-layer context to say anything about agent behavior.

Credential distinctness. Give each agent its own credential and the identity plane records which agent acted even where the kernel cannot separate them. That turns credential design into a detection input rather than only an access control. The cost is real: you provision, scope, and rotate per agent instead of per workload. Spend it on the agent classes touching data you would have to report on.

Network identity. Workload identity on the connection and per-agent egress paths attribute at the point of the connection, which covers the exit well. The published incidents are mostly exits: the documented indirect injection cases were each closed by narrowing an exfiltration channel while the injection stayed open. About the eight steps before the exit, network identity says nothing. Where agents span clouds it is also the anchor to normalize first, and correlating identities across providers is its own piece of work.

None of the three recovers instance identity inside a shared async runtime. That resolution exists in exactly one place: a per-run context the framework propagates into every outbound call. It is also the place a coerced agent is running, which puts your highest-resolution signal in the tier the workload can rewrite. The split that works is to use context propagation for triage, where speed matters more than admissibility, and to hold the kernel and identity planes for the evidence that has to survive an incident review. Whether a signal can be trusted is one axis of any observability decision. Whether it can name the actor is the second, and a signal has to clear both.

Attribution decides whether behavioral enforcement can arm at all

Application Profile DNA (APD™) is a per-agent behavioral baseline. Per-agent presupposes a resolvable agent. Where the ceiling is the workload, what gets built is a workload profile, and a workload profile spanning forty agents with different jobs has a normal wide enough to contain most of what a coerced agent would do.

That makes the audit phase of an Audit to Enforce rollout worth reading twice. It gets described as the period where you learn what normal looks like. It is also the period where the ceiling becomes visible, because a baseline that will not separate two agents in observation will not separate them in enforcement. If the observed profiles for two agents in the same runtime converge on the same shape, you have your answer before you have blocked anything.

Coercion is the case that makes this load-bearing. An agent talked into misusing its own permissions triggers no unauthorized-action rule, matches no signature, and violates no policy it was not already allowed to satisfy. Controls placed upstream of the decision or around the capability narrow the opening without closing it, which is why something has to hold downstream at the action. The only thing separating that action from a normal Tuesday is that this agent has not behaved this way before, and every word in that sentence rests on knowing which agent.

Which is why the resolution question comes before the signal question. Teams that test early tend to find their ceiling one row lower than assumed, while the fix is still a deployment change. ARMO’s cloud-native security for AI workloads builds behavioral profiles from kernel-level observation and carries the same profile through progressive enforcement, so the artifact that answers “is this normal” also answers “for whom.” Ask any vendor to demonstrate that on your topology rather than theirs, and book a walkthrough with the packing configuration you actually run.

A stack that cannot name the agent cannot compare it to itself.

FAQ

How do I check whether my agents are sharing a service account? Read the pod spec for each agent workload and compare the serviceAccountName field across them. Shared service accounts across agent instances are a documented multi-agent anti-pattern, because any agent that can read the mounted token can present the identity of every other agent behind it. Fix the highest-privilege agents first, since a shared account there collapses attribution and blast radius at once.

Can I just add an agent id to my application logs and be done? For triage, yes, and it is worth doing today because it is cheap and it speeds up every investigation you run. For evidence, no. A log line the agent’s own process wrote is a claim the agent made about itself, and a coerced agent runs with the permissions to make that claim differently. Use it to narrow the search and use kernel or cloud audit records to establish what happened.

What if my agents run on a managed agent platform rather than my own pods? Hosted platforms assign stable per-resource identifiers, which is exactly what the OTel conventions point at, so agent-level attribution at the control plane is usually available. The gap moves rather than closes. Once the agent invokes a tool that executes in your infrastructure, you are back to resolving which agent triggered the execution, and a poisoned tool definition can complete an attack through an already-approved call without ever crossing the control plane in a way the platform would flag.

How long does the two-agent test actually take? An afternoon if you already have a non-production cluster mirroring your packing topology, longer if you have to build one, and the mirror is the part worth insisting on. Record what the alert names, how long it took to fire, and how many consoles you touched to reach a conclusion. Those three numbers are the brief for whatever you do next.

Does moving to one agent per pod solve this? It solves the resolution problem completely and creates a cost problem in proportion to how bursty your agents are. Decide by class rather than across the fleet: agents that touch regulated data, hold write permissions, or reach external networks earn their own pod and their own credential, and the rest can stay packed with the ceiling documented. The point is to know which agents you can name, before an incident asks you.

Close

Your Cloud Security Advantage Starts Here

Webinars
Data Sheets
Surveys and more
Group 1410190284
Ben Hirschberg CTO & Co-Founder
Rotem_sec_exp_200
Rotem Refael VP R&D
Group 1410191140
Amit Schendel Security researcher
slack_logos Continue to Slack

Get the information you need directly from our experts!

new-messageContinue as a guest