Protect model inputs and outputs¶
Purpose¶
Use organization model guardrails to block, redact or detect selected text before it reaches a model provider or before a model response reaches its caller. The policy applies to Chat Completions, Responses and managed Agent model calls. Input policy also applies to embedding inputs and reranking queries/documents. Existing MCP tool rules remain separate.
Define reusable guardrailPolicies and assign them to the Organization baseline or exact virtual-model names. Model assignments add checks; they cannot remove Organization requirements. Provider profiles control protocol compatibility and remain separate from guardrail policies.
Prerequisites¶
- An organization owner or administrator with
organization.settings.manage. - An existing virtual model, enabled in the caller's Team, and a caller with
model.generate. - The organization YAML in the deployment's configuration repository.
- For moderation or prompt-injection classification, a reachable classifier implementing the contract below and an environment-referenced credential available only to the gateway.
Deterministic PII and secret matching recognizes documented patterns; it does not establish that arbitrary text is free of sensitive content. The policy inspects text and JSON string values, not image pixels, attachments or hidden model reasoning. Prompt-injection and moderation checks depend on the configured classifier's verdicts.
Procedure¶
- Add
modelGuardrailsto the organization YAML. Start with a small explicit policy:
modelGuardrails:
input:
- id: model-secrets
action: block
match:
secrets: true
- id: model-email
action: redact
match:
pii: [email]
output:
- id: model-cards
action: redact
match:
pii: [credit-card]
maxBufferedOutputBytes: 1048576
- Have the operator run preflight and restart gateway and Console with the reviewed files. New invocations use the loaded policy; no new API key is required. Inspect the current redacted policy under Configuration.
- Make a synthetic model call. From any client working directory, use a configured virtual model and a personal or service virtual key:
export MISSION_CONTROL_URL="https://gateway.example.test"
export MISSION_CONTROL_API_KEY="mck_example_not_a_real_key"
curl --fail-with-body "$MISSION_CONTROL_URL/v1/chat/completions" \
--header "Authorization: Bearer $MISSION_CONTROL_API_KEY" \
--header "Content-Type: application/json" \
--data '{"model":"support","messages":[{"role":"user","content":"Draft a reply to person@example.test."}],"max_completion_tokens":128}'
With the example policy, the provider receives a redacted email. The caller receives the model's response after any applicable output checks.
- Open Observe → Requests and inspect the request. The recorded rule identifies the first blocking rule, otherwise the first redacting rule, otherwise the first detecting rule. Matched text is not stored in rule evidence.
Create and assign reusable policies¶
- Declare the virtual models in the model catalog first. For this example, assume
supportandsupport-sensitiveexist. They may use the same upstream model. Add these definitions to the Organization YAML:
guardrailPolicies:
baseline:
description: Keep recognized credentials out of model input.
input:
- id: baseline-secrets
action: block
match: { secrets: true }
sensitive-data:
description: Redact selected personal information.
input:
- id: sensitive-contact
action: redact
match: { pii: [email, phone] }
output:
- id: sensitive-cards
action: redact
match: { pii: [credit-card] }
modelGuardrails:
policies: [baseline]
virtualModels:
support-sensitive: [sensitive-data]
maxBufferedOutputBytes: 1048576
- Keep mandatory named policies in
modelGuardrails.policiesand add exact model assignments inmodelGuardrails.virtualModels. Thesupportmodel receivesbaseline;support-sensitivereceives both policies. Definitions without an assignment are inactive. Assignment does not enable a model or grant caller access: configure Team allowance and key policy separately, and restrict sensitive-data applications to the intended models. - Have the operator run preflight from
platform/with the service's explicit configuration and environment, then coordinate the gateway/Console restart:
No new key or session is required. There is no Console policy editor or Apply action. New invocations use the restarted process's configuration; calls already admitted retain their policy across fallback and tool-loop rounds.
4. Open each model under Models and inspect Guardrail policies. It shows the baseline/model sources, rule categories and streaming behavior loaded by the Console. The loaded hash alone does not confirm that every gateway process restarted. Full definitions remain available only with organization.settings.manage in Configuration.
5. Send a synthetic secret to both models and confirm both block without provider dispatch. Send person@example.test to both and confirm only the stricter model redacts it. Inspect Observe → Requests → Exact policy evidence for the saved assignments and matched rule source. These assignments are recorded even when standard OTLP is disabled.
Composition and migration¶
Inline modelGuardrails.input and output remain mandatory baseline rules. Effective order is inline rules, Organization policy references, then model references. A policy referenced at both scopes runs once and records both sources. Empty model assignments do not remove the baseline. Caller fields and headers cannot select, replace or disable policies.
All deterministic rules match the original text for their stage before redaction, so an additional redaction cannot hide a deterministic baseline block. The combined deterministic redactions run before the shared classifier receives text. Classification therefore sees sanitized content; changing redaction can change a semantic classifier's verdict.
To migrate existing inline rules, move them into a named definition, preserve their IDs, remove the inline copies, and reference the new policy in modelGuardrails.policies in the same edit. Keeping both copies fails duplicate-ID validation. Existing files without the new fields continue to work. To roll back to binaries without named-policy support, restore compatible inline YAML before restarting those binaries; they reject the new fields.
Each policy contains rules and an optional description. Classifier credentials, endpoint, timeout and buffer size stay in the shared modelGuardrails block; policies cannot override them. All definitions validate, including inactive ones. Named classifier rules require the external classifier configuration. Descriptions are administrative text, not natural-language detector instructions. Team/key targeting, policy inheritance and per-request opt-in are not provided.
Inspect saved policy evidence¶
Request details show Effective guardrail policies with the sources recorded at invocation time. An input block means output checks did not run; configured assignments are not proof that every rule executed. Historical requests without this evidence show Guardrail policy not recorded. Requests recorded with no rules show that explicitly. Restarting or editing YAML does not rewrite historical evidence. Reading Requests uses its existing Team or own-key authorization; captured content and export still need their separate permissions.
Rules¶
Each rule needs a unique id, an action and exactly one matcher. Rule IDs must also be distinct from MCP tool-rule IDs.
IDs must be unique across all named definitions and inline/MCP rules in the Organization file. At most 100 policies and 100 references per assignment are accepted; each effective model policy is limited to 100 input and 100 output rules after deduplication. Policies share the existing evaluation work limits rather than obtaining a new budget for each bundle. See the configuration fields.
| Matcher | Example | Meaning |
|---|---|---|
regex |
regex: "confidential-team" |
Custom RE2 expression; unsupported expressions are rejected during validation. |
maxLength |
maxLength: 2000 |
Inspect an individual text value against the configured length. |
secrets |
secrets: true |
Recognized AWS access keys, GitHub/Slack/Google/Stripe keys, PEM private-key markers, Bearer tokens and secret assignments. |
pii |
pii: [email, phone, ip, credit-card] |
Email, phone-shaped numbers, IPv4 addresses and card-shaped numbers; card recognition includes a Luhn check. |
classifier |
classifier: prompt-injection |
A configured external classifier check; prompt injection applies to input only. |
classifier |
classifier: moderation |
A configured external moderation check on input or output. |
block stops release of the affected content. redact replaces matched text. detect records a match while leaving text unchanged. Classifier rules support only block and detect; they cannot redact. Tool names, schemas, call IDs, model routing and authority are never rewritten. Redaction of tool-call arguments preserves valid JSON; an unsafe rewrite fails closed.
regex, secrets and pii matching runs in RE2, never in JavaScript's backtracking engine, so matching time grows linearly with the text. Each stage check (one input or one output evaluation) is bounded: at most 100,000 searches and 8,388,608 characters read plus 4 for every inspected character. A check reaches a limit only with more than about a thousand matches of one expression in the same text or 100,000 searches, for example 100,000 text values checked by one rule. Then the rule being evaluated and every later regex, secrets or pii rule in that check count as matching: block returns guardrail_input_blocked or guardrail_output_blocked, redact replaces the rest of the unsearched text with [redacted:<rule id>], and detect records the rule.
Each search reads at most 1,310,720 bytes of UTF-8, so the default 1 MiB output buffer is always searched in one window. Longer text is searched in windows that overlap by half. A custom regex that can match text longer than that overlap, meaning any *, + or {n,}, counts as matching such text unless a window finds a match. secrets fails closed only for a private-key block or a whitespace-padded assignment longer than a whole window, and pii email and phone matching trusts the overlap. See Regular expressions for RE2 syntax and window behavior on very long text.
External classifier¶
Declare the classifier alongside rules that use it:
modelGuardrails:
classifier:
url: https://classifier.example.test/check
credential: env/MODEL_CLASSIFIER_TOKEN
timeoutMs: 5000
input:
- id: model-injection
action: block
match:
classifier: prompt-injection
output:
- id: model-moderation
action: block
match:
classifier: moderation
Inject MODEL_CLASSIFIER_TOKEN into the gateway environment. A new or changed environment credential requires a gateway restart followed by operator preflight and a coordinated gateway/Console restart. Never place the token in YAML or a client request.
The gateway sends an authenticated JSON POST using Authorization: Bearer …. Deterministic redaction runs before classifier submission. An example request is:
{
"version": 1,
"checks": ["prompt-injection"],
"items": [
{ "id": "0", "stage": "input", "role": "user", "text": "Sanitized text" }
]
}
The classifier must return exactly one boolean verdict for every requested item/check pair:
Unknown/duplicate identifiers, missing verdicts, unexpected fields, malformed JSON, redirects, timeouts and oversized responses make the classifier unavailable for that evaluation. A stage whose classifier rules include action: block then fails closed with guardrail_unavailable. A stage whose classifier rules only detect or redact passes without the classifier: its deterministic rules still apply, no classifier match is recorded, the gateway logs guardrail_classifier_unavailable and counts the evaluation in mission_control_guardrail_classifier_unavailable_total{stage, outcome} as degraded (refused for a blocking stage). The classifier request limit is 8 MiB and the response limit is 256 KiB. Classifier response bodies and credentials do not appear in public errors. A classifier outage is not permission to bypass a blocking policy.
Expected result¶
Input blocks contact no model provider. Output blocks happen after provider work: the caller receives a fixed error, blocked output is excluded from capture, and reported provider tokens and estimated cost remain billable and still count toward limits. A local guardrail failure never triggers provider fallback.
Every governed-tool loop round is checked before tool execution; newly appended tool results are checked before the next model call. MCP tool rules and durable approvals still apply independently.
When output rules exist, streaming is buffered until a valid provider completion has been inspected. The public SSE format remains supported, but time to first output includes the complete generation and guardrail check. Only sanitized events are released. Input-only policy preserves live streaming. maxBufferedOutputBytes defaults to 1 MiB and may not exceed 8 MiB. At most 10,000 canonical events are buffered. Truncated streams and buffer/event overflow are failures, not successful partial answers. When a policy inspects structured tool arguments, traversal is bounded to 64 levels and 10,000 JSON nodes; excessive structure fails with guardrail_unavailable. These traversal limits do not apply when that policy stage is absent.
Verification¶
- Confirm the loaded configuration contains the intended rule IDs.
- Send a safe synthetic match and inspect Observe → Requests and the authorized Trace.
- Confirm input blocks have no provider dispatch and output blocks retain reported usage.
- Test streaming as well as JSON calls when output policy is configured.
- In a test environment, stop the classifier and confirm that a stage with a blocking classifier rule returns
guardrail_unavailablewithout fallback, while a detect-only stage passes andmission_control_guardrail_classifier_unavailable_total{outcome="degraded"}grows.
Failure recovery¶
| Result | Recovery |
|---|---|
guardrail_input_blocked (400) |
Remove the prohibited input or have an administrator review the rule. Repeating the same call does not bypass it. |
guardrail_output_blocked (400) |
Review the decision and policy. Provider work may have been billed; retrying can incur another charge. |
guardrail_unavailable (503) |
Restore classifier/configuration availability and verify its exact response contract. |
guardrail_output_too_large (503) |
Request a smaller output or review the finite buffer limit. |
guardrail_redaction_failed (503) |
Inspect the rule and structured arguments; correct the input or policy so valid JSON can be preserved. |
| Configuration rejects a regex | Use an expression supported by RE2 and small enough for its fixed memory; backreferences and look-around are not supported, and \p{L}{100} is too large. See the rewrites. |
| Unknown policy/model or duplicate rule/reference | Correct the exact policy/virtual-model name, declare it first, or remove the duplicate. Re-run preflight; the invalid file cannot activate. |
| Effective policy exceeds limits | Reduce the combined baseline/model rule count. Splitting rules into more policies does not increase the per-stage limit. |
guardrail_policy_no_applicable_rules warning |
An output-only policy assigned to an embedding/reranking model has no applicable checks. Add input rules or remove that assignment. |
| Unexpected block or full redaction on very large or match-dense text | The check reached its regex limits, or an unbounded custom regex met text longer than one window, and failed closed. Reduce the text, narrow the rule, or bound its repetitions, for example \s{1,100} instead of \s+. |