Skip to main content

Overview

A guardrail is a named, configured instance of a plugin. Guardrails run inside every workflow that references them, in one of three phases:
  • prompt: after routing, before the request reaches the provider
  • response: on the complete response, before it reaches the client
  • stream: on each streamed event (or on the buffered stream, depending on the plugin)
A guardrail can edit content, add headers, reject the request with an error, answer it with a safe message, or let it through with a warning. Guardrails work across all text-based endpoints:
  • /v1/chat/completions
  • /v1/responses
  • /v1/messages
Guardrails for images, TTS, STT, and video models are planned as a separate system and are not covered here.

Quick Start

Add a guardrails section to your config/config.yaml:
That’s it. Every request now gets the safety prompt prepended to its system instructions.

Manage from the Dashboard

Guardrail definitions can also be created and edited from the admin dashboard instead of config.yaml — useful for iterating on rules without a redeploy, or for operators who don’t manage this repo’s config directly. Open Guardrails in the sidebar and click Create Guardrail: give it a name, pick a type (every loaded plugin with a prompt, response, or stream hook is listed), optionally scope it to a user_path, and fill in the form the plugin declares. The Advanced section holds the failure mode and timeout. config.yaml entries are seeded into the same store at startup and stay in sync with it, so dashboard-created and config-declared guardrails appear side by side. The Plugins list at the bottom of the page shows every loaded plugin type, its hooks, source, and health.
Runtime guardrail execution still depends on GUARDRAILS_ENABLED. With it off, the page still lets you manage definitions — they just don’t run on live traffic yet.

How It Works

  1. The request is mapped to a unified Prompt (system, user, assistant, and tool messages with stable IDs)
  2. The prompt chain runs: each guardrail edits the prompt, or decides to block, respond, or warn
  3. Edits are applied back to the original request, which continues to the provider
  4. The response chain runs on the complete response (or the stream chain on the stream) before anything reaches the client
Guardrails never see the raw API request types — they operate on the unified Exchange. The same guardrail works identically for /chat/completions, /responses, and /messages.

Execution Order

Each guardrail has an order value (the workflow step) that controls when it runs within its phase:
  • Same order → run in parallel (concurrently)
  • Different order → run sequentially (ascending)
Each sequential group receives the output of the previous group. Guardrails that edit content (system_prompt, llm_based_altering, string_replace) cannot share an order with another editing guardrail; only one editor per order, and any number of non-editing checks (llm_judge, header_edit) next to it. When several guardrails at one order decide differently, the most severe decision wins: block > respond > warn > allow.

Configuration

Full Structure

system_prompt and llm_based_altering also accept their settings in a typed block named after the type (system_prompt: / llm_based_altering:), as in the Quick Start. The typed block and config: are equivalent; use config: for every other type. Line-oriented keys such as string_replace.rules and the header_edit lists accept either a block scalar (|) or a YAML list of strings, joined by newlines.

Environment Variable

You can toggle guardrails without editing the config file:

Rule Fields

Guardrail Types

Five types ship with GoModel. Each is a built-in plugin; the tables list its config keys as they appear under config: and on the dashboard form.

system_prompt

Adds, replaces, or decorates the system prompt on every request. Phases: prompt. Edits content: yes.

Settings

Modes

Adds a system message only if none exists. Existing system prompts are left untouched.
Behavior:
  • Request has no system prompt → adds one
  • Request already has a system prompt → no change

llm_based_altering

Rewrites the text of selected message roles by calling an auxiliary model. In the prompt phase it rewrites the request; in the response phase it rewrites the assistant’s reply (when roles includes assistant). This is useful for PII anonymization and other content-preserving rewrites. The default prompt is derived from LiteLLM’s data_anonymization guardrail, so a minimal config acts as an anonymizing preprocessor. Phases: prompt, response. Edits content: yes.

Settings

Rewrites run through the normal translated request path in-process, so workflow selection, failover, usage, audit, and cache behavior still apply. The internal request uses:
  • path: /v1/chat/completions
  • user path: {guardrail.user_path or caller user path}/guardrails/{guardrail name}
  • request origin: plugin
Guardrails are skipped for that internal request to avoid recursion. A rewrite that fails keeps the original text; a cancelled or timed-out call fails the guardrail (see fail_mode).

Example

string_replace

Replaces, flags, or blocks text that matches a list of literal or regular expression rules. Works on prompts, responses, and streams. Phases: prompt, response, stream. Edits content: yes.

Settings

In the stream phase, replace and warn transform events in flight with the configured lookbehind. block and respond buffer the whole stream so nothing leaks before the decision, at the cost of delaying the first token until the response is complete.

Example

header_edit

Sets, adds, and removes HTTP headers on the request, the client response, and the upstream provider call. It never edits content, so it can share an order with an editing guardrail. Phases: prompt, response. Edits content: no.

Settings

Every key is a list of lines. Set and add lines look like Name: value; remove lines are a bare Name. Blank lines and # comments are ignored. Credential headers (Authorization, X-Api-Key, Cookie, …) can never be edited, and names containing secret or token cannot be set.

Example

llm_judge

Asks a judge model whether the prompt (or the response) violates a policy and blocks, answers, or flags it based on the verdict. The judge must reply with one JSON object {"verdict":"allow"|"block","reason":"..."}; the default instructions do that and tell the model to ignore instructions inside the content. Phases: prompt, response, stream (buffered). Edits content: no.

Settings

Identical text is judged once per request, so an instance that runs in both the prompt and the response phase does not double-charge for the same content. In the stream phase the whole stream is buffered and judged as a complete response.

Example

Examples

Single Safety Guardrail

The simplest setup — add a safety prefix to every request:

Checks in Parallel with an Editor

A non-editing check shares order 0 with the system prompt editor and runs concurrently with it:

Sequential Pipeline

Guardrails with different orders run one after another. Later groups see the output of earlier ones:

Response Phase: Redact Secrets on the Way Out

Runs on the complete response before it reaches the client. Set fail_mode: open if you prefer an unredacted answer over a 500 when the guardrail itself fails.

Stream Phase: Redact In Flight and Judge the Whole Answer

The same instance can be referenced in several phases. mask-keys transforms streamed chunks in flight (64 characters of lookbehind, so a key split across two chunks is still caught). answer-judge needs the whole answer, so it buffers the stream and the client receives it once the verdict is in.
A rule in config.yaml places one instance in one phase. To run the same instance in two phases, add it to the workflow twice with different phase values on the dashboard or through POST /admin/workflows; see Workflows.

How It Works With Different Endpoints

Guardrails operate on a unified message format internally. The adaptation between API-specific request types and this format happens automatically:
You don’t need to think about which endpoint your users call. A single guardrail rule works identically for all supported text endpoints.
For /v1/messages, a request that runs any guardrail takes the translated path (the native passthrough is skipped). A response cut by a guardrail is reported with finish_reason: "content_filter" on OpenAI-compatible endpoints and stop_reason: "end_turn" on /v1/messages.

Decisions, Errors, and Rejection

A blocked request never reaches the provider; a blocked response never reaches the client. See Plugins for how decisions merge when several guardrails run at the same order.
Last modified on September 4, 2026