Guardrails overview
An agent takes text from strangers, hands it to a model, calls tools with it, and sends the result back. Agent Guardrails is the platform's check on that text: on the way in, on the way out, and - when you ask for it - around every tool call in between.
It is off for every agent, new or existing, until somebody turns it on. Nothing is called, no latency is added, and no cost is incurred while it is off. That is not a default you have to trust; it is the absence of a request.
What it protects against
| Check | What it catches | Default action |
|---|---|---|
structure | Text that is longer than the policy allows, or that hides its wording in invisible or look-alike characters | block |
denylist | Phrases and patterns you said you never want to see | block |
allowlist | Phrases you always want let through, whatever else fires | allow |
secrets | API keys, tokens, private keys and passwords, in either direction | block |
pii | Personal data - names, emails, phone numbers, card numbers, addresses, identity numbers | mask |
injection | Attempts to override the agent's instructions - "ignore your previous instructions and ..." | block |
toxicity | Abusive and hateful language | block |
policy | A slower judgement of what the text is actually asking for, across ten safety categories | per category |
Those eight names are the names you will see in a verdict, in the CLI's check table, and in the Console. They do not change.
The three presets, in plain terms
A preset seeds the whole policy. Every field it sets is yours to change afterwards - a preset is a starting point, not a mode.
| Preset | What you get | Who it is for |
|---|---|---|
off | Nothing runs. The default for every agent. No checks, no added latency, no cost. | Anything internal, anything latency-critical, and every agent you have not looked at yet. |
standard | Credentials and personal data in both directions, instruction-override detection, abusive-language detection, and the safety judgement on incoming text. Personal data is masked, not refused. | Almost everybody. This is the recommended starting point. |
strict | standard, plus the safety judgement applied to the agent's own replies, the strict judgement profile, borderline content refused rather than flagged, and a refusal when a check cannot run at all. | Agents exposed to the public internet, and anything where a missed check is worse than a false refusal. |
strict deliberately does not block on personal data. Masking stays the default there, because refusing a request because it contains a phone number frustrates the person who typed their own phone number.
A fourth value, custom, is not something you ask for: the platform records it when you change a field to something the preset it names does not say. A policy labelled standard is therefore always actually standard.
Monitor before you enforce
Every policy has a mode:
enforce- ablockdecision stops the request and the caller gets the refusal message.monitor- every check still runs, every verdict is still recorded, and everyblockis recorded as aflaginstead. The original text goes through unchanged.
monitor is the adoption path, and it is honoured by the platform rather than by your code, so there is no way to get it wrong. Turn a preset on in monitor, leave it for a week, read the verdict feed, and only then switch to enforce:
platformctl agents security set research-buddy --preset standard --mode monitor
# a week later: exactly what would have been refused
platformctl agents security verdicts research-buddy --decision block --since 7d
What it costs in latency
The checks run in four tiers, in order, and stop as soon as one of them decides. So the expensive tier is only reached by text the cheap ones did not already settle.
| Tier | Checks in it | Time budget | Typical cost |
|---|---|---|---|
structure | structure | 50 ms | Under a millisecond. Length and character checks. |
pattern | denylist, allowlist, secrets, pii | 400 ms | Tens of milliseconds. Regular expressions and personal-data detection. |
classifier | injection, toxicity | 500 ms | Tens of milliseconds. Two small text classifiers. |
semantic | policy | 1500 ms | Hundreds of milliseconds. A dedicated safety model reads the text. |
Those budgets are the standard preset's defaults and every one of them is settable (--budget-ms semantic=800). A tier that runs out of time does not become an error: it becomes whatever fail.<tier> says - open records a concern and continues, closed refuses. See limits and latency for the measured numbers and what happens when a tier is unavailable.
The cheapest number here is the one for an agent on off: zero, because nothing is called.
The path one message takes
Follow a single turn through a guarded agent, because the order is the whole design.
A message arrives at your agent - from a chat widget, an API call, another workload. Before your framework code sees a single character of it, the harness hands the text to the guardrail service labelled direction: input. The service resolves the policy in force for this agent, then runs the tiers in order.
The structure tier goes first because it is nearly free and because the checks after it can be defeated by text it cleans up: a request padded with invisible characters, or one written in look-alike letters to slip past a phrase list. If strip_invisible is on, the text is cleaned and the cleaned text is what everything downstream sees.
The pattern tier then looks for credentials, personal data and your own denied phrases. This is where masking happens, and masking is not refusal - it is a rewrite. Email john@acme.example the invoice becomes Email <PII_EMAIL_1> the invoice, and that rewritten text is what continues down the pipeline. The real value is not stored anywhere by the platform; it travels back to the caller inside an encrypted mapping reference and nowhere else.
The classifier tier asks two narrower questions - is this an attempt to override the agent's instructions, and is this abuse - and the semantic tier asks the broad one: what is this text actually asking for, across ten safety categories. The semantic tier is the only one that costs tokens, and the only one you would notice in a latency graph.
Out of all that comes one verdict: a decision (allow, mask, flag or block), a one-sentence customer-readable reason, the per-check detail, and the text to use next. On allow the text is what arrived. On mask it is the rewritten text. On flag it is the original, with a record that something fired. On block there is no text at all - the request stops, and the end user receives a normal assistant reply whose content is the refusal message.
Your agent runs on whatever text came back. If the policy asks for tool checks, each tool call's arguments are checked before the tool runs and each tool's result is checked before the model reads it - which is the only place indirect prompt injection can be caught, because a web page or a support ticket that carries instructions aimed at your agent arrives as a tool result and never as a user message.
Then the reply goes out, labelled direction: output, and the outgoing half of the policy runs over it: personal data, credentials, denied phrases, abusive language, and - under strict - the safety judgement again. If the incoming pass masked anything and restore is response, the placeholders in the reply are swapped back for the real values here, so the person reading the reply sees john@acme.example and the model never did.
Every verdict along that path is recorded, without the text and without any detected value. block and flag also write an entry to the project audit log.
Turning it on
Three interfaces, one policy document behind all of them.
# The recommended first move on any agent.
platformctl agents security set research-buddy --preset standard --mode monitor
# What is actually in force, and which layer each setting came from.
platformctl agents security get research-buddy
The API is PATCH /v1/agents/{name}/security/policy; the Console has it on the agent's Security tab. Changing a policy rolls a new revision of the agent, the same way a compute or environment change does.
What to read next
- Quickstart - on for one agent, an email address masked and restored, then a refusal, in under ten commands.
- Policies - the whole document, field by field, plus the tighten-only rule.
- Personal data masking - placeholders, ordinals, the mapping reference, and what never sees the real value.
- Prompt injection and jailbreaks - direct and indirect, and how to tune the thresholds.
- Refusal messages - what the end user is told, and how to change it.
- Verdicts and audit - reading the record, and what is deliberately not in it.
- The standalone API - the same checks over text of your own, with no agent involved.