Overview
Guardrails are a pipeline of rules that run before a request reaches any LLM provider. They can inspect, modify, or reject requests — giving you centralized control over every prompt that flows through GoModel.
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.
How It Works
- Messages are extracted from the incoming request into a normalized format
- The guardrails pipeline processes the messages (inject, modify, or reject)
- Modified messages are applied back to the original request
- The request continues to the LLM provider
Guardrails never see the raw API request types — they operate on a normalized message list. This means the same guardrail works identically for /chat/completions, /responses, and /messages.
Execution Order
Each guardrail has an order value that controls when it runs:
- Same order → run in parallel (concurrently)
- Different order → run sequentially (ascending)
Each sequential group receives the output of the previous group. If any guardrail returns an error, the request is rejected and never reaches the provider.
Configuration
Full Structure
Environment Variable
You can toggle guardrails without editing the config file:
Rule Fields
| Field | Required | Description |
|---|
name | Yes | Human-readable identifier. Supports spaces and unicode, but not /. |
type | Yes | Guardrail type: system_prompt, llm_based_altering, or header_modification. |
user_path | No | Optional base user path for internal auxiliary guardrail requests. |
order | No | Execution order. Default 0. Same value = parallel, different = sequential. |
Guardrail Types
system_prompt
Adds, replaces, or decorates the system prompt on every request.
Settings
| Field | Required | Description |
|---|
mode | No | inject, override, or decorator. Default: inject. |
content | Yes | The system prompt text to apply. |
Modes
inject
override
decorator
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
Replaces all existing system messages with the configured content.Behavior:
- Any existing system prompts are removed
- A single new system prompt is set
Prepends the configured content to the existing system prompt (separated by a newline). If no system prompt exists, adds one.Behavior:
- Existing system prompt
"You are a coding assistant." becomes:
- No system prompt → creates one with just the configured content
llm_based_altering
Rewrites selected message roles by calling an auxiliary model before the main
provider request runs. This is useful for PII anonymization and other
prompt-preserving rewrites.
The default prompt is derived from LiteLLM’s data_anonymization guardrail,
so a minimal config acts as an anonymizing preprocessor.
Settings
| Field | Required | Description |
|---|
model | Yes | Auxiliary model selector used for the rewrite call. |
provider | No | Optional routing hint for model. |
prompt | No | Custom rewrite prompt. Defaults to the built-in anonymization prompt. |
roles | No | Message roles to rewrite. Default: ["user"]. |
skip_content_prefix | No | Skip rewriting when the trimmed message starts with this prefix. |
max_tokens | No | max_tokens for the auxiliary rewrite call. Default: 4096. |
When llm_based_altering calls the auxiliary model, GoModel runs that call
through the normal translated request path in-process. That means ordinary
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:
guardrail
Guardrails are explicitly skipped for that internal request to avoid recursion.
Example
Conditionally sets or removes outbound provider-request headers based on
the inbound request headers. Use it to correct or steer the behavior of
existing clients and agents without changing them — for example pinning an
Anthropic-Beta feature flag for one coding agent, stripping internal debug
headers before they leave the gateway, or renaming a header a client sends
into the one a provider expects.
Unlike the message-based types, this guardrail never touches the request body.
It runs right after workflow resolution, and its changes are applied when the
outbound provider request is built — on translated routes, passthrough routes
(/p/{provider}/...), and realtime websocket upgrades alike.
Settings
| Field | Required | Description |
|---|
when | No | List of inbound-header conditions. All must match; empty means always apply. |
actions | Yes | List of outbound header changes, applied in order. |
Each when condition names a header and one predicate: matches (RE2
regex), equals (exact string), or present: true/false (existence check —
the default when no predicate is given). An explicit equals: "" matches a
header whose value is empty.
Each action has an action (set or remove) and a header. A set takes
either a literal value or from_header, which copies the first inbound
value of another header (the action is skipped when that header is absent).
Credential headers (Authorization, Cookie, X-Api-Key, …) and
transport headers (Host, Content-Length, Connection, …) are rejected
as condition sources and action targets at authoring time. Payload metadata
headers (Accept-Encoding, Content-Encoding, and Content-Type) are also
reserved because changing them can break decompression or body parsing. The
runtime refuses to touch all of these as a second line of defense.
Observability
Every header rule that changes a request is recorded on the audit entry’s
request-revision chain — the same mechanism used for body rewriters. Only the
delta is stored. When LOGGING_LOG_HEADERS=true, set headers include their
final values after normal credential redaction; otherwise only set header names
are stored. Removed headers always contain names only. A request with no
matching rules records nothing. Revisions describe the intended change and are
not proof that execution reached provider egress. The dashboard request log
shows each change as a Rewritten tab on the entry’s detail drawer.
Example
Examples
Single Safety Guardrail
The simplest setup — add a safety prefix to every request:
Multiple Guardrails in Parallel
Two guardrails running at the same order execute concurrently:
Sequential Pipeline
Guardrails with different orders run one after another. Later groups see the output of earlier ones:
Mixed Parallel and Sequential
How It Works With Different Endpoints
Guardrails operate on a normalized message format internally. The adaptation between API-specific request types and this format happens automatically:
| Endpoint | System prompt source | User messages source |
|---|
/v1/chat/completions | messages with role: "system" | messages array |
/v1/responses | instructions field | input field |
/v1/messages | system field | messages array |
You don’t need to think about which endpoint your users call. A single
guardrail rule works identically for all supported text endpoints.
Errors and Rejection
If a guardrail returns an error, the request is rejected immediately. The error is returned to the client and the request never reaches the LLM provider.
This is useful for future guardrail types that validate content (e.g., PII detection, content filtering). The system prompt guardrail does not reject requests — it only modifies them.