Most teams discover the same uncomfortable fact in the same order. First they realise AI agents are already running across their estate, calling models, opening MCP servers, reaching into databases and object stores. Then they try to inventory them and find there is no list. Independent industry research (CSA/Token, n=418) puts numbers on the gap: roughly 82% of organisations have agents running they don’t know about, and only about 21% keep a real-time inventory. Before you can govern any of this, you have to see it. The question is how you get that visibility without making things worse.
There are two honest answers, and they sit at opposite ends of a risk spectrum.
Two ways to see an agent
The first is an inline proxy. You route the agent’s traffic through a component you control, and because every request and response passes through it, you can see, shape and block in real time. This is a legitimate, powerful design. It is how a lot of egress filtering, secret brokering and request-level policy is enforced today.
The second is passive discovery. Instead of standing in the flow, you observe it from the side: the OpenTelemetry an agent already emits, the native audit trails of the systems it touches (PostgreSQL pgAudit, cloud audit logs like AWS CloudTrail, Kubernetes audit), and kernel-level signals via eBPF as a ground-truth backstop. You never terminate the connection. You watch what happened and reconstruct who touched what.
The difference that matters is not how much each can see in the best case. It is what happens when the visibility layer itself fails.
Blast radius is the real axis
An inline proxy is, by construction, in the data path. That buys real-time control, and it costs you a shared failure mode. If the proxy is slow, your agents are slow. If it falls over, the safe default is usually to fail closed, which means your agents stop, or fail open, which means your control quietly disappears at the worst moment. Either way the visibility component now shares a blast radius with production. It also adds a hop of latency to every call and a deployment surface someone has to operate, scale and patch on the critical path.
Passive discovery has the opposite profile. The collector is out-of-band and read-first: it reads telemetry, audit trails and kernel events; it does not forward or rewrite the agent’s traffic. If it degrades or dies, nothing in the request path changes. You lose fresh visibility until it recovers, not throughput, not availability. This is what read-first, low-asymmetric-risk means in practice: there is no upside risk to production from the thing whose only job is to watch.
| Inline proxy | Passive discovery | |
|---|---|---|
| Position | In the data path | Out-of-band, beside it |
| Real-time block | Native | Needs enforcement at the access point |
| Latency added | Yes, every call | None |
| If it fails | Agents stall or control vanishes | Visibility goes stale; agents unaffected |
| Coverage of silent paths | High (it terminates the flow) | Depends on emitted signals + eBPF backstop |
This is why the product stance is no mandatory proxy rather than “proxies are useless.” There are paths where an inline component is the right call, and they compose. But discovery, the part that should never be allowed to take down the thing it is inventorying, is the wrong job to bolt onto the critical path.
The tradeoff, stated honestly
Passive discovery has a genuine weakness, and pretending otherwise would be the kind of strawman that gets a design quietly distrusted. A proxy that terminates a flow sees every byte of it. A passive collector only sees what the agent, its runtime, or the resource chose to emit. On a fully non-cooperative path, one that logs little and emits no useful telemetry, application-level discovery thins out. You can infer that something happened, but attribution gets harder.
Two things keep that honest rather than fatal.
The first is the eBPF kernel backstop. Application logs can be incomplete, delayed, or, in an adversarial case, deliberately suppressed. The kernel cannot be asked politely to forget a syscall. An agent that opens a socket to a database or writes a file leaves a trace at the syscall boundary regardless of what its logging chose to record. That makes eBPF the anti-evasion ground truth: when higher-level signals disagree or go silent, the kernel view is the corroborating layer. It is also why MCP tool annotations such as readOnlyHint and destructiveHint, useful as hints, are treated as untrusted per the MCP spec. A tool that claims to be read-only and is observed writing is precisely the discrepancy worth surfacing, and you only catch it if you corroborate the claim against what actually happened.
The second is honest confidence levels. Not every observation deserves the same weight, so the access map does not pretend it does. An edge backed by a kernel signal and a matching audit entry is marked attributed; an edge inferred from partial telemetry is marked approximate. The product never launders a guess into a fact. A reviewer should be able to see, at a glance, how much to trust each line.
From observation to the diff that matters
Discovery is only the input. The output is the comparison between PERMITTED, what policy allows, and OBSERVED, what the collector actually saw. That diff is least-privilege drift, and the headline case is an observed write that policy never granted and nobody reviewed.
A few lines from an access feed make it concrete. Reads are routine; the flagged write is the story:
agent action resource outcome confidence
data-export-job READ s3://billing-exports allowed attributed
data-export-job READ prod-postgres allowed attributed
data-export-job WRITE prod-postgres flagged attributed
support-rag READ internal-wiki-mcp allowed approximate
An export job reading a production database is unremarkable. The same job writing to it, when policy only ever granted read, is the kind of edge that should never pass unreviewed. Because the write is corroborated at the kernel boundary, it is marked attributed, not a hedge.
Seeing the drift is half the job; closing it is the other half. Enforcement belongs at the access point, expressed as policy-as-code, so the rule is reviewable and the violation is acted on, not merely logged:
policy "data-export-readonly" {
agent = "data-export-job"
resource = "prod-postgres"
allow = ["read"]
deny = ["write"]
on_violation = "block + alert"
}
Passive discovery finds the unpermitted write; the policy pins the agent back to least privilege and blocks the next one. Visibility and control stay separate concerns, which is exactly why the discovery layer can afford to be read-first.
Takeaway
Visibility into AI agents is a risk decision before it is a feature decision. An inline proxy gives you real-time control at the cost of sharing a blast radius with production. Passive discovery gives up some reach on silent paths in exchange for never being able to take production down, then closes most of that reach with an eBPF ground-truth backstop and confidence levels that stay honest about what was actually seen. For an inventory, the layer whose entire job is to watch, that asymmetry is the right default.
If you want the full picture of how the collectors, the access map and the audit ledger fit together, the architecture overview walks through it end to end.