Enforced locally · zero added latency

Per-user authorization for AI agents.
Gate what they do, not just what they say.

One agent, many users, each gated by their own role. Hexgate carries per-request user identity through every tool call, with policy enforced in-process from a signed WASM bundle. Fine-grained control with zero added latency on the critical path.

$ pip install hexgate
Try the cloud version
MIT licensed No per-call round-trips Ed25519 signed bundles
Tool callReal-time evaluationTyped decision
Agent
emitting tool calls
POLICY GATEdecide()
ALLOW0 total
APPROVAL0 total
DENY0 total

Wraps the agent you already built

OpenAI Agents LangChain / LangGraph Google ADK Pydantic AI+ any native runtime
Live audit feed

Every decision, on the record.

Past the gate, each verdict streams to an append-only log: the caller's role, the tool, the outcome, and the exact constraint behind it.

PolicyEnforcer.decide(role, tool, args) live
every decision streamed to the audit log
The control plane for agentic systems

Authorization that travels with every tool call.

The capability you give an agent is the capability it can be jailbroken into using. Hexgate sits at that boundary and turns it into four primitives you can edit, version, and audit.

Policy enforcement

Deny-by-default. Every tool call returns a typed Decision(allow, deny, or approval-required), evaluated against the caller's role at call time.

Signed bundles, local speed

The signed WASM bundle is fetched once per runand enforced in-process. No security service on the hot path, no round-trip per decision. Fast by design, verified before it's trusted.

Per-user authorization, not per-agent

Biscuit tokens carry who is calling; role policies decide whatthey can do. One wrapped agent serves every user: same code, different effective permissions per request. Other governance toolkits gate the agent. Hexgate gates the user, through the agent.

Audit trail

Every decision streams to the audit log: who acted, which tool, the verdict, and the exact constraint that allowed or blocked it. When someone asks why a call went through, you can show them the line.

Quickstart

Wrap your agent in one line. Ship enforcement on day one.

No rewrite, no config object. Set a key, wrap the runner, and the same agent code gates every tool boundary.

agent.py
from hexgate.adapters.openai import HexgateRunner
from hexgate.runtime import User

# picks up HEXGATE_KEY from env, no rewrite
runner = HexgateRunner()

await runner.run(
    my_agent,
    "refund order 30",
    user=User(user_id="alice", role="billing"),
)
# ↳ every tool call now routes through policy
policies/billing.yaml
version: 1
inherits: [read_only]

default_policy:
  mode: deny

tools:
  refund_order:
    mode: allow
    constraints:
      - args.amount <= 500
      - args.currency == "USD"
  wire_transfer:
    mode: approval_required

Identical decisions in dev (in-process) and prod (signed WASM), proven by a parity test suite.

01 / WRAP

Keep your agent

OpenAI, LangChain, Google ADK, or Pydantic AI: wrap it once. Your original object is left untouched.

02 / DECIDE

Gate every call

Each tool invocation resolves the caller's role and returns allow, deny, or approval-required. Denials come back as tool results the model can react to, so a blocked call doesn't abort the run.

03 / PROVE

Audit it all

Decisions stream to the log with the exact constraint behind each verdict. Hot-reload policy without a restart.

FAQ

Questions, answered.

The short version of how Hexgate behaves in a real codebase.

01Do I have to rewrite my agent?

No. Hexgate ships adapters that wrap an existing OpenAI Agents, LangChain / LangGraph, Google ADK, or Pydantic AI agent without touching its logic. Swap your runner for HexgateRunner (or call wrap_langchain_agent / wrap_pydantic_agent) once. Your original agent object is left intact; the wrapper holds the policy and gates every tool the agent can invoke.

02Does gating every call add latency or a network round-trip?

No per-decision round-trip. Policy is evaluated in-process, either by the default pydantic engine or in production by a compiled WASM bundle run via wasmtime. The bundle is fetched once and refreshed only at turn boundaries with an ETag / 304 check, so individual decide() calls never leave the process.

03What happens when a call is denied?

A denial isn't a crash. The tool returns a [policy_denied] (or [approval_required]) marker that the model sees as the tool result, so the agent can recover or try a fallback instead of aborting the run. On Pydantic AI it surfaces as a ModelRetry; on LangChain as a structured {ok: false} result.

04How do approval-required tools work?

Mark a tool approval_required in policy, then pass an approval_handler when you wrap: True (auto-approve), False (auto-deny), or a sync/async (action, context) -> bool callback that inspects the specific call. hexgate chat prompts the terminal, hexgate serve auto-approves, and native code does whatever you wire.

05How does per-user scope work if one agent serves everyone?

Identity and rules are decoupled. A per-request User context manager carries who is calling (user_id, role, session_id, optional ttl) as a signed biscuit token; role policy files decide whatthat role can do. Role is resolved at call time from a contextvar, so a single wrapped agent serves many users concurrently without seeing each other's policies.

Unlike governance toolkits that key policy on agent_id alone, Hexgate threads the end-user identity through every decision. The same agent code runs with different effective permissions depending on which user invoked it. See the full breakdown in Hexgate vs Microsoft Agent Governance Toolkit.

06What does a policy actually look like?

A policy.yaml is deny-by-default with a tools map; each tool gets a mode (allow / deny / approval_required) and optional constraints like args.amount <= 500. Operators are ==, !=, <, <=, >, >=, in, not in, all ANDed.

The same constraint strings compile to OPA Rego for the WASM engine and run in-process for pydantic. A parity test suite proves both produce identical decisions.

07What makes a production bundle trustworthy?

Bundles are signed. The manifest carries a SHA-256 of every artifact (including the wasm_hash) plus a detached Ed25519 signature over that manifest. The hashes authenticate the files; the signature authenticates the manifest. Set HEXGATE_BUNDLE_REQUIRE_SIGNATURE=true to refuse anything unsigned or unverifiable. The signing key is the same root that signs your biscuit tokens.

08Do I need the platform, or can I run the SDK alone?

The SDK runs standalone: YAML on disk, in-process enforcement, no Docker or browser. The optional platform (a FastAPI control plane + React dashboard) adds browser policy editing, mintable tokens, a live Playground decision stream, and an append-only audit log in ClickHouse. Edit policy in the UI and the next turn picks it up.

Get started

Let your agents do more,
because nothing they do is unchecked.

Install the SDK and gate your first agent in minutes, spin it up on Hexgate Cloud, or book a walkthrough of the platform, audit log, and signed-bundle workflow.