--- title: 'Guardrails' description: 'Rules you write once in Studio and the box enforces on every chat turn and workflow run: block the turn, mask the data, or hold it for a person. No code, no redeploy.' position: 11 --- # 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 dev` or > 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. ![The Guardrails screen in Studio listing three rules: block internal codenames on the incoming message, mask personal data in the agent reply, and hold wire-transfer requests for approval on one agent.](/images/guardrails/guardrails-list.png) _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](#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. | ![The New guardrail drawer: name, check "Off-limits subjects", inspects "Incoming message", when it fires "Block the turn", and the generated settings section with two subjects listed in plain language.](/images/guardrails/new-guardrail.png) _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](/docs/sdk/humans/guardrails#the-catalog) 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](#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: ![The Playground: an ordinary question answered, then a message mentioning "project-falcon" blocked by the "Block internal codenames" guardrail, then a wire-transfer request held for review by the "Wire transfers need sign-off" guardrail.](/images/guardrails/playground-decisions.png) _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. ![An approval in the HITL Inbox opened for review: the guardrail badge, the reason, the payload showing the held message, the run context with the guardrail step still running, an empty audit log, a comment field, and the Reject and Approve buttons.](/images/guardrails/held-turn-review.png) _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 `](/docs/cli/reference/hitl#stackbone-hitl-approve) 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](/docs/cli/protocol/auth#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. ![A run's trace in Studio: the run finished in 17 ms with no tokens, one step named "Block internal codenames", and the inspector showing the step's input (target and check type) and output (action block, the reason, and infrastructureFailure false).](/images/guardrails/guardrail-step-trace.png) _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. ![The Catalog entry for the my-workspace agent, scrolled to "Wired to": one guardrail on this agent (Wire transfers need sign-off) and two from the workspace default set (Block internal codenames, Mask personal data in replies).](/images/guardrails/catalog-wired-to.png) _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. ![The General settings screen in Studio with the "Guardrail checking model" card: a note that it runs on every turn, twice on a turn checked on both sides, and that the deterministic guardrails never call a model, above a picker set to Platform default.](/images/guardrails/checking-model.png) _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](/docs/sdk/humans/guardrails): the catalog in detail, the actions, and why there is no `stackbone.guardrails` in your code. - [Human-in-the-loop](/docs/sdk/humans/approval): the inbox a held turn lands in, and `requestApproval()` for the gates you write yourself. - [Governance](/docs/home/features/governance): the rest of Studio, from the Playground to Evals. - [Observability](/docs/home/features/observability): the trace every decision is written to. - [Security and auth](/docs/home/features/security-auth): how the box authenticates a caller before a rule ever runs.