Skip to content

Overview for Developers

Brutor is a gateway. You don’t rewrite your application to use it — you point each client at the gateway (one host, with its own path for each kind of AI resource: LLM, MCP, A2A, skills) and your existing OpenAI SDK, Anthropic SDK, MCP client, or plain HTTP code keeps working. The moment your traffic flows through the gateway, it picks up, with zero agent-side code:

  • Guardrails — prompt-injection and jailbreak detection, PII redaction or blocking, secrets detection, toxic-content filtering, banned words — on inputs and outputs across every surface. See Guardrails.
  • Budgets and limits — requests-per-minute, tokens-per-day, dollars-per-minute, concurrency caps, per group and per model. See Limits.
  • Failover and routing — routing groups retry, cool down failing models, and fall back to secondaries without your code noticing.
  • Audit trail — every request, tool call, block, and approval decision is logged with cost, latency, and the identity that made it. See Logs.
  • Cost tracking — per-token cost attribution by user, group, and model in Mission Control.
  • Human-in-the-loop approvals — sensitive actions pause (the caller gets 202 Accepted) until a person approves them. See Approvals.
  • Agent identity — bind your key to an agent identity and it is default-deny: only the models, tools, skills and agents you grant are reachable. See Agent identity & policies.

And with a few assurance headers on each call:

  • Assurance — calls grouped into runs are checked against your AI System’s signed contract and learned baseline; drift raises a finding. See The run ledger.
# Before
client = OpenAI(api_key="sk-...")
# After — same code, now governed (MCP, A2A and skills have their own paths)
client = OpenAI(
api_key="sk_brutor_api_...",
base_url="http://localhost:8100/v1/proxy/llm",
)

All developer-facing runtime traffic goes to the Core Proxy (Rust, port 8100). The Control Plane (5050) is admin-only configuration — you only touch it from setup scripts or the Admin UI.

┌──────────────────────────────────────────┐
Your app / agent ────▶│ Core Proxy :8100 │
│ │
OpenAI SDK ──────────▶│ /v1/proxy/llm/* LLM (OpenAI-compat)
Anthropic SDK ───────▶│ /v1/messages LLM (Anthropic native)
MCP client ──────────▶│ /v1/proxy/mcp/{id} MCP tool servers
MCP client ──────────▶│ /v1/proxy/vmcp/{id} Virtual MCP (aggregated)
A2A peer ────────────▶│ /a2a/{tenant}/{card} A2A v1.0, inbound
Your agent ──────────▶│ /v1/proxy/a2a/outbound A2A to remote agents
Agent runtime ───────▶│ /v1/proxy/mcp/system- Agent Skills (MCP,
│ agent-skill-server-* progressive disclosure)
OAuth client ────────▶│ /v1/proxy/oauth/* OAuth proxy for MCP tools
Custom portal ───────▶│ /v1/portal/* Portal API (end-user auth,
│ models, approvals, notifications)
└──────────────────────────────────────────┘
│
guardrails · limits · routing
approvals · audit · cost tracking
Surface Base path What it’s for
LLM proxy /v1/proxy/llm OpenAI-compatible chat, completions, responses, embeddings, images, audio, video
Anthropic native /v1/messages Anthropic SDK and Claude Code, unmodified
MCP /v1/proxy/mcp/{server_id} JSON-RPC 2.0 to any registered MCP server (streamable HTTP)
Virtual MCP /v1/proxy/vmcp/{virtual_server_id} One endpoint aggregating tools from many MCP servers
A2A v1.0 (inbound) /a2a/{tenant_id}/{card_name}/message:send Peers call agents the gateway hosts: messaging, tasks, signed agent cards
A2A outbound /v1/proxy/a2a/outbound Your agent calls a remote A2A agent by its card id; guardrails, audit and the signed delegation chain apply
Agent Skills /v1/proxy/mcp/system-agent-skill-server-{tenant} Progressive-disclosure skills over MCP — load instructions, run scripts in the governed sandbox
OAuth proxy /v1/proxy/oauth/{mcp_server_id}/{idp_server_id} Per-user OAuth for SaaS-backed MCP tools
Batch & files /v1/proxy/batch/jobs, /v1/proxy/files Async batch jobs and multimodal file upload over the same governed LLM surface. See the LLM proxy reference
Portal API /v1/portal/* End-user auth, model/server discovery, approvals, notifications — the API the shipped User Portal runs on

Authentication: API key, workload token or portal JWT

Section titled “Authentication: API key, workload token or portal JWT”

There are three developer-relevant credentials:

Credential Header Who uses it How you get it
API key Authorization: Bearer sk_brutor_api_... or X-API-Key: sk_brutor_api_... Agents, scripts, CLIs, backend services Admin UI → Resource Group → API Keys, or via the Admin API
IdP workload token Authorization: Bearer <OIDC or SPIFFE token> Agents with their own workload identity Your IdP; the tenant’s identity provider maps its subject to an agent identity
Portal JWT Authorization: Bearer <jwt> (or X-Gateway-Authorization: Bearer <jwt>) End users in a portal/chat client you build POST /v1/portal/auth/login with the user’s username/password

API keys are sk_brutor_api_ + 43 URL-safe characters, stored SHA256-hashed server-side, and bound to a resource group — for an agent, that group is its AI System, so every call and every run with the key is attributed to that system. Give the key an agent_id and every call also acts as that agent identity: default-deny, with explicit allow / approval_required / deny grants per model, tool, skill and agent card. Who is acting always comes from the credential, never from a header. Portal JWTs are HS256 tokens minted by the Core Proxy at login; they carry the user’s identity, and the user’s group memberships determine what models and tools they can reach.

Two more headers matter on multi-tenant installs:

Header Example Purpose
X-Tenant-ID default Tenant context when it can’t be derived from the credential
X-Resource-Group rg-01ABC... Pin the request to one of the caller’s groups (defaults to primary group, then first membership)

An agent makes many gateway calls to do one task. These optional headers tell the gateway which calls belong together and which release of your code made them. Without them every call is its own run, and the run ledger has nothing to group, close or baseline.

headers = {
# the run: the same id on every LLM, MCP, skill and A2A call of one task
"x-brutor-run-id": run_id,
"X-Brutor-Step-Id": "gather", # optional: name the phase
"X-Brutor-Turn-Id": "t1", # optional: one pass of the loop
# the release: which software, which version
"X-Brutor-Agent-Name": "loan-screening-agent",
"X-Brutor-Agent-Version": "0.2.0",
}
# on the final call of the task
headers |= {"X-Brutor-Run-End": "completed", "X-Brutor-Run-Outcome": "resolved"}
Header Example Purpose
x-brutor-run-id screen-4711-01K… Groups every call of one task into a run
X-Brutor-Step-Id / X-Brutor-Step-Name gather Names a phase; the ledger attributes cost and tools per step
X-Brutor-Turn-Id / X-Brutor-Turn-Seq t1 / 1 One pass of the agent loop; inferred from model-call boundaries when absent
X-Brutor-Run-End completed · completed_degraded · exhausted · errored · blocked_policy · abandoned · cancelled Closes the run explicitly instead of waiting for the idle sweep
X-Brutor-Run-Outcome resolved · escalated · handed_off · abandoned_by_user How the task ended for the user
X-Brutor-Agent-Name / -Version / -Build loan-screening-agent / 0.2.0 / sha256:… The release, checked against the release the contract approved. See Agent implementation

Develop your AI System explains why to send them from day one, and Build an agent §6 wires them into a LangGraph loop.

I want my app or agent to call an LLM → point your OpenAI/Anthropic SDK at the gateway. One base-URL change.

I want my agent to use tools (CRM, Slack, databases…) → call registered MCP servers through /v1/proxy/mcp/{server_id}, or aggregate many behind one Virtual MCP endpoint.

I’m building a chat UI for end users → build on the Portal API: login, model pickers, streaming chat, approval inbox, notifications.

I have a REST tool of my own → wrap it as an MCP server (a few dozen lines with FastMCP), register it, and every agent in the org can use it — governed.

My agent needs to talk to other agents → publish a signed A2A agent card for inbound calls, and reach remote agents through /v1/proxy/a2a/outbound.

I want reusable, versioned automation with human approval gates → package it as an Agent Skill.

Brutor governs traffic that points at the gateway: API calls, agents, internal apps, CLIs, portals — across every provider and resource you register. The moment a client (your agent, curl, Goose, Claude Code, a custom portal) is configured with the gateway’s URL, its traffic is governed, metered, and audited. Brutor does not — and does not claim to — govern what employees do inside the consumer ChatGPT or Claude apps themselves; that is the domain of network controls and the vendors’ own admin consoles. (Brutor can import vendor admin telemetry for cross-vendor cost observability — see Observing bought AI.)

If the gateway isn’t running yet, start with the Quickstart — Docker Compose, about ten minutes. Then make your first governed LLM call and watch it appear in Mission Control. When you are building an agent, follow Your AI System from develop to assure.