Quickstart — arm one guard
Arm one PII guard in monitor mode end to end — the config, the boot, a request with an email in it, and the row that comes back.
On this page
Ten minutes, one binding, no byte changed. At the end you will have a decision row carrying a
guardrails block, and you will know what to read on it before you consider enforce.
In this tutorial we’ll arm the guard on a gateway you run yourself, in a configuration file. A managed gateway takes its bindings from the guardrail profile its applied release pins instead; which guards act on which request is the difference between the two.
1. Add a guardrails: block
Guardrails are off by default — an absent block is zero behavior change. This one arms two
detectors on the input phase, in monitor mode.
# tokentriage.yaml
version: 1
server:
listen: "127.0.0.1:8787"
admin: "127.0.0.1:9090"
mode: ledger
upstreams:
anthropic:
base_url: https://api.anthropic.com
dialect: anthropic
api_key_env: ANTHROPIC_API_KEY
ledger:
decision_log:
path: ./decisions.jsonl
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]
mode: monitor # enforce | monitor — `simulate` is not a mode
action: redact # one-rung sugar for `ladder: {low: redact}`
rule_ref: POL-PII-1 # your auditable policy pointer
profiles:
standard: [pii_in]
2. Read the binding before you boot it
guardrails explain resolves one binding against the compiled engine — the action: sugar
expanded, phases checked against each detector’s own declaration — so what it prints is what would
actually run.
$ tokentriage guardrails explain --binding pii_in --config tokentriage.yaml
BINDING pii_in
category: pii
phases: input
mode: monitor (a CONFIGURED mode; `simulate` is refused as one and is the simulate API's stamp)
on_error: open (a detector that cannot run degrades to a disclosed suspension and traffic flows)
rule_ref: POL-PII-1
GATES — what a finding must clear before the ladder is consulted
threshold: 0
bands: none configured — every finding lands at severity `low`
LADDER — the verb each severity resolves to (fall-down included)
SEVERITY ACTION REVERSIBLE THIS BUILD CAN DO IT
low redact no yes
…
It goes on to print each armed detector’s covers and its NOT covered in full. Read the
second one for anything you are about to arm — it is long on purpose.
3. Boot
$ tokentriage run -c tokentriage.yaml
{"level":"INFO","msg":"guardrails armed","bindings":1,"default_profile":"standard","detectors":2,"policy_sha":"c935358"}
4. Send a request with an email in it
Use obviously synthetic values. 4111 1111 1111 1111 is the standard test PAN, issued to nobody,
and example.com is reserved for documentation.
$ curl -s http://127.0.0.1:8787/v1/messages \
-H 'content-type: application/json' \
-d '{"model":"claude-haiku-4-5","max_tokens":64,"messages":[
{"role":"user","content":"Charge card 4111 1111 1111 1111 for [email protected]"}]}'
The response is unchanged — you are in monitor.
5. Read the row
After the request completes and its ledger append is visible, select the latest primary guardrail
row. Primary rows have no kind; guardrail and other companion rows do. This single-request demo
leaves those rows intact. For concurrent traffic, match the response’s x-tt-request-id instead of
selecting the latest row; the Python/TypeScript recipe
shows that lookup. A missing row means it is not available yet, not that inspection was clean.
$ jq -s 'map(select(.kind == null and .disclosures.guardrails != null)) | last | .disclosures.guardrails' decisions.jsonl
{
"policy_ref": "standard@cfgc935358",
"engaged": ["pii_in"],
"fired": [
{
"binding": "pii_in",
"category": "pii",
"phase": "input",
"detector": "pii.card",
"detector_version": "pii.card-v1",
"action": "redact",
"effect": "annotated",
"entities": [{ "type": "CREDIT_CARD", "count": 1 }],
"reversible": false,
"confidence": { "basis": "exact", "point": 1 },
"rule_ref": "POL-PII-1"
},
{
"binding": "pii_in",
"category": "pii",
"phase": "input",
"detector": "pii.email",
"detector_version": "pii.email-v1",
"action": "redact",
"effect": "annotated",
"entities": [{ "type": "EMAIL", "count": 1 }],
"reversible": false,
"confidence": { "basis": "reported", "point": 0.85 },
"rule_ref": "POL-PII-1"
}
],
"input_ms": 0,
"output_ms": 0
}
Four things worth noticing, because each of them is load-bearing:
effect: annotated, notmutated.monitormode changed nothing. Theactionstill readsredactbecause that is the verb the ladder resolved to.reversible: falseon both. That bit is derived from the action table, never authored —redactis irreversible whatever the effect says.- Two different bases.
pii.cardsaysexactbecause a Luhn checksum and an issuer range passed.pii.emailsaysreported— a raw, explicitly unlicensed score. Neither carries an interval, because no calibration artifact licenses one. - Findings omit matched text. Safe entity counts and execution metadata describe the guard. Separate content capture and other sinks retain their own access and retention policies.
6. Now measure, before you enforce
$ curl -s http://127.0.0.1:9090/tt/api/v1/guardrails/evidence | jq
Or open the guardrail panel in the dashboard’s Honesty center. Either way, the question you are answering is “what did this guard fire on that I did not want it to?” — Evidence & alerting is how.