Overview
Guardrails apply a content policy to traffic passing through the gateway. A request whose prompt violates the policy is refused with HTTP 403 before it reaches any provider; a response that violates it is refused before it reaches the caller. Enforcement runs as a built-in plugin ahead of the semantic cache, so a cached answer cannot serve a prompt that today’s policy forbids.What a policy is made of
A policy has two layers, both configured on the Guardrails page of the console. The default blocklist (the Rules tab) is a list of case-insensitive terms and a block message. It is the quickest way to ban a handful of words across all traffic. Providers (the Providers tab) are named, independently evaluated rules. Each has a type:
The first enabled rule that matches blocks the request. Every rule carries an
optional message that replaces the default one when that rule is the one that
matched.
Choosing what a rule inspects
Every rule — the default blocklist and each provider — carriesapply_to:
apply_to defaults to input, so a policy written before response scanning
existed keeps behaving exactly as it did. Guarding responses is deliberate,
because a term that is right to ban from a prompt is not automatically right to
ban from an answer — a support assistant may need to discuss a term it must
never be asked about.
Responses and streaming
Anoutput rule inspects the model’s message content, its refusal text and its
reasoning trace. Reasoning is included on purpose: a rule banning a term should
not be satisfiable by emitting it in a reasoning block.
For streamed responses the gateway accumulates the text as chunks arrive and
scans the accumulation each time, so a banned term split across a chunk boundary
is still caught and the stream is cut at the first chunk that completes a
violation.
The model-based rule
Anllm provider needs an endpoint and a model, and optionally a bearer token.
The endpoint is usually this gateway’s own /v1/chat/completions, which lets the
classifier reuse a configured provider.
It fails closed. If the classifier is unreachable, rate-limited, mis-keyed or
returning errors, the request is blocked. A security control whose default
posture is “disabled on failure” is indistinguishable from a working one right
up until it matters.
Set fail_open on the provider to prefer availability over enforcement during a
classifier outage. It is off by default and turning it on is a deliberate choice.
Recursion is prevented by an out-of-band per-process header, not by anything in
the prompt, so a caller cannot craft text that exempts itself from moderation.
The API key is masked in API responses and preserved when the console saves an
unchanged policy. It cannot yet be sourced from an environment variable or a
secret manager the way provider keys can.
Configuring guardrails in config.json
Guardrails are configured as a plugin, in theplugins array. There is no
guardrails_config top-level block — a config file containing one is ignored,
and the gateway now says so at startup.
llm rule missing
its endpoint or model, or an unrecognised apply_to.
If a stored policy contains such a fault (saved before that check existed), the
plugin starts with the rest of the policy in force and reports status
degraded, which the console renders as a red banner. A policy running on less
than it was given never passes for a healthy one.
What is covered
Prompts are scanned for chat, responses, compaction, count-tokens, realtime turns, the WebSocket responses bridge, text completion, embeddings, rerank, image generation, image edit, speech, transcription prompts, video generation and video remix — and their streaming variants. The*_passthrough routes are scanned too. Because those forward an arbitrary
provider-shaped body with no schema to read a prompt out of, every string value
in the JSON body is scanned. This over-includes: model names, identifiers and
enum values are scanned alongside the prompt, so an aggressive rule can match
something that is not caller text. Narrow the rule if that happens — the
alternative was that appending _passthrough to a path skipped the policy
entirely.
A request type the plugin does not classify is blocked, not forwarded. An
unrecognised shape cannot be evaluated, and forwarding it while recording the
call as policy-checked is the one outcome worse than refusing it.
Known gaps in coverage
These carry caller text and are not scanned:- Batch create, file upload, cached-content create and container file create — the payload is an uploaded file or provider-specific JSON this hook does not parse.
- Passthrough bodies that are not JSON, or larger than 1 MiB.
- MCP tool arguments and tool results.
- OCR and image-variation requests, where the text is inside a binary or behind a URL and does not exist yet at request time.
What guardrails do not do
Stated plainly, because earlier documentation described all of it:- No managed moderation backends. There is no AWS Bedrock Guardrails, Azure Content Safety, Google Model Armor, CrowdStrike AIDR, GraySwan Cygnal or Patronus AI integration. The four types in the table above are the whole set.
- No CEL rules, profiles, or
/api/guardrails/*REST API. Guardrails are configured through the plugin config, in the console or inconfig.json. - No redaction or content modification. Blocking is the only outcome. There is no log-only or dry-run mode, so a new rule cannot be piloted without enforcing it.
- No sampling. Every request is evaluated synchronously.
- No per-virtual-key, per-team, per-customer or per-model binding. There is one policy for the whole gateway.
- No prompt-injection or jailbreak detection. The built-in classifier prompt asks about violence, illegal activity, self-harm, hate and sexual content.
- No per-rule timeout. The classifier is bounded by a shared 20-second client timeout and by the caller’s own context.
Observability
A block is recorded on the request’s log row aspolicy_decision=deny with
policy_reason=guardrails_violation, for input and output blocks alike. Which
rule matched is written to the process log only, and is not persisted or
surfaced in the console; there is no per-rule hit counter and no metric.

