AI Helpers
One GPU, one charter,
every coding agent.
A sovereign OpenAI-compatible gateway in front of your own GPU — your own graphics processing unit. It carries your team's standards, enforces rules that cannot be bypassed, and counts the context budget with the server's own tokenizer instead of trusting the client's estimate.
Why the server has to count
On 2026-09-25 an agent session believed it was under its limit and sent 40,433 tokens to a server configured for 32,768. The server counted for real, refused the whole request, and the session died.
The client was not lying. It was estimating, with its own tokenizer rule, which is more optimistic than the model's on French and on code. The gap was 55%. Every harness does this: Claude Code compacts at effective context − 13,000, an estimate about an estimate.
The only party that can count correctly is the one holding the tokenizer, and that party is the server.
| tool schemas (7) | 2,875 | client |
| client system prompt | 960 | client |
| charter | 465 | ai-bunker |
| knowledge index | 142 | ai-bunker |
| FLOOR | 4,442 |
backend=server exact=True — counted by the engine, not estimated. A floor nobody can decompose is a floor nobody can reduce.
Four stages, and the order matters
Any client that speaks OpenAI speaks ai-bunker, and gets all four without being configured for them. That is the argument for putting this at the endpoint rather than in each developer's harness: a plugin can be disabled by editing a file, and a different harness simply never had it.
-
01 — inbound
Screen what the agent reads
A role: "tool" message is text the world just handed the agent. Secrets leak out here, and indirect prompt injection arrives here. Both are caught, and what remains is marked as data rather than instruction.
-
02 — charter
Serve the standards
Your team's pages, advertised as a ~600-token index rather than pasted into every prompt. A page costs nothing until it is opened, so the charter can be large.
-
03 — budget
Measure, then refuse
A session with no room to work is refused here, with the component responsible named — rather than failing downstream as a context-length error nobody can act on.
-
04 — outbound
Refuse what the agent runs
When the model answers with a tool call carrying a shell command, that command has not run yet. This is the one enforcement point no client can bypass — including when the answer is streamed, which is what every agent client asks for by default. A call delivered in fragments is assembled and judged by the same code, while the text around it streams through untouched.
Two attacks nobody else screens for
Both were found by sending payloads through the real engine rather than by reading the patterns, which is how most of this project's rules were found.
Output that exfiltrates by being looked at
A tool result carrying  needs no instruction and no cooperation from the model: the moment a client renders it, the renderer fetches the address and the query string lands in somebody else's access log. Nothing asks the agent to send anything, so a rule looking for a request to send finds nothing.
The payload cannot be the signal — a length threshold is something to stay under, and parameter names are the attacker's to choose. What works is an allowlist of hosts whose untrusted content may fetch itself, which is the Content Security Policy model and never reasons about the payload at all.
An estimate that refuses on the wrong side
When no tokenizer is reachable the budget falls back to a ratio, and that ratio was under-counting by up to 37% — the same direction as the incident this project was built around. Measured across thirteen real files and three tokenizer families, the true figure ranges from 2.51 to 4.23 characters per token.
It now takes the measured floor, so an estimate is never optimistic, and it says so: it over-counts ordinary prose by about half, which is the recoverable direction. A figure that can refuse a request must never flatter it.
Work the engine does not have to do
A gateway in front of one graphics processor is also deciding what that processor spends itself on. Both of these were found by measuring, against a socket that accepts and answers slowly, rather than by reading the code.
A client that gives up stops the engine
A non-streaming request used to be awaited and nothing watched the caller. A client that timed out after one second left the engine free to generate for twenty, and the upstream never saw the connection close — so it had no way to know, and no reason to stop.
The call is now raced against the client's own connection and cancelled when that goes away, which closes the socket — the only signal an engine can act on. Measured on the same stall: the upstream hangs up at one second instead of never. Streaming already behaved, because closing the response closes the stream with it; that is measured too rather than assumed.
Half a command is not a command
An upstream stream can simply stop — a provider timeout, a proxy capping response duration, a reset. The client then gets a normal close and treats a truncated turn as a finished one. Here that was worse than untidy: rm -rf / is refused by a BLOCKER, and cut to {"command": "rm -rf it matches nothing, because the rule needs a target.
The prefix screened clean and was relayed as if complete. A tool call whose arguments do not parse is now refused rather than passed on: screening a fragment answers a question nobody asked, and answers it confidently.
Rules that ship with their evidence
A regular expression is the easiest thing here to get subtly wrong, and a wrong one fails silently: it stops matching, nobody notices, the guardrail is gone. So every rule carries its own examples, and they run in continuous integration (CI).
$ ai-bunker rules --check
rules loaded 27
assertions passed 247
assertions failed 0
rules carrying evidence 100%
Three shipped patterns were wrong on first write — including one whose bound could not cross the dot in ~/.aws/credentials. The examples caught all three before they reached a deployment.
2
scopes: text on its way in, commands on their way out
3.85
bits/char — the measured entropy threshold that tells a credential from a placeholder
0
catastrophic patterns allowed to load — the guard caught two in a working production rule set
What it is not
- Not a router with a hundred providers. LiteLLM is simply better at that, and ai-bunker sits in front of it rather than competing.
- Not a content classifier. LLM Guard and Guardrails AI ship trained models; this ships versioned regular expressions. Reviewable and free to run — a trade, not a win.
- Not mature. This is 0.2.0-alpha next to projects with years of production use.
What it is
The gateways route. The guardrails inspect. Neither counts.
ai-bunker is the intersection, and that corner of the map is still thinly populated. Less empty than in 2025, though: a token budget at the gateway is now a pattern others describe. What stays narrow is whose tokenizer does the counting.
Every number on this site was measured. Where a figure is an estimate, it says so.