The model gateway checks content in three layers, each of which can only add checks:
- DLP scans every prompt with your organisation's patterns, acting by data classification. Always on.
- Guardrails are your own rules: deny and allow patterns, an injection heuristic, mandated system text and output checks. Off until you add rules.
- Governance detectors add sensitive-data detection with tokenisation, a scored injection detector, tool-call governance and output safety checks. Shadow mode by default.
DLP on every request
Every text part of a prompt is scanned with the organisation's built-in and custom DLP patterns: system prompts, messages, tool call arguments, tool results, thinking text and documents with text. The action follows the request's classification, not the pattern's own setting:
| Classification | Action on a finding |
|---|---|
| public, internal | The finding is redacted, and the request continues |
| confidential, restricted, or any request in a regulated project | The request is refused with dlp_blocked |
Content the gateway cannot read as text, such as a base64 PDF, a file reference or an unknown block type, is refused at the refusing levels rather than passed unscanned. Images and audio pass as media. If the scan cannot run, the request is refused with 503 dlp_unavailable. Request metadata and tags are scanned the same way.
DLP patterns themselves are managed in the console's data protection pages; see Security.
Guardrails
Open Models > Guardrails, or use GET/PUT /api/v1/models/guardrails (models:view to read, models:admin to change). PUT replaces the whole list of at most 50 rules.
{
"rules": [
{"scope": "org", "input_deny": ["(?i)\\bproject falcon\\b"],
"system_prepend": "Answer in English.", "output_dlp": true, "output_json": true, "injection": "log"},
{"scope": "project", "project_id": "<project id>", "input_allow": ["(?i)payments"], "injection": "block"},
{"scope": "model", "model_key": "team/default", "output_deny": ["internal-codename"]}
]
}
Scopes
| Scope | Applies to |
|---|---|
org | Every request (at most one org rule) |
project (project_id) | Requests for that project, and requests without a project by its members |
model (model_key) | Requests that name the model or route, and every request that model may serve as a route target or fallback |
Every enabled rule that applies is enforced together: deny patterns add up, every rule with allow patterns must be satisfied, system text is concatenated, output checks are on when any rule turns them on, and the strictest injection action wins.
Rule fields
| Field | Effect |
|---|---|
input_deny | No user message may match. A match refuses with 400 guardrail_blocked. A deny always wins over an allow. |
input_allow | The latest user message must match at least one pattern of each rule that lists allow patterns. |
injection | off (default), log or block. A fixed, deterministic pattern list over user text and untrusted content (tool results, documents). |
system_prepend, system_append | Organisation-mandated system text, at most 8192 characters each |
output_dlp | Scan the answer like the prompt: redacted at public and internal, withheld with 422 dlp_blocked at higher levels |
output_deny | An answer that matches is withheld with 422 guardrail_blocked |
output_json | When the request asked for JSON, the answer must match the requested schema |
enabled | Default true |
Patterns are RE2 regular expressions, case-sensitive unless they start with (?i), at most 32 per list and 512 bytes each. A pattern that matches empty text is refused. A stored pattern that no longer compiles refuses requests with 503 service_unavailable.
The injection heuristic recognises phrasings such as "ignore previous instructions", requests to reveal the system prompt, persona switches, chat template tokens, "do not tell the user" and requests to send credentials elsewhere. It catches common phrasings, not every attack, and complements tool and data permissions; it does not replace them.
Streamed answers
Streamed answers are checked through a sliding window: text is held back until 2048 bytes of later text have been checked with it, so a finding up to 2048 bytes long is caught before any of it is sent. On a block the stream ends with an error event in the caller's wire format. A streamed answer that must be validated as JSON is held whole (at most 4 MiB) until it is complete.
A withheld answer is still charged for the tokens the provider generated.
Audit
Guardrail decisions are added to the request's models.gateway.request audit entry: how many rules applied, whether input passed, which rule and pattern refused, the injection patterns found, and the output result. Rule changes are audited as models.guardrails.put.
Governance detectors
The AI Gateway service adds a detector chain, configured on AI Gateway > Governance and reviewed on Detections, Governance findings and Risk. Everything runs locally; no detector calls an external service.
| Detector | Checks | Default when added |
|---|---|---|
sensitive_data | PII, PHI, payment card data, credentials and secrets, with checksum validation; can redact or replace values with reversible tokens | enforce, redact |
injection | Weighted signals over prompts and untrusted content, including hidden text and encoded payloads | enforce, block |
runaway_agent | Repeated identical calls, call cycles, request, cost and duration limits per agent session | enforce, block |
tool_governance | Destructive shell and SQL, infrastructure teardown, privilege changes, payments, external email, exfiltration; holds the call for approval | enforce, require approval |
tool_chain_anomaly | Unusual tool sequences against a learned baseline | shadow |
harmful_content, code_security, groundedness, verbatim_reproduction | Output safety | shadow |
injection_judge | Asks one of your own self-hosted models as a judge | off until configured |
An organisation that configured nothing gets runaway_agent, injection and sensitive_data in shadow mode: findings are recorded and nothing changes. Each detector runs in enforce, shadow or off, with actions flag, redact, tokenize, require_approval or block, per scope (gateway, project, route or model, stream topic).
Tokenised values are sealed under a key generated for the request, never written anywhere, and restored in the answer for the caller who sent them. A tokenised request is never cached.
These detectors are heuristics. groundedness is lexical: it finds claims the supplied context does not contain; it does not prove them. Run new detectors in shadow mode and review findings before you enforce them.
Route (under /api/v1/models/governance) | Permission |
|---|---|
GET / | models:view |
PUT /settings, PUT /policies, DELETE /policies/{scope_kind}/{detector} | models:admin |
PUT /tiers, DELETE /tiers/{target_kind}/{target_key} | models:admin |
GET /findings, GET /risk | models:view |
GET /findings/{id}/excerpt | models:admin (audited) |
POST /test | Runs the detectors on a sample; nothing is stored |
Refusal codes: governance_blocked, approval_required (with X-Keystone-Approval), step_up_required, risk_rate_reduced, purpose_not_permitted, agent_circuit_open and governance_unavailable. Tool calls held for approval go to the approvals inbox; see Approvals.
A model or route can be given a risk tier: minimal, limited, high (served only once approved) or unacceptable (never served). Findings feed compliance evidence; see Evidence.