Shadow-agent inventory from traffic BR already sees — no scanners, one query

2026-09-17

governancesecuritypolicylineagemcp

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: true when 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.

  • assertedX-BR-Agent-Id headers 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

QuestionBeforeAfter
Which callers have no agent identity?not answerableuntagged_keys[] with spend and models
Which agent ids have no profile?not answerableagents[].classification=unregistered
Is this X-BR-Agent-Id real?trusted, unrecordedverified, header + chain-committed
Share of traffic from shadow callersunknownshadow_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_inventory in manifest + handler; agents.json regenerated.
  • [x] Docs: docs/api-reference/governance.mdx (Agent inventory section); openapi.yaml merged.
  • [ ] 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_keys and shadow_ratio is non-null.