# 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.

Source: https://tokentriage.com/docs/guardrails/quickstart

---
import Callout from '../../../components/Callout.astro';
import NextSteps from '../../../components/NextSteps.astro';

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](/docs/guardrails/overview#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.

```yaml
# 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]
```

<Callout type="note">
`action: redact` in `monitor` mode changes nothing. The verb is still recorded, and the row's
`effect` will read `annotated` — see [Verbs & effects](/docs/guardrails/verbs-and-effects) for why
those are two different facts.
</Callout>

## 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.

```console
$ 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

```console
$ tokentriage run -c tokentriage.yaml
{"level":"INFO","msg":"guardrails armed","bindings":1,"default_profile":"standard","detectors":2,"policy_sha":"c935358"}
```

<Callout type="warning">
**Boot is where a bad binding fails.** A binding naming a phase a detector does not declare, an
`on_error: closed` with no `fail_closed_ack`, or a judge tier with no consent string all refuse
here — the process does not start. That is the correct failure: no row is ever written carrying a
fallback disclosure for a policy that should not exist. See
[boot-time refusals](/docs/reference/errors#guardrail-boot-refusals).
</Callout>

## 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.

```console
$ 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 docs-demo@example.com"}]}'
```

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](/docs/recipes/redact-pii-before-it-reaches-the-model)
shows that lookup. A missing row means it is not available yet, not that inspection was clean.

```console
$ jq -s 'map(select(.kind == null and .disclosures.guardrails != null)) | last | .disclosures.guardrails' decisions.jsonl
```

```json
{
  "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`, not `mutated`.** `monitor` mode changed nothing. The `action` still reads
  `redact` because that is the verb the ladder resolved to.
- **`reversible: false` on both.** That bit is *derived* from the action table, never authored —
  `redact` is irreversible whatever the effect says.
- **Two different bases.** `pii.card` says `exact` because a Luhn checksum and an issuer range
  passed. `pii.email` says `reported` — 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

<Callout type="key">
**A fire count is not a precision figure.** A binding with 400 fires and zero adjudicated outcomes
has produced no evidence at all about whether those fires were correct — a false-positive storm and
a working guard look identical from here. Before you set `mode: enforce`, go and read the
evidence surface and label some findings.
</Callout>

```console
$ 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](/docs/guardrails/evidence-and-alerting) is how.

<NextSteps items={[
  { title: "Configuring a guard", href: "/docs/guardrails/configuring-a-guard", description: "The whole binding schema: ladders, severity bands, thresholds, on_error and profiles." },
  { title: "Evidence & alerting", href: "/docs/guardrails/evidence-and-alerting", description: "Reading a window of rows, and the one field to page on." },
  { title: "Redact PII before it reaches the model", href: "/docs/recipes/redact-pii-before-it-reaches-the-model", description: "The same guard taken all the way to enforce, with the check in between." },
  { title: "Detector reference", href: "/docs/reference/guardrail-detectors", description: "Explore the other detector categories available for your next guard binding." }
]} />
