Skip to main content

Governing an Agent from Any Platform

An agent does not have to be built in Beak to be governed by Beak. An agent written on LangGraph, CrewAI, LangChain.js, Mastra, or nothing at all can put itself in your governed inventory with one call at startup, and from then on it appears in Agent Governance beside the agents you built here.

What Beak can actually do for that agent, though, depends on something a registration cannot change: which of Beak's boundaries the agent's traffic crosses. An agent that only tells Beak it exists can be inventoried and identified. An agent whose model calls route through the Beak AI Gateway can additionally have its prompts inspected, its answers scanned, and its spend attributed. This page is about that distinction, because it is the difference between an agent that is governed and an agent that is merely listed.


What a boundary is, and why it decides everything​

A boundary is a point where the agent's traffic passes through Beak. There are five, and they are the only five:

  • gateway.llm — Model calls route through Beak AI Gateway on a Beak virtual key.
  • hub.tools — Tool and MCP calls go through Beak AI Hub and authorize against the agent's own scopes. Beak proxies the call and sees its result.
  • tool.hook — The agent's platform asks Beak before it runs a tool, and runs it or not on Beak's answer. Microsoft Copilot Studio's external threat detection is this boundary today. Beak sees the tool definition, its inputs, the planner's reasoning and the user message that triggered the turn; it never sees the tool's result, and it sees nothing on a turn that calls no tool.
  • inference.hook — The model vendor calls Beak's inference hook before completing a request.
  • registry.only — Beak holds a record and nothing more.

hub.tools and tool.hook are kept apart on purpose. Both authorize tool calls against the agent's scopes, but only the proxy can act on what came back, so reporting them as one boundary would claim result-side inspection for an agent whose platform never shows Beak a result.

Governance follows from those crossings and from nothing else. Two planes come from registering at all, because Beak can always say an agent exists and who it is:

PlaneWhat it meansGranted by
inventoryThe agent is in the registry and counted in coverage figuresregistration itself
identityThe agent has a Beak identity and can be given an autonomy grantregistration itself
tool_authzTool and MCP calls authorize against the agent's scopeshub.tools, tool.hook
content_runtimeRule-engine policy applies to the agent's trafficgateway.llm, hub.tools, tool.hook, inference.hook
content_promptPrompts are inspected before the model sees themgateway.llm, inference.hook, tool.hook (on tool-calling turns only)
content_completionAnswers are scanned after the model returns themgateway.llm
costSpend, budget, and rate limits are attributed to this agentgateway.llm

Read the table in the direction that matters: content_completion and cost are only ever available through gateway.llm. No amount of registering, adopting, or configuring produces them, because there is no other point at which Beak sees the answer or the bill. If you need either one for an agent, that is a change to how the agent is deployed, not a setting on this page.

Note: registry.only grants nothing beyond inventory and identity. It exists as a value you can state explicitly, so that "Beak holds a record and nothing more" reads as a decision somebody made rather than a field somebody forgot.


Declaring is not granting​

When an agent registers, it may declare the boundaries it intends to cross. A declaration is a request, not a fact:

  • Declared boundaries appear as pending. Nothing about the agent's governance has changed.
  • Granted boundaries are ones Beak has issued the credential for — a Gateway virtual key, a Hub connection. Only these raise what is governed.

This split is deliberate and it is the security property of the whole model. If declaring were enough, an agent could assert its own governance: it would name gateway.llm, Beak would report its prompts as inspected, and nothing would be inspecting them. The dashboard would read as covered while the estate was not. So the agent states intent, and Beak — never the agent — decides when that intent became real.

Warning: A declaration is visible in the console as a pending request, and an agent with every boundary declared and none granted is governed exactly as much as an agent that declared nothing. Read the granted column, not the declared one.


What the tier name means, and when to ignore it​

Beak reports a governance tier as a shorthand for a whole capability set:

TierPlanesTypically
managedall sevenAn agent whose model calls and tool calls both route through Beak
brokeredinventory, identity, tool_authz, content_runtime, content_promptA Copilot Studio agent reached through Beak's external-security frame
inspectedinventory, identity, content_runtime, content_promptAn estate covered by Anthropic inference hooks
registeredinventory, identityAgent 365, Power Platform, and other third-party listings
customwhatever the boundaries grantA combination matching none of the four above

custom is not a lesser tier. It is what Beak reports when the honest answer is a set the four preset names cannot express — an agent holding six of the seven planes, for instance. Beak reports it rather than rounding to the nearest preset, because rounding down understates coverage you have and rounding up claims coverage you do not.

This is why the console shows a capability matrix rather than only a tier badge, and why any integration reading this over the API should branch on the individual planes:

# Right: ask what is governed.
if profile.governs("content_prompt"):
...

# Wrong: a `custom` agent is in no per-tier lookup table, so this under-reports
# an agent holding six planes out of seven.
if profile.governance_tier == "managed":
...

registered is also the default for an agent that declares no source at all. A source Beak cannot identify must under-claim rather than over-claim, so the floor — not the ceiling — is what an unrecognised agent gets.


Registering an agent​

Two SDKs cover the frameworks agents are actually written on. Both are idempotent on (tenant, source, external_id), so calling on every boot is the intended use — it is how a redeployed agent re-asserts itself with no operator step.

Python​

from beakagentidlib import Declaration, configure, register_agent

configure(
security_url="http://beak-aisecurity:5000",
service_code="orders",
environment="prod",
)

profile = await register_agent(
tenant_id=42,
agent_id="orders-triage",
name="Orders Triage",
source="langgraph",
external_id="svc-orders/triage@v3",
declares=Declaration(boundaries=("gateway.llm",), runtime="customer_hosted"),
)

profile.pending # ("gateway.llm",) — asked for, not yet issued
profile.governs("content_prompt") # False, until Beak issues the key

TypeScript​

import { configure, governs, registerAgent } from '@beak/agent-identity';

configure({
securityUrl: 'http://beak-aisecurity:5000',
serviceCode: 'orders',
environment: 'prod',
});

const profile = await registerAgent({
tenantId: 42,
agentId: 'orders-triage',
name: 'Orders Triage',
source: 'langgraph',
externalId: 'svc-orders/triage@v3',
declares: { boundaries: ['gateway.llm'], runtime: 'customer_hosted' },
});

governs(profile, 'cost'); // false

tenant_id, source, and external_id are required with no defaults. The governance record is keyed on all three, and a vendor's own id is unique only inside one connection — without the tenant, two customers whose connectors discover the same agent id would share one record.


Adopting the SDK changes nothing until you say so​

Both SDKs default to mode: "log". In that mode a governance outage writes a loud warning and returns a degraded profile; it does not stop your agent from starting. Switch to enforce when you want a failed registration to be fatal.

Two behaviours are worth knowing before you wire this in:

  • A degraded profile is not an answer about your estate. Every plane reads false on it, because Beak could not be reached — not because nothing is governed. The warning says so explicitly.
  • A misconfiguration is fatal in both modes. A missing security_url can never succeed on retry, so it raises rather than degrading. Degrading it would leave the agent permanently ungoverned behind a log line reporting that everything was fine.

Agents synced through a connector​

An agent brought in by a Runtime connector — Amazon Bedrock, Salesforce Agentforce, and the others — is registered for governance automatically as part of each sync, with the provider type as its source. Nothing is required of the agent's author.

This is off until an operator sets AGENT_GOVERNANCE_SECURITY_URL on Beak AI Agent Runtime. Until then connector syncs behave exactly as they did before: the agent is invocable and has no governance record.


How a boundary becomes granted​

Only Beak records a grant, and it does so when it has issued or verified the credential that puts the agent's traffic on the boundary. Nothing an agent or an operator types raises a tier.

BoundaryRecorded when
gateway.llmBeak AI Gateway binds a virtual key to the agent's identity
hub.toolsBeak AI Hub creates an MCP connection owned by the agent
tool.hookThe agent's platform makes its first verified, in-scope call to Beak's tool hook — for Copilot Studio, the first analyze-tool-execution call from the tenant's registered app that Beak actually inspected
inference.hookBeak verifies the vendor's first signed hook call for the agent
registry.onlyNever granted; it is the statement that nothing else will be

Each recording service proves it is a Beak service with a signed workload assertion, so "Beak issued this credential" can be asserted by Beak and by nobody else. A call the platform made but Beak did not inspect — an environment outside the tenant's configured scope, a frame Beak could not parse — records nothing, because granting a boundary on it would claim governance that did not happen.


The three integration levels​

The boundaries above compose into three levels, which is how the onboarding wizard and the API describe how deeply Beak governs an agent:

LevelBoundariesTier it derives toWhat Beak seesWhat Beak cannot see
Full Gatewaygateway.llm + hub.toolsmanagedPrompts, completions, tokens and cost, every tool call and its resultNothing on the model or tool path
Inference hookinference.hookinspectedThe prompt side of each model callCompletions and cost — the vendor meters the call
Tool hooktool.hookbrokeredTool inputs, planner rationale, the outputs of earlier tool calls the platform carries in the request, the user message on tool-calling turnsTurns with no tool call, completions, cost, the result of the call being authorized

Not every framework or platform can reach every level, and the reason differs in kind. GET /aisecurity/agents/onboarding-catalog states, per source, which levels are reachable and by what mechanism, with two separate flags:

  • reachable: false — The source's own protocol has no hook of that shape. The OpenAI Agents SDK, the Claude Agent SDK, LlamaIndex, AutoGen and the low-code builders expose no blocking per-model-call hook, so an inference hook cannot be wired in-process for them; the Full Gateway or a tool hook is the honest choice.
  • available: false — The source could reach it, but Beak has not shipped the receiving half yet. The note names the gap, for example the Anthropic Messages format that Claude Code needs from the Gateway.

Note: A level is a set of boundaries, nothing more. Choosing one declares intent; the capabilities appear only when the boundary is granted as described above.


Broker tokens for external tool dispatchers​

POST /aisecurity/tools/authorize answers "may this agent call this tool" and is meant to be called by runtimes Beak does not run. A caller outside the Beak cluster must present a broker token — Authorization: Bearer bkr_... — minted by an operator with the agents:keys permission:

POST /aisecurity/brokers/tokens
{ "name": "orders-runtime" }

The token is shown once in the response and stored hashed; list tokens with GET /aisecurity/brokers/tokens and revoke one with DELETE /aisecurity/brokers/tokens/{id}.

A broker token authenticates the caller and fixes the tenant: the decision is still made from the agent's own profile, and the tenantid header is ignored when a token is presented, so a token for one tenant cannot be pointed at another. Nothing is inferred from the network: a caller that presents no proof is refused wherever the request came from. Beak's own services — the Agent Runtime and the Hub — prove themselves instead with an Ed25519 workload assertion (the same credential they use toward BEAKIAM) and name the tenant in the header; Security holds their public keys as trust anchors per environment.

Note: TOOL_AUTHZ_ALLOW_UNAUTHENTICATED_IN_CLUSTER lets a cluster whose Runtime and Hub do not sign yet keep working during rollout. It is off by default, and every answer given under it is logged as unauthenticated. Roll out in this order: deploy Security with the switch on, register the Runtime's and the Hub's public keys as trust anchors for the platform tenant, deploy the Runtime and the Hub with their signing keys, then turn the switch off. Turning it off before both services sign refuses every tool call made through Runtime-managed agents and Hub connections.


The tool-hook receiver​

Any platform that can call a URL before it runs a tool can put Beak on the tool.hook boundary. The receiver is one endpoint that understands four payload shapes and answers in the caller's own shape:

POST /aisecurity/hooks/tool?format=<beak|claude_code|mcp|agent_hooks>
Authorization: Bearer bkr_...
X-Beak-Agent-Identity: <BEAKIAM identity id> (or X-Beak-Agent-Id: <registry id>)
X-Beak-Mcp-Server: <server slug> (format=mcp only)
formatWhat the platform sendsWhat Beak answers
beakBeak's frame: agent_id or agent_identity_id, tool {name, server, required_scope}, inputs, context {user_message, planner_thought, prior_tool_outputs}{allowed, decision, reason, code, resume_token?}
claude_codeClaude Code's PreToolUse hook input (tool_name, tool_input, session_id); mcp__server__tool names are split into server and toolhookSpecificOutput.permissionDecision of allow, deny or ask, with the reason
mcpA JSON-RPC tools/call request — what an MCP proxy or a Bedrock AgentCore Gateway interceptor forwardsA JSON-RPC result, or error -32001 carrying the reason code (and a resume token on escalation)
agent_hooksAn AGENT-HOOKS-0.1 pre_tool_call interception (agent.id, tool.name, tool.arguments); best-effort against the 0.1 contractdecision of ALLOW, DENY or ESCALATE with the reason

The beak frame also takes the result of a tool that already ran: direction: "output" with result. Authorization no longer applies — the call happened — so Beak scans the result and answers allow, redact with redacted_text, or block, meaning do not hand this result to the model. It is the one place indirect prompt injection or a leaked secret in a tool result can still be stopped before the next turn. Every tool-hook call, allowed or not, now leaves a row on the same ledger the prompt scan writes, with the direction and the tool in its metadata.

Every format runs the same decision, in this order:

  1. Tool authorization — the agent's own scopes, allow and deny lists and governance state, exactly as /aisecurity/tools/authorize decides. An unknown or ungoverned agent is refused. This check fails closed.
  2. Content — the frame's text (user message, planner rationale, prior tool outputs, the tool's inputs) through the moderation pipeline against the tenant's tool policies. A block refuses; a redact also refuses, because a hook cannot rewrite what the platform is about to run. If Beak's own pipeline fails, the call is allowed and the verdict says scan_failed_open: true — content inspection fails open with an alarm, like every other content boundary.
  3. Escalation — when the agent's profile lists the tool under permissions.tools.require_approval, the answer is escalate with a resume token. The caller polls GET /aisecurity/hooks/tool/approvals/{token}; an operator sees the request under pending approvals and decides it with agents:authorize. Approvals expire after 30 minutes and an expired token reads as expired, never as approved. If the escalation cannot be recorded, the call is refused.

Every verified call records the agent as seen at the tool boundary and grants tool.hook — the crossing is the evidence — and every refusal leaves an audit row.

Note: The receiver always answers 200. Each of these protocols treats a non-2xx as "the hook failed" and then applies its own default, which is usually to run the tool. A refusal Beak chooses always beats a status code Beak emits.

Note: Microsoft Copilot Studio keeps its dedicated endpoint (/aisecurity/agentsecurity/{slug}/analyze-tool-execution): its 1-second budget and Entra app authentication are protocol-specific. It grants the same tool.hook boundary.


The prompt hook​

The inference-hook level needs someone to ask Beak about a prompt before the model sees it. Inside the cluster that is the Gateway. Outside it — middleware running in your own LangChain, ADK or CrewAI process — the same scan is reachable behind a broker token:

POST /aisecurity/hooks/prompt
Authorization: Bearer bkr_...
{
"messages": [{"role": "user", "content": "..."}],
"agent_id": "acme:orders-triage", // or "agent_identity_id": "<BEAKIAM identity id>"
"agent_metadata": {"source": "langgraph", "model": "gpt-4o"}
}

The tenant comes from the token — a tenantid header is ignored — and the request then runs the inline scan unchanged: the same pipeline, the same rule engine, the same ledger row and the same verdict (action of allow, warn, log, redact or block, with risk_score and scan_scope) that the Gateway's own traffic gets. Without a token nothing is scanned and the call is refused.

Every verified call records the agent named in the body as seen at the inference boundary and grants inference.hook. The grant is the consequence of the call, not a prerequisite for it.

The same route takes the completion too: send direction: "output" with the model's response. By itself that is a detection pass — the answer may already be on its way — so a policy that fired is recorded as would_block and the response says enforced: false. A caller that holds the completion until Beak answers says so with caller_applies_verdict: true; then block stays block, enforced is true, and the audit row records an enforcement, because one happened. The Gateway never sets the flag, so its completion scans are unchanged. Hook-sourced rows keep the whole scanned span, not the Gateway's excerpt: for a platform calling in, the row is the only record of what was said.

Note: Tokens and cost stay with the vendor that meters the call; that is what separates the inference-hook level from the Full Gateway.


Framework hooks: beakagenthooks​

For agents built on LangChain, Google ADK or CrewAI, the beakagenthooks library wires both hooks in one line per framework. It is published to the Beak package feed; install the extra for your framework:

pip install "beakagenthooks[langchain]"     # or [adk] or [crewai]

Configure it once per process, then attach the framework's adapter:

import beakagenthooks as hooks

hooks.configure(
security_url="https://<tenant>.beakops.com/api",
broker_token=os.environ["BEAK_BROKER_TOKEN"],
agent_id="acme:orders-triage", # or agent_identity_id=<BEAKIAM identity id>
source="langgraph",
mode="log", # start here; switch to "enforce" when the log is clean
)

# LangChain 1.x — middleware on the agent
from beakagenthooks.langchain import BeakGovernanceMiddleware
agent = create_agent(model, tools, middleware=[BeakGovernanceMiddleware()])

# Google ADK — a plugin on the runner, so it covers every agent in the app
from beakagenthooks.adk import BeakPlugin
runner = Runner(agent=root_agent, app_name="orders", plugins=[BeakPlugin()], ...)

# CrewAI — process-wide execution hooks
from beakagenthooks.crewai import install
install()
BeforeThe library callsOn refusal in enforceIf Beak cannot be reached
Each model callPOST /aisecurity/hooks/promptThe model is not called; LangChain raises PromptBlocked, ADK returns a refusal response, CrewAI aborts the stepThe call proceeds and an error log names the uninspected prompt
Each tool callPOST /aisecurity/hooks/tool?format=beakThe tool does not run; LangChain hands the model a tool message with the reason, ADK returns an error result, CrewAI aborts the stepThe tool is refused — authorization fails closed

In mode="log" nothing is ever refused: each would-be refusal is logged as a warning so you can read what enforcing would have changed before it changes anything. escalate (a tool the agent's profile reserves for a human) counts as a refusal in these synchronous hooks; the verdict carries the resume token for a runtime that can poll the approval.

Note: Adding the library grants nothing. The first prompt it sends earns inference.hook, the first tool call it sends earns tool.hook, and the agent's tier follows — which is what the wizard's verify step waits for.


Finding the agents nobody onboarded​

Two doors bring agents into the registry before anyone fills in the wizard. Both produce candidates — inventory and identity, coverage observed, nothing enforced — because knowing an agent exists is not the same as standing on its call path.

Platform connectors. Eight discovery integrations enumerate the agents a platform already knows about: Power Platform (Copilot Studio), Amazon Bedrock, Amazon Bedrock AgentCore, Azure AI Foundry Agent Service, Google Vertex AI Agent Engine, iGentic, Salesforce Agentforce and A2A agent cards. Every platform with a saved credential is also re-enumerated on the background discovery tick, so an agent first seen in traffic picks up its display name without anyone clicking Discover. Each one's detail page states the least privilege it needs, read from the connector itself. See Integrations.

Governing agents discovered from iGentic​

Discovery lists iGentic's orchestrators and agents as candidates. From there the operator chooses one of two modes per agent, and the deployment holds one integration key for both:

  • Gateway mode (Full Gateway). iGentic's Azure OpenAI client points at the Gateway's Azure-shaped route with the integration key as its API key and the agent's GUID in a header; Beak resolves the per-agent key it holds for that agent. Prompts, completions, tokens and cost are the agent's.
  • Hooks mode (Level 2 and Level 3 together, chosen as Both hooks). iGentic calls the prompt hook before the model and again with the completion, and the tool-hook receiver before each function runs and again with its result. Completion and result verdicts are enforcements, and every call is on the ledger.
  • Passive telemetry works in either mode. Point iGentic's OpenTelemetry exporter at POST /aisecurity/hooks/otlp/v1/traces and every run populates the inventory at observed.

The contract, the settings, and the operator sequence are on iGentic. Until an agent is adopted and wired, it is inventory and identity: assign it an owner, and the posture panel counts it as discovered, not onboarded after 14 days so it does not quietly read as governed.

The passive OpenTelemetry receiver. Most agent frameworks and observability SDKs — LangSmith, Pydantic AI, the OpenAI Agents SDK, Google ADK, Datadog — already export gen_ai.* spans. Point an exporter at Beak and every agent that runs shows up, with the tools it called and the models it used, at the cost of one environment variable:

OTEL_EXPORTER_OTLP_PROTOCOL=http/json
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://<tenant>.beakops.com/api/aisecurity/hooks/otlp/v1/traces
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer bkr_..."

The receiver reads OTLP/HTTP JSON only; a protobuf exporter is refused with a 415 that names the setting above, rather than acknowledged unread. The tenant comes from the broker token. Spans whose gen_ai.operation.name is an agent or model operation (invoke_agent, execute_tool, chat, …) name the agent from gen_ai.agent.id or gen_ai.agent.name, falling back to the resource's service.name; the provider is mapped to a catalog source where that is unambiguous and kept verbatim otherwise, so the inventory says what the exporter said. A classic Bedrock agent seen through telemetry lands under the same bedrock source the Bedrock connector uses, so it stays one agent in the registry. Batches larger than the receiver's limit are refused with a 413; the limit defaults to 4 MiB and is set on Beak AI Security with OTLP_RECEIVER_MAX_BODY_BYTES.

Note: A span proves an agent exists and what it did. It cannot stop anything, so an agent seen only through telemetry is recorded at coverage observed and granted no boundary — it must never read as protection. Onboard it through the wizard to put Beak on its path.


The onboarding wizard​

Agent Registry → Onboard agent walks the same contract in seven steps. Everything the wizard says is read from the service; it predicts no capability.

  1. Where does the agent run? — Pick a platform, a framework, or Custom framework from the catalog. Each tile says how many of the three levels are available today. Declare it by hand keeps the raw boundary picker for a source the catalog does not list.
  2. How deep should Beak govern it? — Three level cards. Each lists the planes the level would govern, once credentialled, with the absent ones stated in words. A card the source's own protocol cannot reach is disabled and says so ("Not possible for OpenAI Agents SDK — no blocking per-model-call hook"); a card Beak has not shipped the receiving half for is disabled and names the gap ("Not yet available in Beak — …"). The two are different advice, and the card says which.
  3. Register — Agent id, name, and the owner (required — the accountable person every finding about this agent names). The level is the declaration, so the boundaries are shown, not re-ticked.
  4. Result and credential — What the service actually returned: tier, granted and pending boundaries, the capability list. Then the credential the level needs, shown once: an enrolment token for Full Gateway on an agent Beak does not host (the identity comes from enrolling; the Gateway key is bound to it afterwards under Keys), a broker token for a middleware or webhook level, or a pointer to the Integrations page for a platform hook.
  5. Integrate — The verified snippet for the chosen framework and level, with placeholders for your Beak host and the credential, plus the registration call. A level with no verified snippet yet shows the catalog's note rather than invented code.
  6. Verify — Beak polls for the first governed event on this agent every five seconds while the step is open and shows it when it arrives. Until then the agent is pending integration, not protected. The coverage ladder underneath dates each rung the agent has reached and labels each one it has not.
  7. Mode — The agent starts in log mode. The step says where enforcement is switched for each level and how that level fails, because "protected" is easy to read as fail-closed.

An Integrations page for a platform Beak knows links straight into step 2 with that platform chosen (Onboard an agent from this platform), so an operator who has just connected a platform is not asked to find it again.

Agents a connector or a hook already found take the same path. The first screen offers Already discovered — adopt it, listing every candidate a platform connector enumerated or a hook saw before anyone onboarded it, with the source it came from. Choosing Onboard on a candidate does not adopt it on the spot: it continues into step 2 for that candidate's source, then step 3 with the id and name filled in from the discovered record and the owner still required, and on through result, integrate, verify and mode. A Beak AI Runtime candidate can be issued its Gateway credential at step 3 by choosing the Gateway team whose budget it counts against. Adoption records the owner and the declared level in the same transaction as the governance change, so an agent that arrived through a connector is described exactly like one registered by hand — and the posture panel's declared a level but never crossed it finding applies to it in the same way. Exclude still removes a candidate from the list without onboarding it.

Note: The wizard cannot make a level real. Choosing one declares intent and issues the credential; the level's capabilities appear only when traffic arrives on that credential and Beak records the grant, as described above.


Posture: what is not protected yet, and the two clocks​

Every earlier section describes a way to put Beak on an agent's path. This one is about the gap between that and what a console reads as protected. Agent Governance shows an Onboarding posture panel, served by GET /aisecurity/agents/onboarding-posture, computed from the tenant's own evidence on every load — never stored, never estimated.

Findings are conditions that look governed and are not, each with the count behind it and what to change:

FindingSeverityWhat it means
Copilot Studio in log modeHighEvery tool call is evaluated and every one is allowed. The integration reads as connected while nothing is prevented.
Copilot Studio enforcing, allow on timeoutMediumEnforcement is on, but a request that breaches Copilot's one-second budget lets the tool run. A cold worker and a warm one give opposite outcomes on the same content.
Decisions proceeded uninspectedHigh or MediumPrompt decisions in the last 24 hours that were allowed because the policy store could not be reached. On the wire they are indistinguishable from a clean pass.
Foundry agents on the Gateway with no inline policyHighMicrosoft's content filters do not apply to bring-your-own-model traffic, and no API-guardrail or hybrid policy is active, so those prompts are inspected by nobody.
Refused at the Gateway, not adoptedMediumIn the last 24 hours an integration key named an agent that has no adopted key behind it, and the Gateway refused the call. The agent is live and trying to call an LLM while ungoverned. Named when a connector already holds its profile, otherwise shown by its platform id. Onboard it; the per-agent key is minted at adoption.
Declared a level, never crossed itMediumRegistered more than a day ago with an integration level declared and no boundary granted. The integrate step was never completed; the level is intent, not coverage.
Discovered, not onboardedLowFound by a connector or telemetry and still at coverage observed after fourteen days.

The two clocks measure the promise the onboarding experience makes, per tenant:

  • Minutes to inventory — from saving a discovery integration to its first candidate appearing in the registry. One figure per connected platform, and the median across them.
  • Minutes to first protected event — from an agent's registration to the first governed decision recorded against it, as a median over the last thirty days of onboardings, with the number of agents still waiting for their first event.

Both clocks answer null until there is something to measure, and show how many samples the median stands on. A clock with no samples is not a fast clock.

Note: Fail-open is the market default at the tool boundary — Copilot Studio's one-second budget, Model Armor on unavailability — and Beak's own prompt scan fails open with an alarm by design (a policy-store outage must not stop every agent from thinking). The posture panel is where that design choice becomes a line item rather than a footnote: every level card states how it fails, and this panel says how often it did.


Best Practices​

  • Register on every boot, not once at provisioning. It is idempotent by design, and a redeployment that skips it leaves a stale record nobody notices.
  • Declare only what the agent really crosses. A declaration is a request Beak will show as pending; declaring everything produces a queue of requests that were never true and hides the one that is.
  • Read the capability matrix, not the tier badge. The badge is a summary, and for a custom agent it is a summary no lookup table can expand.
  • Treat registry.only as a real answer. For an agent you want inventoried and nothing more, stating it is better than leaving the field empty and letting a reader wonder.
  • Give the agent its own external_id. It is half the idempotency key; without it, two agents from the same source can collapse into one record.