Known ungoverned paths and how to detect them
The calls raxit's autopatch does not intercept — what each one is, how to detect it, and how to close it.
A security product should be able to name its own blind spots. raxit governs tool calls by hooking the point inside each framework where tools are dispatched — but a call that never reaches that point is never seen, and a call routed around the interception on purpose is never seen either. This page collects every such path we know about, each already documented on its own framework or concept page, in one place. For every one: what it is, how to detect it, and how to close it.
The common thread: a governed call always leaves a decision record. If a tool ran and produced no
entry in raxit audit tail, it was not governed — whatever the reason. That single check is the
detection tool behind most of the paths below.
None of these are silent gaps we hope you don't notice — each is called out on the relevant framework page, in Limitations, or in Three ways to run raxit. This page just gathers them.
A mismatched --agent-id doesn't fail loudly
What it is. The --agent-id you pass to raxit run must equal the agent: field raxit init
wrote into security.yaml. If they differ, the daemon governs a different principal than the one
your agent presents, and calls resolve against the wrong policy — or no policy at all. It does not
stop the run by default. This is the single most common first-run trap.
How to detect it. Watch for everything denying with deny.principal_mismatch — that reason
code means the attested principal matches no rule's agent:, so no rule can ever apply (see
Common issues and
Reason codes). Confirm the id a call was bound to with
raxit explain <call_id>.
How to close it. Drop --agent-id entirely and let raxit auto-derive the id, or copy the id
straight out of security.yaml's agent: field. The Quickstart walks the
exact trap.
Calling a tool object directly, past a framework-scoped autopatch
What it is. raxit.autopatch.install("langgraph") patches that framework's tool-dispatch
path only. A call that bypasses that path — for example invoking a tool object directly instead of
routing it through the framework — is not intercepted. install() with no argument patches every
framework it detects, but the same rule holds: only calls that flow through a patched dispatch
point are governed. (Concretely, a LangGraph agent that calls a tool directly through BaseTool,
bypassing the graph, is governed by the LangChain patcher — but a call that flows through no
patched path at all is not; see LangGraph (Python).)
How to detect it. install() returns {framework: [methods patched]} — check that the
framework you rely on is listed. Then use the audit trail: a tool that executed with no matching
record in raxit audit tail ran ungoverned.
How to close it. Prefer the full-process launcher (raxit run) or an argument-free
install() so every detected framework is patched, and wrap any tool you invoke outside a
framework with an explicit wrapper (below). See
Three ways to run raxit for how the modes compose.
Vercel AI SDK: four telemetry-hook bypasses
What it is. The Vercel AI SDK's zero-code autopatch hooks the SDK's telemetry registry. Four documented conditions route around it, each silently:
experimental_telemetry: { isEnabled: false }disables the dispatcher entirely;experimental_telemetry: { integrations: [...] }replaces the global registry raxit registered into;- a direct
tool.execute()call outsidegenerateText/streamText/Agentnever passes through the dispatcher; providerExecutedtools run server-side, with nothing local to intercept.
MODIFY is also not available at this hook — the SDK captures input before the wrapper runs.
How to detect it. Read the call sites: any of the four configurations above, or a tool call
made outside the standard generation loop, is a bypass. As always, a missing raxit audit tail
record for a call you expected to be governed confirms it.
How to close it. Wrap the tools you care about with governedTool() — the explicit wrapper is
unaffected by all four bypasses and is the only way to get MODIFY on this framework. Full detail on
the Vercel AI SDK page.
Mastra: only the regular Agent.generate()/stream() path
What it is. Mastra's autopatch governs the regular Agent.generate()/stream() path only.
Not covered: the durable agent's direct execution sites, direct makeCoreTool() users, direct
tool.execute() calls outside the agent loop, Mastra's internal AI-SDK-compat
executeTools/runToolsTransformation family, and clientTools (whose execute is stripped
server-side by design). The interception point (convertTools) is internal-marked, so an upstream
Mastra change could move it.
How to detect it. If Mastra's internal API drifts, raxit's fail-closed startup self-check
raises ChokepointDriftError and aborts raxit run rather than running ungoverned — so drift is
loud, not silent. For the uncovered call sites, audit which path your agent actually uses, and
check raxit audit tail for a record.
How to close it. Use an explicit governedTool() / wrapped createTool for any of the
uncovered paths. See Mastra for the full scope note.
MCP servers: install order decides whether the handler is governed
What it is. MCP has no framework-wide dispatch point to patch at import time, so governance is
opt-in and per-instance: you call installMcpAutoPatch(server) yourself. The intended order is
patch first, then register your tools/call handler. If you register the handler first, raxit
retroactively wraps the already-registered handler in place and emits a loud warning — the
call is still governed, just wired in the wrong order. Only if raxit cannot reach the SDK's
internal handler table (an SDK-version drift case) does it degrade to a warn-only no-op: the
handler then runs ungoverned, but never silently.
How to detect it. Watch for the ordering warning on stderr. The audit trail is the reliable
check either way: a tools/call that produced no decision record was not governed.
How to close it. Call installMcpAutoPatch(server) before you register the tools/call
handler. The ordering contract is spelled out on the MCP servers page.
The general rule: in-process interception is only as strong as the process
What it is. Autopatch and install() work by patching functions inside your process. Code in
that same process can always reach a tool by a path the patch doesn't cover — an unwrapped, direct
call that bypasses governance entirely is invisible to raxit, as it is to any in-process hook. This
isn't a raxit-specific weakness; it's the nature of in-process interception.
How to detect it. The audit trail is the ground truth: if a tool executed and left no record, it ran outside governance. Treat "a side effect happened with no decision behind it" as the signal.
How to close it. For a zero-tolerance setup — where every governed call must be explicit in the
source rather than attached at startup — use explicit wrappers (governed_tool in Python,
governedTool() in TypeScript). The wrapper supports every decision type on every framework, and
because the governance is written at the call site, there is no dispatch path to route around. This
is Mode 3 in Three ways to run raxit.
Related
- Three ways to run raxit — launcher, in-process autopatch, and explicit wrappers, and when each leaves a gap.
- Frameworks — the full coverage matrix, with the per-framework honesty notes these paths come from.
- Limitations & honesty — the broader list of what raxit does not do yet.
- Reason codes — including
deny.principal_mismatch.

