raxit

Three ways to run raxit

The three ways to put raxit in front of your agent — launcher, in-process autopatch, and explicit wrappers — and how to pick one.

There are three ways to put raxit in front of your agent. All three talk to the same local engine, read the same .raxit/security.yaml, return the same permit / defer / deny decisions, and write the same audit trail. They compose — you can launch most of an app zero-code and still wrap one high-risk tool explicitly. Pick by how your process starts, not by how much governance you want.

Mode 1 — Zero-code launcher

You run your agent through raxit run. It loads autopatch before your code and governs every supported framework with no change to your agent.

raxit run --agent-id default-agent -- python agent.py
# TypeScript:
raxit run --agent-id default-agent -- node app.mjs

`--agent-id` must match `security.yaml`

The --agent-id you pass must equal the agent: field raxit init wrote into security.yaml. A mismatch governs a different principal than the one your agent presents, and calls resolve against the wrong (or no) policy — see Quickstart for the full trap.

Best when you control the launch command: local development, containers (make raxit run the entrypoint), and CI jobs. This is the mode the Quickstart uses.

Mode 2 — In-process autopatch

When you don't launch the process by hand — a gunicorn or uvicorn worker, a serverless handler, a notebook, an existing entrypoint you can't wrap — call the same hook raxit run triggers, yourself, as early as possible in your entrypoint:

entrypoint.py
import raxit.autopatch
raxit.autopatch.install()   # patch every detected framework, before your agent code runs

install() returns {framework: [methods patched]} and fails closed (raises ChokepointDriftError) if a framework is importable but the dispatch path raxit hooks has moved — it will not silently leave that framework ungoverned. Configure it with environment variables the same way the launcher does:

  • RAXIT_DAEMON_URL — an explicit engine endpoint to call. Set this to point at a remote or already-running engine (a shared team daemon, a container sidecar, CI infra). It is always honored first when set.
  • RAXIT_AGENT_ID — the principal to present; must match agent: in security.yaml, exactly as --agent-id does in Mode 1.

TypeScript processes you don't launch by hand use the equivalent preload — set NODE_OPTIONS=--import @raxitlabs/raxit/autopatch-preload on the worker (raxit run wires the same preload for you in Mode 1).

Zero setup: no port, no `raxit run`, no daemon to start yourself

If RAXIT_DAEMON_URL is unset, the SDK is fully self-sufficient: the first governed call connects to — or, on a cold start, spawns and attaches to — a local managed engine over a per-project Unix domain socket. There is no port to pick, no raxit dev/raxit serve to run in another terminal, and no daemon lifecycle to manage. pip install raxit (or the TypeScript equivalent) plus one install() call is the whole story for local development, a gunicorn/ uvicorn worker, a serverless handler, or a notebook.

The managed engine binds the socket in a 0700 per-uid runtime directory, verifies same-uid peer credentials (SO_PEERCRED) on connect, and idles itself down after 15 minutes — pinned alive by any open taint/budget state so it never disappears mid-session. Every process on the same host resolving the same project/instance key shares that one engine (and its taint view), so a Python app and a Node sidecar attach to the same decision authority. See Running the daemon for the socket/lifecycle details and the advanced explicit-daemon path.

Set RAXIT_DAEMON_URL only when you deliberately want the SDK to attach to a specific engine instead of managing its own — pointing at a shared team daemon, a container's own raxit serve, or a proxy tier. An explicit RAXIT_DAEMON_URL is always honored first and disables the managed auto-spawn entirely for that process.

A framework-scoped install only covers that framework's dispatch

raxit.autopatch.install("langgraph") patches that framework's tool-dispatch path only. A call that bypasses it — 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 path are governed. See Known ungoverned paths for the exact bypasses and how to close them.

Best for: gunicorn / uvicorn workers, serverless handlers, notebooks, and any long-lived entrypoint where you can add one import but can't change the launch command.

Mode 3 — Explicit wrappers

When you want governance on a specific call and want it to be visible in the code, wrap the tool yourself. This is a first-class mode, not a fallback.

Python
from raxit import RaxitClient
from raxit.agent import governed_tool

client = RaxitClient()

@governed_tool("send_email", client)
def send_email(to: str, body: str) -> str:
    ...
TypeScript
import { RaxitClient, governedTool } from "@raxitlabs/raxit";

const client = new RaxitClient();

const sendEmail = governedTool("send_email", client, async (args) => {
  // ...tool body...
});

RaxitClient is the low-level client the demos drive directly when they need a decision without a framework in the loop.

Reach for explicit wrappers when:

  • your framework has no autopatch support — wrap the tools you care about directly;
  • you run a zero-tolerance setup where every governed call must be explicit in the source, by policy, rather than attached at startup;
  • you hit an integration point autopatch can't carry a MODIFY through (e.g. the Vercel AI SDK) — the wrapper supports every decision type, including argument rewrites, on every framework. See Frameworks for which integrations need this.

Experimental: daemon-side proxies

The three modes above enforce in your process. The engine binary also ships mcp-proxy, http-proxy, a2a-gateway, and ext-authz subcommands — enforcement points that sit outside your process (in front of an MCP server, an HTTP egress path, an agent-to-agent hop, or as an Envoy external-authz check). These are experimental and carry no stability contract; flags and behavior can change without notice, and they are not part of the supported run paths. See limitations for what is and isn't guaranteed.

Which mode should I use?

Your deployment shapeRecommended mode
Local dev / a script you launchMode 1 — raxit run
Container imageMode 1 — make raxit run the entrypoint
CI jobMode 1 — raxit run around the test command
gunicorn / uvicorn workerMode 2 — install() in the worker entrypoint
Serverless handlerMode 2 — install() at cold start / module load
NotebookMode 2 — install() in the first cell
Framework raxit doesn't autopatchMode 3 — governed_tool / governedTool()
One high-risk tool in an otherwise zero-code appMode 1 or 2, plus Mode 3 on that tool
Every governed call must be explicit in sourceMode 3 — wrap each tool

They stack: a container launched with raxit run (Mode 1) can still wrap its one credential- minting tool with governed_tool (Mode 3). Same daemon, same policy, same audit trail.

  • Quickstart — the launcher path end to end.
  • Running the daemon — the managed auto-spawn default, and how to point Modes 2 and 3 at an explicit daemon instead.
  • Frameworks — which frameworks autopatch, and which need Mode 3.
  • Known ungoverned paths — the calls autopatch does not intercept, and how to close them.

On this page