Guardrails guide
How to configure LLM and MCP request/response guardrails.
Overview
Guardrails inspect traffic before it leaves Cortega or before it is returned to the caller.
Use guardrails to:
- mask PII or secrets
- block unsafe prompts or responses
- call moderation or safety providers
- enforce different policies for models, keys, MCP servers, or MCP tools
Guardrail types
| UI type | What it does |
|---|---|
| Regex | Built-in detectors (SSN, credit card, phone, email, CA SIN) plus your own patterns; masks or blocks on a match. No external call. |
| Presidio | Microsoft Presidio entity detection and anonymization, run by Cortega's Presidio service. Supports custom recognizers and the Log persistence scope (see below). |
| Webhook | Calls your own HTTP endpoint implementing Cortega's guardrail webhook contract. |
| OpenAI moderation | Calls the OpenAI moderations API. |
| Local model | Calls a locally hosted classifier model; no upstream credential. |
| Azure AI Content Safety | Calls Azure AI Content Safety. |
| Google Model Armor | Calls Google Model Armor. |
| Bedrock Guardrails | Applies an AWS Bedrock guardrail. |
| State setter | Records a value out of the message (regex or JSONPath) into a named state variable. Never blocks; runs before every other guardrail on the message. Variables are local to the request. |
| State checker | Compares a variable a State setter recorded and rejects, flags, or allow-lists on the result. |
| Social engineering | social_engineering |
| Policy document | policy_document |
| Conversation risk | conversation_risk |
| AI Verifier (Legal) | Looks up legal citations in Cortega's CourtListener index and, when enabled, runs a relevancy check that the opinion supports the claim and answers the question. On a fabricated, mismatched, or off-point citation it runs the same check-and-correct loop as Cortega Policy Guardrails: the original model is asked to drop the bad cite and not add a new one. Lookup errors fail open. After max attempts the default is to return the last correction. Response phase only. |
| AI Verifier (Documents) | Checks an answer against the documents attached to the request (PDF with a text layer, DOCX, PPTX, text). Built for contract review: each statement about a field (limitation of liability, termination for convenience, warranty period, IP ownership, and others) is matched to the contract. A value or quote that is not in the document, a value taken from another attached document, a value that comes from the wrong kind of clause (an insurance limit reported as a liability cap), and a "no clause" answer for a clause that exists are all flagged. An LLM judge decides the rest from the matching passages only, and must quote the document. Audit mode (default) returns the answer and records verdicts. Enforce mode blocks an answer with failed statements; it needs a chat completions response. Response phase only; the gateway turns streaming off only for requests that carry attached documents. |
| Tool-call review | Checks the tool calls an agent proposes against an allowlist you write as JSON: which tools, and which values each argument may take (a folder a path must stay inside, hosts a URL may name, addresses mail may go to). It uses no model, so the same call always gets the same answer. On a violation it rejects the response or, in flag mode, only logs it. It runs on the LLM response and on MCP tool calls. Response phase only for LLM traffic; streaming is never allowed. |
Any guardrail — not just a State checker — can carry a run/skip condition on a state variable, so you can make a detector conditional (e.g. run Presidio only when a State setter flagged the record). State setter / State checker forms offer variable templates that fill in the JSONPath. Async execution and streaming enforcement are not available for these two types.
Conversation risk
Conversation risk reads the whole conversation, in order and with who said what, and rates it low, elevated or high. It is made for attacks that look harmless one message at a time: an opening question, then a claimed purpose, then a request that builds on the last answer, or the same request reworded after a refusal.
It rates the conversation against five categories:
- Credentials: getting, stealing, replaying or misusing logins, keys, tokens, sessions or one-time codes.
- Intrusion: breaking into systems, staying hidden on them, switching off security tools, damaging data, malware or ransomware.
- Rules: getting the assistant to drop its rules, play an unrestricted persona, obey a fake system message, or remember a fake standing exception.
- Authority claim: a claimed job, test, training exercise or approval used to unlock any of the above. A claim that cannot be checked inside the conversation never unlocks anything.
- Out of scope: a request outside what the assistant is for. Only used when you fill in the scope text.
A rating of high means the conversation is clearly pursuing one of these and the latest request asks for working, step-by-step help. Elevated means it is moving that way, or it is unclear. Low is ordinary work, general explanations, and defence or policy questions. The rating depends on what is asked, so explaining a concept for defence is low and asking for working commands to do the harm is not.
It runs on the request, before the model sees it. It does not read the system prompt, and it does not check the model's reply.
Settings
| Setting | Choices | Default |
|---|---|---|
| What this assistant is for | Plain text, up to 1,000 characters. Leave empty to rate only the categories. | Empty |
| When the rating is high | Reject the request, or flag only | Reject |
| When the rating is elevated | Allow, allow and flag, or reject | Allow |
| Threat levels to raise | 0 to 5. Added to the caller's application on an elevated or high rating. Needs a threat policy for the application. | 1 (0 in the strict template) |
| If the check cannot run | Let the request through, or block it | Let it through |
The guardrail is always in the request phase and always runs before the model call. A rejected request returns the guardrail's rejection response (HTTP 403 by default, with code governance_blocked).
Set it up
- Assign a fast judge. Conversation risk makes up to two judge calls per request: a first check on every request, and a second check when the first rated above low. On the System Agents and Judges screen, give the two judge sites Conversation risk guardrail: first pass and Conversation risk guardrail: recheck a profile whose team routes to a model that answers in about a second. The gateway ends a guardrail call after 10 seconds and answers 503, so a model that thinks before it answers can be too slow. Cortega also stops the second check after 8 seconds in total and keeps the first rating.
- Add the guardrail. On the Guardrails page choose Add a guardrail, then the Conversation risk template. Fill in what the assistant is for if you want out-of-scope requests rated, set the actions, and set the scope (virtual key, model and so on). A guardrail with no scope applies to nothing.
- Optional: escalate on repeated attempts. Add a threat policy for the application (Threat levels panel), make sure its callers send the
X-Cortega-Applicationheader, and set Threat levels to raise to 1 on the guardrail above. Then add a second guardrail from the Conversation risk, strict template with the same scope. It runs only while the application's threat level is above 0, and it rejects elevated ratings too. With both in place, an elevated rating raises the level, the strict copy then runs on that same request and rejects it, and later requests from the same application name get the strict check until the countdown ends. Without the header or a threat policy, the level is not raised and the strict copy never runs, so elevated conversations are allowed. - Test it. Send a conversation that plainly asks for help stealing a coworker's saved passwords. Expect a 403 with code
governance_blocked. Send an ordinary question and expect a normal answer. Check Runtime logs for the decision.
What you see in the logs
Each decision records the rating (risk), the category, a one-sentence reason, whether the second check ruled (rechecked) and what the first check said (screen_risk), and how long each check took (screen_ms, recheck_ms). When the threat level is raised the row also records the application, the level before and after, and the time left.
Speed and cost
With a fast judge the check added a median of 0.7 to 0.9 seconds per request in our October 2026 tests, and cost about three hundredths of a cent per check. A rating is kept for 2 minutes, so a second Conversation risk guardrail on the same conversation reuses it instead of calling the judge again.
Limits
- It rates the last 24 messages at most (the first user message is always kept), each cut to 2,000 characters, 14,000 characters in total.
- A request that asks directly for harmful help in its first message can be rated low. In our tests, 5 of the 7 attacks that still got through were first messages.
- The threat level belongs to an application name. The header is set by the caller and is not authenticated, and every caller that uses one name shares its level.
- If the judge cannot answer in time, If the check cannot run decides. The strict template blocks, the plain template lets the request through.
- It does not read your policy documents and it does not check tool calls. Use Policy document and Tool-call review for those.
Using several guardrails together
Each turn has two passes: one on the request before the model, one on the response after it. A request guardrail (Social engineering, Conversation risk) and a response guardrail (Policy document, AI Verifier, Tool-call review) never run in the same pass, so they do not interfere.
- Several request guardrails. They run one after another and only allow, flag or reject. None rewrites the request, so you can combine Social engineering and Conversation risk. Each judge call adds time to the request.
- Several response guardrails. They run in the order shown on the Guardrails page, and a guardrail that corrects the answer (Policy document, AI Verifier (Legal), masking) hands its corrected text to the next one. Each runs its own check-and-correct loop. The earlier guardrail does not check the answer again after a later one changes it, so one fix can undo another, and each loop adds model calls and waiting time. Use at most one correcting guardrail on the same traffic, put guardrails that only reject or flag after it, and bind two correcting guardrails to different keys or models if you need both.
Choosing a guardrail
| You want to | Use |
|---|---|
| Mask or block PII and secrets by pattern | Regex, Presidio |
| Catch manipulation of a staff-style assistant in one request (fake authority, urgency, a made-up approval) | Social engineering |
| Catch multi-turn attacks and requests for credentials, intrusion or dropping the rules, and tighten up after repeated attempts | Conversation risk, with Threat levels |
| Check every answer against approved company policy and have the model correct it | Policy document |
| Check legal citations in answers | AI Verifier (Legal) |
| Check contract answers against the attached documents | AI Verifier (Documents) |
| Limit which tools an agent may call and with which values | Tool-call review |
| Use your own detector | Webhook |
Conversation risk does not replace Social engineering or Policy document. Social engineering flags manipulation techniques even when the request is not about credentials or intrusion, for example urgent pressure to approve a payment. Conversation risk rates the goal of the whole conversation and also catches direct requests without any manipulation. Policy document works on the response against your own written rules. Conversation risk works on the request and has no access to your policy documents.
Threat levels
A threat level is a counter for one application, named by the
X-Cortega-Application header on its requests. Every request with the same
name shares it. A Tool-call review guardrail or a Conversation risk guardrail with
raise_threat set adds to the level when it catches a violation. The level drops back to 0 when its countdown
ends. Any guardrail can read it through the state gate on the variable
threat.level, so a stricter rule can switch on while the level is raised.
For example, one guardrail always runs and raises the level, and a second one
that allows fewer tools runs only while threat.level is greater than 0. The
stricter guardrail must not set raise_threat itself.
Only applications listed under Threat levels at the bottom of the Guardrails page take part. Each has a countdown, a maximum level, and a choice of whether each request restarts the countdown or only each violation does. The same panel shows the live level and time left, and Reset level sets it back to 0 at once. The header is set by the caller and is not authenticated, and all callers that use one name share its level, so give each agent deployment its own name. Threat levels apply to LLM traffic. MCP requests do not carry an application name.
Document findings
Verdicts from AI Verifier (Documents) guardrails are in the Document findings panel at the bottom of the Guardrails page. Each checked answer is a row with its team, model, documents, and how many statements failed. Open a row to see a grid of documents by field. Each cell shows the verdict for that field in that document. Click a cell for the statement, the reason, and the passage it was checked against, with its section and page. Documents sent more than once lists documents that were attached to more than one request, with the tokens those requests used.
A document sent by file id or URL, instead of in the request body, is listed as not checked, because its content never passes through the gateway. Add fields for your own contract terms in the guardrail's Custom fields. Merger agreement fields (closing conditions, material adverse effect definition, matching rights, no-shop, fiduciary out, termination fee) are built in.
With Check pasted text on (the default), contract text pasted into the
prompt is checked the same way when nothing is attached. Put the contract text
in a code block, backticks, or <document> tags so the verifier uses only that
text. Without delimiters, the whole message is used when it is at least 400
characters long, and the answer is checked in audit mode even when the
guardrail enforces: a pasted message without delimiters can mix instructions
with contract text, so it is recorded but never blocked. Shorter messages are
not checked and keep streaming.
Some models and providers drop file attachments, so the model never sees the document. Turn on Send attachments to the model as text to replace each attached file in the request with the text Cortega extracted from it. The model then reads the same text the verifier checks. Layout and images in the file are not sent.
Requests up to 32 MB pass through the gateway, attached documents included.
The model-backed types (OpenAI moderation, Azure AI Content Safety, Google Model Armor, Bedrock Guardrails) need a credential or an endpoint/region. See Provider credentials.
Create a guardrail
Navigate to Guardrails. Use Guided setup for a walkthrough that also proposes a starter set of guardrails, or New Guardrail to add one directly:
- Click New Guardrail.
- Enter a name.
- Choose phase: request or response.
- Choose the guardrail type (see the table above).
- Choose execution type.
- Configure type-specific settings, and a credential for the model-backed types.
- Choose scope.
- Save as draft or activate it.
Draft guardrails do not enforce. Active guardrails enforce after the gateway receives updated config.
Provider credentials
The model-backed guardrail types call an external service. The Guided setup wizard has a credentials step that collects what each type you keep needs:
- OpenAI moderation: choose Reuse OpenAI provider (Cortega uses the stored, encrypted credential from an existing Models → Providers OpenAI entry) or Use environment variable (Cortega reads a named env var, pre-filled with the conventional name). Reuse avoids keeping a second copy of the key.
- Azure AI Content Safety: the resource endpoint.
- Bedrock Guardrails: the guardrail ID, region, and version (
DRAFTor a published number). Cortega signs with its ambient AWS credentials. - Google Model Armor: the project and location.
Cortega never returns a stored credential in an API response. A guardrail whose credential can't be resolved does not enforce.
Execution types
Guardrails run in this order:
1. Sanitize
2. Parallel checks start
3. Ordered checks run while parallel checks are still running
4. Cortega allows only after ordered checks pass and the parallel expression passes
| UI term | Stored type | Use when |
|---|---|---|
| Sanitize | sanitize | The guardrail may mask or transform content |
| Parallel checks | async | The guardrail only passes or blocks and can run concurrently |
| Ordered checks | sync | The guardrail must run in a specific order after sanitation |
Sanitize and ordered checks run in the order shown in their tables. Parallel checks run concurrently.
Async pass logic
Parallel checks use AND and OR join behavior.
ANDchecks must all pass unless anORcheck passes.- An
ORcheck can allow the parallel expression when it passes. - If the expression is conclusively blocked, Cortega blocks the request/response.
Use AND for mandatory checks. Use OR for alternative safety checks
where any passing check is acceptable.
Scopes
Choose the narrowest scope that matches your intent.
| Scope | Applies to |
|---|---|
| All Models | LLM traffic for all models |
| Model | A specific direct LLM model (provider:model_id in the dropdown) or a managed team router (cortega-team-…) |
| Key | A specific LLM key identity |
| Log persistence | Presidio only. Redacts PHI in stored gateway telemetry (LLM/MCP query fields and Guardrail Log content attributes) before ClickHouse |
| MCP Server | Eligible MCP traffic for a server |
| Tool | Eligible MCP traffic for a tool |
Model scope matches the model name Cortega stamps after any team-router rewrite. For a team with Model Authorization configured, choose that team's router name in the Model dropdown so the guardrail covers the team's traffic. Direct models still cover unrouted calls that name that model. Hand-authored virtual models are not listed as Model choices; use All Models or Key for that traffic.
Request/response Model (or All Models) scope controls what the provider sees. Log persistence is a separate binding: without it, Guardrail Log and traffic rows can still store the pre-mask text even when the wire mask worked. Add Log persistence on a Presidio guardrail when observability must not retain cleartext PHI.
MCP guardrail coverage
MCP guardrails scan only methods that carry useful request/response content:
tools/callresources/readprompts/listprompts/getroots/listresources/templates/list
Other MCP protocol calls, such as initialize and notifications, are
logged in MCP Queries but are not content-scanned by guardrails.
Presidio custom patterns
When you create or edit a Presidio guardrail, use Custom detection patterns to teach Presidio identifiers its built-in detectors miss, for example an internal patient number or a local licence format.
Each pattern needs:
| Field | Purpose |
|---|---|
| Name | Recognizer name stored on the guardrail |
| Entity type | Entity the match is reported as (for example LOCATION or PERSON) |
| Regex | Detection pattern |
| Score | Confidence for the match (typically 0.85) |
| Context words | Optional nearby words that raise confidence |
Saving the guardrail sends these as presidio_ad_hoc_recognizers.
Clearing every custom pattern removes them from the guardrail.
When LOCATION is enabled, Cortega also applies built-in defaults for
US street lines and state/ZIP fragments. Those defaults do not appear in
the form; add custom patterns only for formats they do not cover. When
MEDICAL_LICENSE is enabled, a US state-prefixed medical licence default
is applied the same way.
Runtime logs
Use Observability → Guardrail Log to see what guardrails did.
Important fields:
| Field | Meaning |
|---|---|
| Runtime scope | LLM Request, LLM Response, MCP Request, or MCP Response |
| Input text | Text inspected on request paths |
| Output text | Text inspected on response paths |
| Guardrail | Guardrail name |
| Type | Guardrail provider/type |
| Score / threshold | Provider score compared to configured threshold |
| Action | Pass, mask, block, or below threshold |
| Latency | Time spent in that guardrail evaluation |
LLM Queries and MCP Queries show applied guardrails in their security area when the traffic row can be correlated with guardrail execution rows.
Troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
| Guardrail does not fire | It is draft, disabled, wrong phase, or wrong scope | Check status, phase, and scope |
| Model-scoped guardrail misses a routed team | Scope is bound to a direct model, but the team router rewrote the request | Bind Model scope to that team's cortega-team-… router (or use All Models / Key) |
| Model scope does not apply to a hand-authored virtual model | Hand-authored virtual models are not Model-scope choices | Use All Models or Key |
| MCP request is not scanned | MCP method is outside the eligible method list | Check method in MCP Queries |
| MCP Guardrail Log row exists but MCP Query security is empty | MCP request correlation is not available yet | Use Guardrail Log directly |
| Output text appears without input text | Response guardrail row | Runtime scope should say LLM Response or MCP Response |
| Input text appears without output text | Request guardrail row | Runtime scope should say LLM Request or MCP Request |
Related guides
The AI Verifier (Legal) and AI Verifier (Documents) names above are existing guardrail labels. The standalone AI Verifier desktop product has been replaced by AI Booster; this product rename does not rename guardrail types or API paths.