Guardrails
A guardrail is a rule that inspects a message before your agent sees it, or a reply before the user does, and blocks it, masks it or holds it for a person. You write it in Studio, not in code, and the box (the container running your workspace, on your laptop under
stackbone devor deployed in your cloud) enforces it from the next turn on. No rebuild, no restart. Every decision shows up in the chat, on the run's trace and, when a rule holds a turn, in the HITL inbox.
Three rules with an action each. Two apply everywhere, the third to a single agent.
Govern what reaches your agents
| Stage | Where you work in Studio |
|---|---|
| Write | Guardrails › New guardrail. Pick a check from the catalog, what it inspects, what happens when it fires and where it applies. The check's settings form is generated from the same schema the box enforces, so a rule that saves is a rule that runs. |
| Attach | The Where action on a rule. Attach it to the whole workspace, to one agent or to one workflow, and detach it without deleting it. |
| Enforce | The box, at two borders: a chat turn (the message in, the reply out) and a workflow run (its input payload). Nothing to call from your code. |
| Decide | HITL Inbox. A turn a rule held waits there with the message, the reason and an audit log until someone approves or rejects it. |
| Inspect | Playground prints the reason inline. Runs records a guardrail step on the trace. Catalog shows which rules govern each agent and workflow. |
| Tune | Settings › General › Guardrail checking model picks the model the three model-backed checks ask. |
Write a rule
Open Guardrails and click New guardrail. A rule is one form:
| Field | What you choose |
|---|---|
| Name | What a blocked user reads. A refusal quotes it, so make it readable. Unique on the box. |
| Check | One of the six checks in the catalog. |
| Inspects | Incoming message (what the user or the trigger sent) or Agent reply (what the agent is about to say). |
| When it fires | Block the turn, Mask and carry on or Ask for approval. |
| Check settings | The check's own settings, rendered from its schema. Switch to the JSON tab to paste them. |
| Where it applies | Everywhere in this workspace (the default), One agent or One workflow, picked from what the box serves. |
| Enabled | Off keeps the rule and its attachments, but the box skips it. |
| Let the turn through if the check itself cannot run | Off by default. Off means a check that errors stops the turn. On means the turn is waved through and the failure is only recorded. |
A rule for the checking model: two subjects written the way you would explain them to a colleague, judged in one call.
Studio refuses three combinations at save time instead of storing a rule that does nothing: an Agent reply rule on a check that only judges the incoming message, Mask and carry on on a check that can only say yes or no, and Ask for approval on the agent's reply, because a reply that already exists cannot be parked. Owners, admins and members can save. An approver or a viewer sees the screen read-only.
The catalog
Six checks ship. The first three are deterministic and cost nothing per turn. The last three ask the checking model, so each adds one model call and some latency.
| Check | Looks for | Inspects | Needs a model |
|---|---|---|---|
Blocked words (denylist) |
Whole words from your list, case-insensitive by default. Pattern mode takes regular expressions. | Message, reply | No |
Maximum length (length_cap) |
A message longer than a number of characters. An emoji counts as one. | Message | No |
Personal data (pii) |
Email, phone, credit card, IBAN, national id, IP address, URL and pasted credentials, one row per kind you add, each with its own masking. | Message, reply | No |
Prompt injection (prompt_injection) |
Text trying to take over the agent: an instruction hidden in a pasted document or a web page. Optional guidance about your own agent. | Message | Yes |
Off-limits subjects (denied_topics) |
Subjects written in plain language: "competitor pricing", "advice about tax". Your own pricing still goes through. | Message, reply | Yes |
Harmful content (content_moderation) |
Only the categories you pick among hate, harassment, violence, sexual, self-harm, illegal and profanity. | Message, reply | Yes |
Off-limits subjects and Harmful content judge the whole list in one model call, so six subjects cost what one costs. The SDK page has the details that decide whether a rule is useful: what "whole word" means, which kinds of personal data exist, and why URLs are never masked by default.
What an action does
| When it fires | On a chat turn | On a workflow run |
|---|---|---|
| Block the turn | The turn ends. The message never reaches the agent. | The run never starts and is not retried. An inbound trigger event that trips it is marked rejected and the queue moves on. |
| Mask and carry on | The matched values are replaced and the conversation carries on. Only Personal data can do this. | Each masked value goes back where it came from, so the payload keeps its shape and your input schema still accepts it. |
| Ask for approval | The turn is held and lands in the HITL Inbox. Needs a durable session (see Decide a held turn). | Restated as a refusal. A run has nobody waiting and no thread to replay, so the box does not promise a review it cannot queue. |
Watch it fire
Open the Playground, pick the agent and type. A rule that fires answers in the agent's place, naming itself and the reason:
Three turns against the three rules above: one answered, one blocked, one held.
A blocked turn ends cleanly: the stream closes normally and the transcript
prints the reason. On the OpenAI, Anthropic and AG-UI wires the same sentence
arrives as ordinary assistant text. A structured guardrail frame arrives
mid-turn with the rule, the check, the side, the action and the reason, so a
client of your own can render it its own way. A masking decision
names the kinds it masked, never the values.
Decide a held turn
A held turn waits in the HITL Inbox with a guardrail: badge. Open it and
you see the message the user sent, the reason, the run it belongs to and the
audit log.
What the reviewer sees. Fallback reject, timeout 24 hours: a hold nobody decides closes, it never opens.
Approve replays the message through the conversation it was held on, as if
the rule had never fired. The box does not judge it again: a person already said
yes. Reject ends the turn with a reply naming the rule and, when you left one,
your comment. If nobody decides within 24 hours, the box rejects it. The inbox
updates live, and
stackbone hitl approve <id>
does the same from a terminal.
Holding a turn needs a durable session: the Playground has one, and your
own client sends the x-stackbone-session header (or a threadId over AG-UI).
Without one there is no thread to replay, so the box tells the user the message
could not be sent for review and ends the turn. See
Session keys.
Read it on the trace
Every decision is a guardrail step on the run's trace, next to the model
calls and tool calls of the same run.
The blocked turn: 17 milliseconds, no tokens, one step. The model was never called.
The step's output says why. infrastructureFailure: false means your content
broke the rule. true means the check itself could not run (a model timeout, a
rate limit) and the rule failed closed. The two need different fixes. A held
turn keeps its step running until someone decides, and the decision lands in
the same output. Evals count a case a rule stopped as stopped, not failed:
it says nothing about how well the agent answers.
Where a rule applies
A rule guards nothing until you attach it somewhere. The Applies to column says where, and a rule attached nowhere reads Nowhere.
| Scope | Covers |
|---|---|
| Everywhere in this workspace | Every agent and every workflow, including the ones you add later. The default set. |
| One agent | One named deep agent. |
| One workflow | One named workflow. |
Attach and detach from Where. Detaching never deletes the rule. A rule reachable twice, through the default set and its own attachment, still runs once. Masking rules run before the checks that read the text, so a moderation rule never judges data another rule masked.
The Catalog shows the same facts from the other side. Open an agent or a
workflow and Wired to lists what governs it, in two groups: rules attached
to this entry, and the workspace default set. A rule switched off carries an
off badge.
What governs one agent, read from its Catalog entry.
Runs the platform starts on your behalf are exempt: indexing an uploaded document, mapping a trigger payload, and scoring an eval case. The exemption follows the platform's own start call, never a workflow's name, so a run you start yourself on the same workflow is judged like any other.
The checking model
Prompt injection, Off-limits subjects and Harmful content ask a model. Pick it
once under Settings › General › Guardrail checking model. Leave it on
Platform default and the box uses openai/gpt-4o-mini. The call goes
through the model provider you already configured, so there is no second
credential to manage.
One knob for the three model-backed checks. The other model pickers on this screen do not touch guardrails.
Budget one call per checked content: a turn with model-backed rules on both the message and the reply makes two. Reading a reply needs the whole reply, so a turn with an Agent reply rule buffers the answer and loses token-by-token streaming. Keep reply rules to the agents that need them.
When the check cannot run, the rule fails closed unless you switched on Let the turn through if the check itself cannot run for it. An unreadable answer from the checking model counts as a failure, never as a pass.
Where guardrails live
Studio edits the box you selected in the header. The rules never pass through the control plane.
| Layer | Role |
|---|---|
| Studio | The screens: the rules, the inbox, the trace. Reads and writes the box you selected. |
| Box | Keeps the rules in its own Postgres, judges both borders, writes the guardrail step and the approval row. The same code runs under stackbone dev and in your cloud. |
| Control plane | Nothing about your rules. It holds your organization, its members and roles, and mints the token that decides who may edit. |
| Model provider | Answers the three model-backed checks, through the endpoint and key you configured for the box. |
What each part needs
| To use | You need |
|---|---|
| Blocked words, Maximum length, Personal data | A running box. Nothing else. |
| Prompt injection, Off-limits subjects, Harmful content | A model provider configured on the box. Optionally a checking model picked in Settings. |
| Ask for approval | A durable session on the chat: x-stackbone-session, or AG-UI's threadId. The Playground has one. |
| Mask and carry on | The Personal data check. |
| Editing rules | The owner, admin or member role. Approver and viewer read only. |
Read more
- Guardrails in the SDK: the catalog in detail,
the actions, and why there is no
stackbone.guardrailsin your code. - Human-in-the-loop: the inbox a held turn lands
in, and
requestApproval()for the gates you write yourself. - Governance: the rest of Studio, from the Playground to Evals.
- Observability: the trace every decision is written to.
- Security and auth: how the box authenticates a caller before a rule ever runs.