---
title: "Configure guardrails"
description: "Look up settings that control the guardrail checks."
canonical_url: "https://docs.oz0.ai/build/plugins/verified/guardrails/settings"
---
# Configure guardrails

> Look up settings that control the guardrail checks.

For: tenant administrators and plugin users.

Look up settings that control the guardrail checks.

## Settings

Everything is a tenant [setting](https://docs.oz0.ai/admin/secrets/settings#settings-are-not-secrets) of the plugin. Without any, every pack is on,
personal data and secrets are redacted, injections escalate, the command guard is on and the intent check is off.

```bash [Terminal]
oz0 setting set GUARDRAILS_CONFIG '{"packs": {"pii.dk": false}, "pii": "redact", "injection": "escalate",
  "agents": {"support-triage": {"pii": "escalate", "intent": "on"}}}' --reason "Danish numbers are order numbers here"
```

<table>
<thead>
  <tr>
    <th>
      Key in <code>
        GUARDRAILS_CONFIG
      </code>
    </th>
    
    <th>
      Values
    </th>
    
    <th>
      Default
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        packs
      </code>
    </td>
    
    <td>
      <code>
        {"&lt;pack id>": true or false}
      </code>
    </td>
    
    <td>
      every pack on
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pii
      </code>
    </td>
    
    <td>
      <code>
        redact
      </code>
      
      , <code>
        escalate
      </code>
      
       (a person decides whether the value goes on), <code>
        refuse
      </code>
      
       (the model gets a refusal instead of the result; the job fails at <code>
        job_end
      </code>
      
      ), <code>
        off
      </code>
    </td>
    
    <td>
      <code>
        redact
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        secrets
      </code>
    </td>
    
    <td>
      <code>
        redact
      </code>
      
      , <code>
        refuse
      </code>
      
       or <code>
        off
      </code>
      
      : never <code>
        escalate
      </code>
      
      , since the person asked would read the secret
    </td>
    
    <td>
      <code>
        redact
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        injection
      </code>
    </td>
    
    <td>
      <code>
        escalate
      </code>
      
      , <code>
        refuse
      </code>
      
      , <code>
        off
      </code>
    </td>
    
    <td>
      <code>
        escalate
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intent
      </code>
    </td>
    
    <td>
      <code>
        on
      </code>
      
      , <code>
        off
      </code>
    </td>
    
    <td>
      <code>
        off
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commands
      </code>
    </td>
    
    <td>
      <code>
        escalate
      </code>
      
       (the <a href="https://docs.oz0.ai/build/plugins/verified/guardrails/commands#the-command-guard">
        command guard
      </a>
      
       sends a catastrophic or unreadable shell command to a person, on a node whose runtime does not decide commands itself) or <code>
        off
      </code>
      
      ; what the platform's command rules ask about is changed with <a href="https://docs.oz0.ai/govern/command-rules">
        command rules
      </a>
      
      , not here
    </td>
    
    <td>
      <code>
        escalate
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        agents
      </code>
    </td>
    
    <td>
      the same keys per agent name; an agent's entry overrides the tenant's key by key, and its <code>
        packs
      </code>
      
       merge pack by pack
    </td>
    
    <td>
      none
    </td>
  </tr>
</tbody>
</table>

When several findings disagree, `refuse` wins over `escalate`, and `escalate` over `redact`; but when a value holds a
secret too, an `escalate` for personal data becomes a redaction, so that nobody is shown the secret. A setting the plugin cannot
read makes its checks fail closed: each result and output goes to a person with the reason, and none passes
unchecked; the command guard stays on then, since it only ever asks a person. Changing a setting restarts the plugin's
server on the nodes.

`GUARDRAILS_CONFIG` is a [governance setting](https://docs.oz0.ai/govern/reviewed-changes#four-eyes-for-governance-changes): it turns
safety checks off, so changing it needs the governance-write permission besides settings-write and a reason (`--reason`,
or `$OZ0_REASON`), and the audit log keeps the reason. A change that loosens a check (one turned off or to a weaker
action, or a pack turned off, for the tenant or an agent) waits for a second admin where the tenant has four eyes on;
one that only tightens applies at once. A value the plugin would not read is refused when it is set.

<table>
<thead>
  <tr>
    <th>
      Setting
    </th>
    
    <th>
      What it holds
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        GUARDRAILS_CONFIG
      </code>
    </td>
    
    <td>
      The JSON above. A governance setting: changing it takes a reason
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GUARDRAILS_PACKS
      </code>
      
      , <code>
        GUARDRAILS_PACKS_2
      </code>
      
      , <code>
        _3
      </code>
      
      , <code>
        _4
      </code>
    </td>
    
    <td>
      Extra packs: a JSON array of packs, or <code>
        gz:
      </code>
      
       and the base64 of its gzip, since a setting holds at most 4 KiB. A pack with a baseline pack's ID replaces it, except a classified pack, which an extra pack may add to but never replace
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GUARDRAILS_CLASSIFIED_LOCAL
      </code>
    </td>
    
    <td>
      The classification markings allowed into agents whose data stays on the node, by ID, such as <code>
        ["se_begransat_hemlig"]
      </code>
      
       (see <a href="https://docs.oz0.ai/build/plugins/verified/guardrails/classified-text#classified-text">
        Classified text
      </a>
      
      ). A governance setting: changing it takes a reason
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GUARDRAILS_INTENT_MODEL
      </code>
    </td>
    
    <td>
      The model the intent check asks, by the provider's name, such as <code>
        claude-haiku-4-5
      </code>
      
      . Another model than the agents it judges run on: it never judges a call on the model that made it (see <a href="https://docs.oz0.ai/build/plugins/verified/guardrails/intent-and-limits#the-intent-check">
        The intent check
      </a>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GUARDRAILS_INTENT_PROVIDER
      </code>
    </td>
    
    <td>
      <code>
        anthropic
      </code>
      
       (the default) or <code>
        openai
      </code>
      
      : which API the check speaks to the gateway
    </td>
  </tr>
</tbody>
</table>

### Your own packs

A pack is JSON in the format `oz0.guardrails.pack/v1`:

```json [pack.json]
{
  "format": "oz0.guardrails.pack/v1",
  "id": "acme.customers",
  "title": "Acme customer numbers",
  "version": "1",
  "kind": "pii",
  "description": "Customer numbers of the form AC-123456.",
  "patterns": [
    {
      "id": "customer_number",
      "label": "customer number",
      "regex": "\\bAC-[0-9]{6}\\b",
      "false_positives": "Order numbers of the old shop used the same prefix.",
      "examples": {"match": ["customer AC-123456"], "no_match": ["AC-12345", "BAC-123456"]}
    }
  ]
}
```

`kind` is `pii`, `secrets` or `injection`. A pattern's `regex` must be valid in both Go's and Python's syntax (no
back-references, look-arounds or inline flags), and it must not be one of the shapes that take minutes in a
backtracking engine on a long result someone wrote to stall it. The plugin refuses a pack whose pattern has:

- a repetition without an upper bound (`+`, `*`, `{2,}`) anywhere but at the pattern's end: write
`[A-Za-z0-9._-]{1,64}@` rather than `[A-Za-z0-9._-]+@`;
- a count over 1000;
- a group repeated more than once that holds a repetition of a varying count, such as `(a+)+` or `(?:a{1,8}){2,}`;
- a repeated group whose alternatives can start with the same character, such as `(?:A|AA){1,40}`; `(?:[0-9]|-[a-z])*`
is fine;
- runs that can match the same text side by side, whose ways to split a text multiply past 4096 at one position, such
as `[A-Z0-9]{1,1000}[A-Z0-9]{1,1000}`. Put a character between them that neither matches, or lower their bounds:
`[A-Z0-9]{1,64}[A-Z0-9]{1,64}` is at the limit.

These rules are an estimate, not a promise that a pattern is quick: a pattern at the limit still takes seconds on 1 MiB of text. So every check also runs under a deadline of 3 seconds. A check that runs past it is stopped, and the hook fails closed: the result goes to a person, or is refused when it holds one of the tenant's secrets. Keep your patterns well inside the limits, and anchor them with a word or a fixed prefix where you can (`\bAC-[0-9]{6}\b`).

`flags: ["i"]` ignores case; `validate` names one of the checks above (`luhn`, `mod97`, `se_pnr`, `se_samordning`, `no_fnr`, `dk_cpr`, `fi_hetu`, `is_kt`, `de_idnr`, `de_idcard`, and `isin` and `lei` for securities and legal-entity identifiers by their check digits); `context` lists words of which one must be near a match; `redact_as` is the text a match is replaced with (at most 64 characters), instead of `[PII:<label>]`. The plugin checks every pack when it starts (the format, the validator's name, that the regex compiles and that every example holds) and fails closed when one does not. Packs are data, never code.

A pack may also set `action`: the least its findings get, whatever its check's action says. `redact`, `escalate` or `refuse` for a `pii` pack, `redact` or `refuse` for a `secrets` pack (secrets never go to a person), `escalate` or `refuse` for an `injection` pack. A pack of secrecy references (such as "sekretess enligt 21 kap. 7 § OSL") sets `"action": "escalate"`, so such a document goes to a person while personal data elsewhere is still redacted: the person reads the value with the personal data redacted and the marking left in, and a yes passes on that redacted value.

When the same value also holds a secret, it is redacted instead and nobody is asked. A stricter action of the check still wins (`refuse` stays `refuse`), and a check or pack you turn off stays off.


## Sitemap

See the full [sitemap](https://docs.oz0.ai/sitemap.md) for all pages.
