Skip to main content

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​

CheckWhat it catchesDefault action
structureText that is longer than the policy allows, or that hides its wording in invisible or look-alike charactersblock
denylistPhrases and patterns you said you never want to seeblock
allowlistPhrases you always want let through, whatever else firesallow
secretsAPI keys, tokens, private keys and passwords, in either directionblock
piiPersonal data - names, emails, phone numbers, card numbers, addresses, identity numbersmask
injectionAttempts to override the agent's instructions - "ignore your previous instructions and ..."block
toxicityAbusive and hateful languageblock
policyA slower judgement of what the text is actually asking for, across ten safety categoriesper 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.

PresetWhat you getWho it is for
offNothing 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.
standardCredentials 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.
strictstandard, 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 - a block decision stops the request and the caller gets the refusal message.
  • monitor - every check still runs, every verdict is still recorded, and every block is recorded as a flag instead. 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.

TierChecks in itTime budgetTypical cost
structurestructure50 msUnder a millisecond. Length and character checks.
patterndenylist, allowlist, secrets, pii400 msTens of milliseconds. Regular expressions and personal-data detection.
classifierinjection, toxicity500 msTens of milliseconds. Two small text classifiers.
semanticpolicy1500 msHundreds 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.