LLM Proxies
An LLM Proxy is a managed gateway in front of a single LLM provider (OpenAI, Anthropic, Google or Mistral). Provision them via this API and your app traffic flows through AIronClaw with rules, budgets, logs and audit applied centrally.
The LLM Proxy object#
A proxy ties together a provider, an inbound auth method, an (optional) provider API key, an allow-list of models, an optional budget and per-key permissions. The backing record is stored in Redis and the routing/policy enforcement happens in the gateway + the aifw Lua plugin.
Fields
openai, anthropic, google, mistral.provider server-side. Returned for visibility but not user-settable.{ "mode": "aifw_api_key" } or { "mode": "jwt", "jwksJson": "..." }.{ "period": "monthly", "capUsd": 200, "hardBlock": false }. Period is one of fixed, daily, weekly, monthly.providerKey ciphertext, internal IDs, and any other server-side machinery are stripped from every response by the same toSafeLlmProxy filter. You can never read a provider key back through the API after writing it.
Manage proxies#
List proxies#
Returns every LLM proxy owned by the caller, in creation order.
curl https://app.aironclaw.com/api/llm \
-H "Authorization: Bearer $AIFW_PAT"Create a proxy#
Creates the Redis record and immediately wires the matching gateway Service + Routes + aifw plugin. The proxy goes live on its proxyHost the moment the gateway returns. Until you attach an inbound credential (API key or JWT), every request gets a 401.
Body
openai, anthropic, google, mistral.{ "mode": "aifw_api_key" }.curl -X POST https://app.aironclaw.com/api/llm \
-H "Authorization: Bearer $AIFW_PAT" \
-H "Content-Type: application/json" \
-d '{
"name": "production-openai",
"provider": "openai",
"providerKey": "sk-...",
"allowedModels": ["gpt-4o", "gpt-4o-mini"],
"defaultModel": "gpt-4o-mini",
"logConversations": true,
"budget": { "period": "monthly", "capUsd": 200, "hardBlock": false }
}'Retrieve a proxy#
Fetches a single proxy by its UUID. Includes the current upstream DNS pin (when the gateway has resolved one) so you can verify routing.
curl https://app.aironclaw.com/api/llm/$ID \
-H "Authorization: Bearer $AIFW_PAT"Update a proxy#
Partial update. Send only the fields you want to change. Setting providerKey to null or empty string removes the stored key. Changing provider swaps the upstream URL automatically and updates the gateway service.
curl -X PATCH https://app.aironclaw.com/api/llm/$ID \
-H "Authorization: Bearer $AIFW_PAT" \
-H "Content-Type: application/json" \
-d '{ "allowedModels": ["gpt-4o"], "logConversations": false }'Delete a proxy#
Tears down the gateway resources, removes the Redis record, and strips any llm:<id>:* permission tags from your API keys. Idempotent: returns 404 if the proxy does not exist.
curl -X DELETE https://app.aironclaw.com/api/llm/$ID \
-H "Authorization: Bearer $AIFW_PAT"Re-resolve upstream IP#
AIronClaw pins the upstream provider IP at first contact for SSRF protection. Call this endpoint after the provider rotates DNS, or whenever you see upstream unreachable errors, to refresh the pin.
curl -X POST https://app.aironclaw.com/api/llm/$ID/re-resolve \
-H "Authorization: Bearer $AIFW_PAT"Rules#
Rules attach inline policy to the proxy: rate limits, IP ACLs, prompt-replace (DLP), model routing, prompt guards and Lua lambdas (phase="access" only). The full ruleset is replaced atomically on every PUT; there is no per-rule add/remove endpoint.
List rules#
curl https://app.aironclaw.com/api/llm/$ID/rules \
-H "Authorization: Bearer $AIFW_PAT"Replace rules#
Replaces the full rule set in one shot. Allowed rule_type values on LLM proxies: ip_acl, rate_limit, prompt_replace, model_route, prompt_guard, lambda (with phase="access"). Every rule must include a tools array — use ["*"] to apply globally.
curl -X PUT https://app.aironclaw.com/api/llm/$ID/rules \
-H "Authorization: Bearer $AIFW_PAT" \
-H "Content-Type: application/json" \
-d '{
"rules": [
{
"rule_type": "ip_acl",
"tools": ["*"],
"action": "allow",
"cidrs": ["10.0.0.0/8", "203.0.113.0/24"]
},
{
"rule_type": "model_route",
"tools": ["*"],
"pattern": "^gpt-3\\.5",
"target_model": "gpt-4o-mini"
}
]
}'Prompt Guard#
The prompt_guard rule type runs detection on the inbound prompt before it reaches the LLM (phase="request"), or on the model's completion before it leaves the gateway (phase="response" — alert-only on LLM proxies). Three layered detection modes are available: regex (fast deterministic detectors curated on public taxonomies), judge (an LLM classifier with semantic understanding), and both (regex first as a cheap pre-filter, judge on misses for defense-in-depth).
Modes
detectors against the input. Sub-millisecond, no external calls. Patterns are curated on OWASP LLM01, MITRE ATLAS AML.T0051, Microsoft Prompt Shields, Anthropic browser-use defenses, the PromptInject (arXiv 2211.09527) and ChatInject (arXiv 2509.22830) papers, and NVIDIA garak probes. Nothing for the user to write.judge block. Returns a structured verdict { verdict, confidence, category }. A block verdict is acted on when confidence ≥ threshold.On a positive verdict, action decides what happens: block returns 403 to the client; rewrite replaces the matched content using rewrite_template (supports $0..$9 back-references); alert is log-only.
Built-in detectors#
The detector catalog is a curated library of regex-based rules organized into seven categories. Reference detectors by id in the rule's detectors array. The dashboard lists every available id with a short description; the categories are:
Detector categories
judge mode, not a replacement.Every detector regex is anchored, linear, and uses bounded quantifiers (no nested unbounded .*) so the matching engine cannot fall into exponential backtracking on adversarial input.
Judge configuration#
The judge block configures the LLM classifier for mode="judge" or mode="both". The classifier endpoint is hardcoded per provider — no user-supplied URL — so the judge call is SSRF-safe and TLS verification is always on.
judge fields
openai, anthropic, google, mistral. Picks the chat-completions endpoint and request shape.prompt_injection_semantic, jailbreak_intent, toxicity, bias, confabulation, off_topic. Each adds a labelled bullet to the system prompt.user (default — direct prompt injection on the user turn), user+system, or all (entire conversation, needed for indirect / RAG-poisoning detection — more expensive on long contexts).pass (default, fail-open on infra glitches) or block (fail-closed). Pick the side you'd rather err on.head or head_tail (default) — head_tail keeps both initial and appended-injection windows without doubling token cost.Native OCR / PDF-attachment scanning is on the roadmap. In the meantime, when scope="all" the judge receives exactly the same payload the upstream model would receive — including image URLs and base64 attachments. Pick a multimodal model as the judge (e.g. gpt-4o, claude-haiku-4-5, gemini-2.0-flash) and you get multimodal injection detection at zero extra integration cost: the judge sees what the protected model would see.
prompt_guard evaluates a single request at a time. To defend against cross-call abuse — the same identity issuing many individually-legitimate requests that cumulatively constitute an attack — pair it with a rate_limit rule scoped on match_key="consumer" or "api_key" with ban_after_n_exceeded + ban_timespan set. The combination of per-request semantic detection and per-identity threshold- with-ban handles the bulk of the abuse model. True trajectory-based detection — sequence-level analysis of a series of calls — is on the roadmap.
Examples#
{
"rule_type": "prompt_guard",
"tools": ["*"],
"phase": "request",
"mode": "regex",
"detectors": ["pi_ignore_previous", "pi_chatml_smuggle", "jb_dan", "jb_aim"],
"action": "block"
}Budgets#
Two budget layers exist per proxy: a proxy-level cap (set on the proxy object via budget) and a per-(key, proxy) cap for fine-grained per-tenant control. Both share the same period semantics: fixed (no rollover), daily, weekly or monthly. Set hardBlock: true to refuse requests once the cap is hit; otherwise the budget is informational and only flags an alert.
Reset proxy window#
Zeros the current-window spend counter for the proxy. Daily history and monthly totals are preserved — only the enforcement counter is cleared. Returns 204 No Content.
curl -X POST https://app.aironclaw.com/api/llm/$ID/budget/reset \
-H "Authorization: Bearer $AIFW_PAT"List proxy keys#
Lists the API keys that have been granted access to this proxy (their credential carries an llm:<id>:* tag), with their per-key budget and current-window spend. Keys are shown masked (aifw_p…_xyz4).
curl https://app.aironclaw.com/api/llm/$ID/keys \
-H "Authorization: Bearer $AIFW_PAT"Per-key budget (get / set / delete)#
Read or upsert the budget for a specific (proxy, key) pair. The same path supports GET (read), PUT (upsert) and DELETE (remove).
PUT body
fixed, daily, weekly, monthly.curl -X PUT https://app.aironclaw.com/api/llm/$ID/keys/$CRED_ID/budget \
-H "Authorization: Bearer $AIFW_PAT" \
-H "Content-Type: application/json" \
-d '{ "period": "monthly", "capUsd": 50, "hardBlock": true }'Reset key window#
Zeros the current-window spend counter for the (key, proxy) pair. Returns 204 No Content. Returns 400 if no budget is configured for that pair.
curl -X POST https://app.aironclaw.com/api/llm/$ID/keys/$CRED_ID/budget/reset \
-H "Authorization: Bearer $AIFW_PAT"Usage#
Three flavors of usage data are exposed: a monthly summary with per-key breakdown, a daily series with per-model split, and a per-key daily series. Costs are computed at request time using the AIronClaw pricing table (the response includes the pricingVersion so you can detect price-list changes).
Monthly summary + per-key (current month)#
Query
curl "https://app.aironclaw.com/api/llm/$ID/usage?months=6" \
-H "Authorization: Bearer $AIFW_PAT"Daily history + budget window#
Query
Returns daily totals with per-model breakdown plus a snapshot of the running budget window: spentCents, tag (e.g. 2026-04 for monthly), and therollsOverAt timestamp.
curl "https://app.aironclaw.com/api/llm/$ID/usage/daily?days=7" \
-H "Authorization: Bearer $AIFW_PAT"Per-key daily history + budget window#
Per-key daily counters are notscoped per proxy — a single key's daily hash covers every proxy it has touched, so the top-line numbers reflect the key's entire activity. The per-model split lets you tell which proxies' models contributed.
curl "https://app.aironclaw.com/api/llm/$ID/keys/$CRED_ID/usage/daily?days=14" \
-H "Authorization: Bearer $AIFW_PAT"Delete history#
Removes daily usage hashes. Exactly one of the two query parameters must be provided. Budget-window enforcement counters are not touched — call budget/reset separately for that.
Query (one of)
The same shape exists scoped to a single key: DELETE /api/llm/:id/keys/:credId/usage/history.
curl -X DELETE "https://app.aironclaw.com/api/llm/$ID/usage/history?before=20260301" \
-H "Authorization: Bearer $AIFW_PAT"Logs#
When logConversations is enabled on a proxy, every request and response is encrypted with AES-256-GCM and written to Redis with a 7-day TTL. The list endpoint returns metadata only; full plaintext is fetched on demand from the detail endpoint.
List logs (metadata only)#
Query
curl "https://app.aironclaw.com/api/llm/$ID/logs?limit=50" \
-H "Authorization: Bearer $AIFW_PAT"Log detail (decrypts plaintext)#
Decrypts the conversation log on the server with the AIronClaw master key and returns the plaintext request/response. The response is never cached (Cache-Control: no-store).
curl https://app.aironclaw.com/api/llm/$ID/logs/$LOG_ID \
-H "Authorization: Bearer $AIFW_PAT"Purge logs#
Removes every log blob, metadata hash and the index for this proxy. Returns the number of removed entries.
curl -X DELETE https://app.aironclaw.com/api/llm/$ID/logs \
-H "Authorization: Bearer $AIFW_PAT"