Configuring a guard
The binding schema in full — id, category, phases, detectors, mode, thresholds, severity bands, and error handling.
On this page
A binding is one guard: a category, some detectors, the phases to run them on, and what to do when a finding clears the gate.
guardrails:
enabled: true
default_profile: standard # applied to anonymous/keyless traffic; "" ⇒ off
local_budget_ms: 50 # cumulative request budget for embedded detector work
guards:
- id: pii_in
category: pii
phases: [input]
detectors: [pii.card, pii.email, pii.ssn]
mode: monitor # enforce | monitor
threshold: 0.5 # minimum gate number a finding must clear
severity_bands:
- { min: 0.5, severity: low }
- { min: 0.9, severity: high }
ladder:
low: observe
high: redact
on_error: open # open | closed
rule_ref: POL-PII-1
profiles:
standard: [pii_in]
The fields
| Field | What it does |
|---|---|
id |
the binding’s name. It is what engaged[], fired[] and suspended[] carry, and what you alert on |
category |
one of pii secrets injection toxicity topical code multimodal format custom. Every detector must belong to it |
phases |
input · output · stream. Refused at boot if a named detector does not declare the phase |
detectors |
the detector ids to arm. An unregistered id fails the compile |
mode |
enforce or monitor. monitor runs and discloses and changes no byte |
action |
one-rung sugar for ladder: {low: <verb>} |
ladder |
the real severity → verb table. Use it when you want different verbs at different scores |
threshold |
the minimum score a finding must clear before the ladder is consulted at all |
severity_bands |
the score cut-points that decide which rung a finding lands on |
on_error |
open (allow and disclose — the default) or closed (refuse unreleased traffic in enforcing mode); monitor preserves traffic |
rule_ref |
your auditable policy pointer. It rides every fire on the row |
local_budget_ms |
engine-level: the cumulative request budget for embedded detector work |
stream_fallback |
monitor (default) or acknowledged closed for missing stream coverage; buffer is recognized syntax but refused by stock gateway activation |
profiles / default_profile |
named sets of binding ids; the default applies to anonymous traffic. "" means off |
action or ladder, not both jobs
action: redact is exactly ladder: {low: redact}. With no severity_bands configured, every
finding lands at low — which is why the sugar works and why a one-rung binding needs neither
bands nor a threshold. Reach for ladder when you want a real table:
severity_bands:
- { min: 0.5, severity: low }
- { min: 0.8, severity: high }
ladder:
low: observe # note it and move on
high: redact # splice it out
mode: simulate is not a thing
mode: simulate # ✗ refused by validation
binding "pii_in": mode 'simulate' is the off-path simulate API's stamp, never a configured mode
Simulation is a property of a dry-run report, not of a policy. The dry run lives at
tokentriage guardrails test and POST /tt/api/v1/guardrails/simulate; see
Proving it before you arm it.
The two settings that make you type out a consequence
on_error: closed requires fail_closed_ack
on_error decides what happens when inspection cannot complete. Under enforce, open allows
traffic with a failure receipt; acknowledged closed refuses input or suppresses unreleased output.
Monitor mode preserves traffic and discloses the failure under either posture. See the
failure matrix.
on_error: closed
fail_closed_ack: "outage-over-leak, signed off by platform-security 2026-08-07"
Without the ack:
binding "pii_in": on_error 'closed' requires fail_closed_ack — a dead detector becoming an outage is a decision an operator must type out
An external tier requires external: allow
An external judge requires destination consent before it can send request text. Consent alone does not enable every registered detector: the stock gateway rejects sidecar bindings until their pinned runtime is qualified, rejects external detectors that require raw content, and rejects spanless external mutation. This example permits no judge spending and therefore makes no judge calls:
guardrails:
judge:
external: allow # the air-gap gate
api_key_env: ANTHROPIC_API_KEY
model: claude-haiku-4-5
timeout_ms: 2000
max_usd_per_request: 0 # explicit zero: deny before any judge call
Budgets, and what overrunning one costs
local_budget_ms bounds cumulative local inspection work for one request across input,
secondary preparation, output and stream phases; provider wall-wait time is excluded.
Exhaustion discloses budget_exceeded and applies the effective failure posture. An
acknowledged enforcing closed binding refuses release; monitor and open bindings retain
their documented behavior.
Measure the selected bindings on representative traffic before enforcing them. The sample value of 50 ms is a configuration example, not a latency or coverage guarantee.
Profiles
A profile is a named set of binding ids. default_profile is what anonymous or keyless traffic
gets; setting it to "" means such traffic is unguarded.
profiles:
standard: [pii_in]
full: [pii_in, pii_out, sec_in, inj_in]
default_profile: standard
The resolved profile and the config hash ride every row as policy_ref — standard@cfgc935358 —
so a row can always be traced back to the policy that produced it.