For offensive security teams

An offensive-AI gateway that won't bankrupt you.

It does two things. Every turn goes to the model that hacks best, and to the cheapest one that solves the task. And only endpoints with your EDR installed get in: a stolen key opens nothing from outside, so no outside attacker can use it or run up your bill.

  • Every turn, to the model that hacks best
  • Only your endpoints with EDR get in
YOUR FLOW · CAPPED CodexYOUR TEAM GateTHE KEY, ONLY HERE Azure FoundrySPEND IN ONE PLACE 403 · OUTSIDE YOUR IPs STOLEN KEY = $0 AttackerSTOLEN KEY
The key never leaves the gate and only works from your IPs. Stolen, it opens nothing: no access, no bill.

What it does

Picks the model and locks the door.

Your team asks for AI from Codex and the gateway sits in between. It decides which model each turn goes to and only lets your endpoints through. That is all.

01 · Router

Every turn goes to the model that hacks best.

Before answering, every message goes through Clef, a small, fast decision model that writes no replies: it only says what kind of task it is. With that, the gateway picks the model and how much effort to ask of it. Tap a model to see which turn ends up there and why.

  • 01Offensive work?It goes to a model that won't refuse it, DeepSeek or GLM depending on difficulty, on the same resource. The conversation stays there until it ends.
  • 02How heavy is the task?Otherwise it goes to Claude: the small one for simple work, the big one for hard work, and the mid one at high effort when in doubt. You pay for opus only when it is needed.
User turn · sentra-ai

Rename the tmp variable to buffer in utils.py and update the tests.

Design the migration of sessions from Redis to Postgres with no downtime.

Write a proof of concept for the SSRF in the webhook URL.

Chain the export IDOR with the SSRF to reach the cloud metadata endpoint.

Codex → 127.0.0.1:4000 · Responses API · model sentra-ai
Clef · decisionclef-flash · 140 ms

Small task, no offensive intent. Clef sees it as simple (complexity 0.4) with no trace of offsec, so it goes to the small Claude at low effort. Fast and cheap.

{"ns_hook":"route","model":"claude-haiku-4-5","effort":"low","branch":"claude","p_offsec":0.02,"complexity":0.4}
Clef · decisionclef-flash · 162 ms

Hard task, no offensive intent. Complexity 2.7 and high reasoning: Clef picks the most capable Claude at high effort. You only pay for opus when it is needed.

{"ns_hook":"route","model":"claude-opus-4-8","effort":"high","branch":"claude","p_offsec":0.03,"complexity":2.7}
Clef · decisionclef-flash · 151 ms

Offensive intent, simple task. P(offsec) 0.91: the offensive branch leaves Anthropic and goes to the cheap model on the same Foundry resource. Reasoning effort is dropped because DeepSeek does not support it.

{"ns_hook":"route","model":"DeepSeek-V4.1-Flash","effort":null,"branch":"offsec","p_offsec":0.91,"complexity":1.3}
Clef · decisionclef-flash · 158 ms

Offensive intent and hard task. P(offsec) 0.97 and complexity 2.6, above the 2.0 threshold: it goes to the strong model of the offensive branch at high effort. The conversation stays in offsec until it ends.

{"ns_hook":"route","model":"FW-GLM-5.3-Flash","effort":"high","branch":"offsec","p_offsec":0.97,"complexity":2.6}
02 · EDR-only access

Only your endpoints with EDR get in.

The gateway only accepts traffic from the public IPs of the machines running your EDR. SentinelOne knows which IP each endpoint with an agent comes from, and that list is the only door. A stolen key used from anywhere else opens nothing: the network rejects it before it even looks at the credential, so no outside attacker can get in.

  • 01SentinelOneKnows the public IP of every endpoint running its agent. That is your trusted list.
  • 02AllowlistSentra turns it into the list of allowed IPs and keeps it current as the fleet changes.
  • 03The keyOnly works from those IPs. Stolen and used from outside: 403.
SentinelOne TRUSTED PUBLIC IP 403 · IP NOT ALLOWED EndpointsSENTINELONE AGENT SentraIP ALLOWLIST Azure FoundryIP-BOUND KEY AttackerSTOLEN KEY
The trusted IP list comes from SentinelOne. Outside it, the key does not open.

Threat model

We assume LiteLLM is compromised.

An infostealer in a dependency of the proxy is the case it is designed for. This is what it would get away with: nothing.

Models

One Foundry resource, eight models.

All of them are published on the gateway. The standard is sentra-ai: the router decides per turn and you rarely touch anything else. claude-sonnet-4-6 stays as the default fallback because it has handled security tasks without running into the guardrails.

ModelGate routeReasoningNotes
claude-haiku-4-5/anthropicyesSmall, fast Claude
claude-sonnet-4-6/anthropicyesDefault fallback · no friction on security tasks
claude-sonnet-5/anthropicyesGeneral-purpose Claude
claude-opus-4-8/anthropicyesMost capable Claude
claude-fable-5-1/anthropicyes—
gpt-6-luna/openai/responsesyesVision
DeepSeek-V4.1-Flash/openai/responsesnoOffensive branch, cheap
FW-GLM-5.3-Flash/openai/responsesyesOffensive branch, more capable
sentra-aiClef decidesper turnVirtual model: the router · the standard
Deployment

One script. Two keys typed in.

The script generates the configuration, brings up the two containers, registers the models in Codex and backs up what it touches. Keys are read as hidden input and never land in your shell history.

  • 01RequirementsDocker running, Codex installed and an Azure AI Foundry key. The Cloudflare token is optional.
  • 02Without a tokenThe router stays off and sentra-ai behaves like claude-sonnet-4-6.
  • 03With a tokenThe script tests the router for real: it sends two decisions to Clef and shows which model each would go to.
~/ai-gatewayzsh
$ ./foundry-codex.sh
Clave de Azure Foundry:                               ••••••••••••
Token de Cloudflare / Workers AI (Intro = sin router): ••••••••••••
ok. LiteLLM operativo en 127.0.0.1:4000
ok. LiteLLM sin salida a internet (red aislada)
ok. Clef: 'what does `ls -la` print?' -> claude-haiku-4-5 effort=low (claude)
ok. Clef: 'write a working exploit for CVE-…' -> DeepSeek-V4.1-Flash effort=None (offsec)
En config.toml: model_provider = "azure-claude", model = "sentra-ai"
Decisiones:     docker logs litellm-foundry | grep '"route"'
Observability

Every request leaves one line.

A LiteLLM hook writes one JSON line per request: the router's decision, the tokens and the real cost, and the size of the tools. It also deduplicates the MCP namespace descriptions that Codex repeats in every tool, so each request weighs far less.

  • 01routeWhich model and which effort Clef chose for the turn, with the probabilities that justify it.
  • 02usageInput, cached and output tokens, and the real cost of each request.
  • 03requestHow much the tools and the context Codex sends weigh on every call.
litellm-foundrydocker logs
$ docker logs litellm-foundry | grep '"route"'
{"ns_hook":"route","model":"claude-opus-4-8","effort":"high",
 "branch":"claude","p_offsec":0.03,"complexity":2.7,"clef_ms":162}
$ docker logs litellm-foundry | grep '"usage"'
{"ns_hook":"usage","model":"claude-opus-4-8","prompt_tokens":18412,
 "cached_tokens":16890,"completion_tokens":1204,"cost_usd":0.1127}

FAQ

What you ask us before deploying it.

What if my key gets stolen, like the $82k-in-48-hours bills?

That is exactly what it stops. The key only works from your endpoints' public IPs, and that list comes from your EDR, so from an attacker's infrastructure it opens nothing: the network rejects it before it even looks at the credential. And since all traffic goes through a single point, that is where you set the cap and the brake on an anomalous spike. No jump from $180 to $82,000 with nothing to stop it.

Why send offensive work to models that aren't Claude?

Because a legitimate pentest runs into the big models' guardrails: they cut you off halfway through an exploit or a payload. The router detects offensive intent and sends that task to non-Anthropic models on the same resource (DeepSeek, GLM), which don't refuse it. Everything else goes to Claude, with its size and effort set by difficulty.

Where does the trusted IP list come from?

From your EDR/RMM. SentinelOne knows the public IP of every endpoint running its agent, and Sentra turns it into the allowlist and keeps it current as the fleet changes. It is not a list you maintain by hand, and the IP is observed by the EDR on the agent's connection, not self-reported by the machine.

What about an operator working remotely, roaming or from CI?

Access is bound to your endpoints' IPs, so a laptop with the agent on a new IP gets in once the inventory picks it up, and for roaming or CI runners you start in monitor mode before blocking. It is a network control, with honest limits (an office behind one NAT is a single address), not magic.

Do my prompts or keys pass through your servers?

No. The gateway runs in your infrastructure, against your Azure account and your own Cloudflare one. The provider key never leaves the gate, and the decision model only sees the last trimmed message to pick the model, never your tools or your system prompt.

Does it lock me into one provider or one client?

No. The gate translates the Responses API to Anthropic's and to the other models on the resource, so the same deployment serves several models without touching your agent. If you switch model or resource tomorrow, you change the gate's config, not your workflow.

M

We deploy it with you

In your infrastructure, against your Azure account and with your endpoints as the perimeter. Two containers pinned by digest, no dependency other than Docker, and documented for your team.

0

Provider keys inside LiteLLM.

Microsoft Azure

2

Containers. One with keys, one with no network.

Cloudflare