---
title: "Guardrail coverage and limits"
description: "Configure intent checks and understand what redaction and detection can miss."
canonical_url: "https://docs.oz0.ai/build/plugins/verified/guardrails/intent-and-limits"
---
# Guardrail coverage and limits

> Configure intent checks and understand what redaction and detection can miss.

For: tenant administrators and plugin users.

Configure intent checks and understand what redaction and detection can miss.

## The intent check

The intent check is an escalate-only hook (`decisions: [allow, escalate]`), as the command guard is: it may only let a
call through or send it to a person, never refuse it or change it.
Whatever else it answers, and when it times out (after 20 seconds) or fails, the call waits for a person. A person's
yes to it is not an approval of anything else: the call still meets every rule of the tool policy after it.

When it is on, each tool call's arguments, the function's name and the job's request (its input, cut to 4 KiB) are
sent to `GUARDRAILS_INTENT_MODEL`. That model call goes through the tenant's gateway like any of the job's model calls:
it is metered and budgeted to the job and its agent, recorded, and under the tenant's data rules and model allowlist.

The plugin holds no provider key: each call brings a key for the node's own gateway path that is good for that job
alone. So the arguments of every call reach that model; choose one your data rules allow for them.

Three cases ask no model and send the call to a person instead: what the check would send holds a classified marking (whatever `GUARDRAILS_CLASSIFIED_LOCAL` allows), the job's agent keeps its data on the node (`data: local`), whose calls get no key for models behind the edge, and the job's agent runs on `GUARDRAILS_INTENT_MODEL` itself.

A model asked whether its own call fits tends to say yes, so the check does not judge a call on the model that made it: the call waits for a person with the reason "the intent check cannot judge a call on the same model the agent is running; set GUARDRAILS_INTENT_MODEL to a different model".

The event the check gets names the model the agent runs on, as the job resolved it through the tenant's aliases and mapping rules, and the check compares the model's name, whichever provider record serves the agent: the check always asks the vendor's model by the vendor's name for it. Set `GUARDRAILS_INTENT_MODEL` to a model none of the agents it watches run on, such as a small model of another family.

It adds a model call to every tool call, and its answer is a model's opinion: a model that is fooled by what it reads
lets a call through to the rules after it, and one that is too careful asks people more often. Watch how often it
escalates on the [approvals](https://docs.oz0.ai/agents/approvals) page.

## Invisible characters and look-alike letters

Every check reads text through the platform's one fold, the same one every model judge reads through: invisible code points (zero-width characters, soft hyphens, invisible operators, Hangul fillers, bidi controls, Unicode TAG characters and the like) are dropped, a line or word break they stand for stays, and fullwidth letters and the like read as their plain forms (Unicode NFKC).

The injection check scans a value and its folded form, so phrasing split by characters nobody sees is found; redaction reads the value and its folded form, both before anything is redacted, so neither reading hides what the other finds, and redacts each finding where it sits in the value, the invisible characters and look-alike letters inside it included. The injection check also counts hidden text as a finding: TAG characters, bidi controls, or three or more other invisible characters, variation selectors among them, in which bytes can be written one after another or one after each letter.

A variation selector is not counted where Unicode gives it a meaning: the emoji and text presentation selectors after any visible character, an ideographic variant selector after a Han ideograph (as Japanese names use it), and one of the other selectors after a character of a block with standardized variants, such as mathematical symbols, CJK punctuation, Myanmar or Mongolian. Nor are the zero-width joiner and private-use characters, which icon fonts draw. A selector after a Latin letter, after another selector or after nothing visible is counted.

Where findings of two packs overlap, the redaction covers both, under the stricter kind (a secret over personal data), and each finding's action counts. The intent check's model reads the request, the function and its arguments folded too, each in a block of its own with every `<` in it written as `&lt;`, so no text in them can close its block or open another.

## What the checks miss

- Personal data that is not a format: names, addresses, free-text descriptions, and identifiers no pack lists.
- Formats written another way: an IBAN in lower case, a personnummer split across lines, digits spelled out,
values encoded (base64, URL-encoded) or split across fields of a result.
- False positives: a validator lowers them, never removes them. About one in ten long numbers passes Luhn, so
an order or customer number can be redacted as a card or a personnummer. Each pack's notes say what it confuses.
- Secrets the tenant does not hold: only the tenant's own secrets are found by value; others only when their
format is listed. A private key block is replaced from its header to the end of its body, and its end line stays;
a key that is not in a block (a bare base64 value) is found only when it is one of the tenant's own secrets.
- Injections that are not phrased as one: a page that persuades rather than commands, another language, text in an
image or a file, or an attacker who reads these patterns and writes around them.
- What the hooks do not see: the model's own calls and context, a harness session's tools after the call, and
anything a tool sends elsewhere itself.
- Shell commands no harness asks about, and what a command does once it runs: the command guard and the platform's
command rules read only what a harness asks to run, before it runs (see [what it does not see](https://docs.oz0.ai/build/plugins/verified/guardrails/commands#the-command-guard)).
They are a check, not a boundary.

## What redaction does not remove

Redaction changes what the model reads and what the job gives back. It does not change what the platform keeps:

- The job's history and trace keep the result as the tool returned it, before any hook. People who may read the
job's content (the content permission) can read it there, as they can read any tool's result.

### A person asked about an escalation reads what escalated

The approval stores the escalated value and shows it to the person deciding it. What is set to `redact` in the same value stays redacted, for the person and after a yes; what escalates is left for the person to read. That is why secrets cannot be set to `escalate`.

It holds for `pii: escalate` (the personal data is shown), for the intent check (it shows the call's arguments), and for the escalations by which the checks fail closed: a result longer than 1 MiB, a check that runs past its 3-second deadline, and a setting the plugin cannot read, go to a person as they are (unless the platform found one of the tenant's secrets in the value: then it is refused). Choose `redact` or `refuse` where nobody should read the value.

No detection rate is claimed. Test the checks on your own data with `oz0 run oz0-guardrails-check "<text>"` or
`oz0 plugin test plugins/guardrails`.


## Sitemap

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