Shadow-agent inventory from traffic BR already sees — no scanners, one query
2026-09-17
LOCKSTEP TRACEABILITY MATRIX --- api_endpoints: ["GET /v1/governance/agents/inventory"] sdk_methods_updated: ["governance.agentInventory()", "governance.agent_inventory() sync + async"] mcp_tools_updated: ["br_agent_inventory"] ---
What We Built
Identity vendors "discover" agents by scanning cloud accounts, Kubernetes, repositories and secret managers. BrainstormRouter sits on the model call, so every caller is already observed. This ships the view that turns that observation into an inventory: every distinct caller in a window, classified against the two identity registries the platform already keeps.
- registered — the agent id has a profile.
ownerless: truewhen no accountable owner
is bound (owner binding, 2026-09-15). Idle registered agents are still listed.
- unregistered — an agent id seen in traffic with no profile: deleted, never enrolled,
or forged.
- untagged keys — API-key traffic carrying no agent identity at all. This is where
scripts and un-enrolled agents hide. It is labeled as exactly that, never as proof of an agent, with a hint on how to enroll.
- asserted —
X-BR-Agent-Idheaders the policy plane accepted for scoping, with whether
each matched a profile.
shadow_ratio is the share of requests from unregistered agents and untagged keys. It is null, never 0, for an empty window.
The second half closes a gap the audit surfaced: the policy middleware trusted X-BR-Agent-Id from any authenticated key for policy scoping, and the value was never recorded or checked. It is now verified against the profile registry on every request, stamped on the response as X-BR-Agent-Assertion, and chain-committed inside the lineage row's decision.accountability as asserted_agent_id + agent_registered. Scoping behavior is unchanged; what changed is that a made-up id is now visible as made-up.
Why It Matters
"Find your shadow agents" is the opening line of every non-human-identity pitch, and it costs them a scanner per environment. BrainstormRouter answers it from one query over traffic it already routed, and answers a question the scanners cannot: not just _which_ agents exist, but which ones are spending, on what models, under which key, and whether a human is accountable for each.
How It Works
src/api/agent-inventory.ts is a pure classifier over four tenant-scoped reads: usage_events grouped by (agent, key, user) with platform-initiated traffic excluded; agent_profiles; agent_registry (mesh SPIFFE identities); and a best-effort aggregation of decision_lineage.decision->'accountability' for header assertions, reported as null with a note when unreadable. The capability, the MCP tool and both SDKs call the same function.
X-BR-Agent-Id: ghost-7 ─▶ policy.ts: profileStore.get(tenant, "ghost-7") → false
c.set(_assertedAgentId, _assertedAgentRegistered=false)
X-BR-Agent-Assertion: unregistered
─▶ resolveRequestAccountability → decision.accountability
{ asserted_agent_id: "ghost-7", agent_registered: false } (chain-committed)
─▶ GET /v1/governance/agents/inventory → asserted[] + shadow_ratio
The Numbers
| Question | Before | After |
|---|---|---|
| Which callers have no agent identity? | not answerable | untagged_keys[] with spend and models |
| Which agent ids have no profile? | not answerable | agents[].classification=unregistered |
Is this X-BR-Agent-Id real? | trusted, unrecorded | verified, header + chain-committed |
| Share of traffic from shadow callers | unknown | shadow_ratio |
Competitive Edge
A directory scanner finds an identity and stops. This inventory starts from the action: every request already carries who paid, what model ran, what it cost and, since owner binding, who is accountable. Enrolling a caller moves it from untagged to registered and the ratio falls — a number a tenant can put on a dashboard and drive to zero.
Lockstep Checklist
- [x] API Routes:
GET /v1/governance/agents/inventory(registered in_registry.ts). - [x] TS SDK:
governance.agentInventory({ period }). - [x] Python SDK:
governance.agent_inventory(period=)sync + async. - [x] MCP Schemas:
br_agent_inventoryin manifest + handler;agents.jsonregenerated. - [x] Docs:
docs/api-reference/governance.mdx(Agent inventory section);openapi.yamlmerged. - [ ] Master Record: capability record entry — follow-up with owner binding and approvals.
- [ ] Production verification: deploy; call the route on the soak tenant; confirm the soak key appears under
untagged_keysandshadow_ratiois non-null.