# Guardrails

> Field reference for Prompt Guard, Expert Witnesses, Judge, and Jury, the four guardrail layers attachable to an LLM Surface.

See [Guardrails →](/products/affinidi-trust-fabric/agent-stream/concepts/guardrails.md) for how the four layers compose conceptually. This page is the field-level reference for each layer’s configuration block.

## Prompt Guard

The Prompt Guard block holds regex-based content filtering plus model-backed PII detection, screening what a caller sends before it reaches the model and what the model sends back before it reaches the caller. Its regex rules run locally with no extra LLM call, making it the fastest and cheapest of the four guardrail layers. It’s the natural first stop for known-shape sensitive data (SSNs, emails, credit cards) before reaching for something heavier like Judge or Jury.

- Regex guards handle anything with a recognizable shape (SSNs, emails, credit cards, custom patterns) instantly and with no extra model call, so start there.

- Model-backed PII detection only earns its cost when you need to catch PII without a fixed shape, like names, addresses, or organizations. It calls a detection model, so pair it with regex rather than using it alone.

- Add guards on the request side to stop sensitive data before it reaches the model at all; add them on the response side to catch anything the model itself echoes back or generates.

Request-side and response-side guards are reached separately, not shown together: selecting the request-side canvas node opens its own full-width editor, headed “→ Prompt Guard” with the subtitle “Screen requests before they reach the model”; selecting the response-side node opens the mirrored “← Prompt Guard” editor instead.

| Field | What it does | Default |
| Enabled | Master switch. Defaults on so a stored config predating this flag keeps enforcing its rules. | On |
| Request Guards | Guards applied to client requests before they reach the LLM. Add as many as you need. The dashboard has no field literally labelled “Request Guards”. Reached by selecting the request-side canvas node, headed Prompt Guard with the subtitle “Screen requests before they reach the model”; each entry renders as a numbered “Prompt Guard #N” card. | None added |
| Response Guards | Guards applied to LLM responses before they reach the client. Add as many as you need. Reached by selecting the response-side canvas node, headed Prompt Guard with the subtitle “Screen responses before they reach the caller”. | None added |
| Model-Backed PII Detectors | Model-backed PII detectors (see below). Add as many as you need. Not a separate top-level section in the dashboard. These appear as AI Matching-type “Prompt Guard” cards mixed into the request-side list (request only; the response side offers Regular Expression only). Accepts a single object on the wire for back-compat with configs predating the list form. | None added |

### Regex guards

Regex guards match a request or response against a pattern and, on a hit, reject, redact, or pseudonymize the matched text.

- Reject the call completely is a hard stop, not a filter, for when a match means something has already gone wrong and the call shouldn’t proceed.

- Redact the text that matches the pattern fits most cases: the sensitive value just needs to disappear, with no need to preserve context across turns.

- A conversation that keeps referring to the same masked value across turns needs Use a NER ID to preserve context instead. Each distinct value gets its own stable ID rather than being replaced by the same static token every time. See [PII protection and NER IDs →](/products/affinidi-trust-fabric/agent-stream/concepts/pii-protection.md).

| Field | What it does | Default |
| Regex Rules | The rule set (below). Not shown under a single “Regex Rules” heading. It’s the whole card body once “Regular Expression” is chosen as the detection type: a “Detect these patterns” list plus a “When detected” handling control. | — |
| Rejection Response | Custom rejection response, shown under the “When detected” control’s Rejection response heading once “Reject the call completely” is chosen. Only meaningful when the handling is set to reject. | — |

The rule set:

| Field | What it does | Default |
| When detected | What happens on a match, picked from a single “When detected” dropdown with three options: “Reject the call completely,” “Redact the text that matches the pattern,” and “Use a NER ID to preserve context.” | Reject the call completely |
| When detected (mask rewrite) | How a match is rewritten when the handling is set to mask. Set via the same three-option “When detected” dropdown described above, not a separate “Mask Mode” field. “Redact” replaces every match of a type with the same static token (<EMAIL>); “Use a NER ID” gives each distinct value its own stable NER ID ([EMAIL_NER_001]), so entity relationships survive while the raw value is hidden. | Redact the text that matches the pattern |
| Restore Original Values in the Response | Restore the original values in the final response, as a toggle. Only meaningful on a request-side guard using the NER ID option. | Off |
| Link Pronouns to a Person’s NER ID | Link singular gendered pronouns (he/she/…) to a person’s NER ID when exactly one person has been named so far. Only meaningful on a request-side guard using the NER ID option. | Off |
| Detect These Patterns | The patterns to match, shown under a “Detect these patterns (N)” heading with “Built-in Pattern” / “Custom Regex” add buttons. | None added |

Add a pattern with Built-in Pattern to pick a ready-made detector by name (SSN, credit card, email, and similar common formats), or with Custom Regex to write your own expression and give it a label that shows up in logs and masked output.

The rejection response:

| Field | Default |
| Status Code | 403 |
| Response Body | A JSON prompt_guard_violation error |
| Content-Type | application/json |

Judge and Jury offer this same override further down their own panels, each under its own heading.

### Model-backed PII detection

Sends the request text to an OpenAI-compatible detection model that returns typed PII entities (names, addresses, organisations, …), catching PII that regex cannot. Send text only to a trusted, ideally self-hosted, detection endpoint.

- Link Coreferences collapses aliases like “Bob” and “Robert Smith” onto the same NER ID, but it forces every turn to re-scan the whole, ever-growing conversation. Turn it on only when you genuinely need that collapsing; the cost is real and ongoing.

- Cache Detections stays on by default so repeated messages in a multi-turn history skip the detection model on later turns.

- The default, Reject if PII detection is unavailable, fails a request rather than letting it through unmasked. Switch to Continue with regex masking only instead if availability matters more than catching non-regex PII on that one call.

| Field | What it does | Default |
| Enabled | Master switch for this detector. Not a separate toggle on the card: a detector is active as soon as it’s added, and removed via the card’s Remove button. | On |
| Provider | Provider type used to resolve a relative endpoint (e.g. OpenAI, Ollama, Azure, Custom), picked from a dropdown. Leave unset when the endpoint below is already a full URL. Labelled “Provider” (not “Provider Type”) in the shared provider selector. | Not set |
| Endpoint | Full URL, or a path resolved against the provider’s base URL. Rendered as an “Endpoint” dropdown of the provider’s known endpoints (via the shared LLM provider selector), not a free-text “Endpoint URL” field. | Not set |
| Model | Model name requested from the detection endpoint. | Not set |
| API Key Secret | Secret reference for the detection endpoint’s API key. | Not set |
| Entity Types to Detect | Entity types the model should return, shown as removable pills under “Entity types to detect”. | Person, Address, Organization, Location, Email Address, Phone Number |
| When Detected | What to do when PII is found. Set via the same three-option “When detected” dropdown used by regex guards (“Reject the call completely” / “Redact the text that matches the pattern” / “Use a NER ID to preserve context”). With “Reject,” the restore/coreference options below don’t apply. | Use a NER ID to preserve context |
| When Detected (mask rewrite) | How detected values are rewritten when the handling is set to mask, using the same “When detected” dropdown as above rather than a separate “Mask Mode” field. | Use a NER ID to preserve context |
| Rejection Response | Custom rejection response, shown under the “When detected” control’s “Rejection response” heading once “Reject the call completely” is chosen. | Not set |
| Restore in Response | Restore detected values in the response, as a toggle. Dashboard label: “Restore in response”. | Off |
| Link Coreferences | Collapse the model’s coreference groups (aliases like “Bob” and “Robert Smith”) to a single NER ID, as a toggle. Only meaningful when “When Detected” is set to preserve context with a NER ID. Dashboard label: “Link coreferences”. | Off |
| Cache Detections | Cache detection results so repeated messages (multi-turn history re-sends) skip the model, as a toggle. Dashboard label is plural: “Cache detections”. | On |
| Timeout (Seconds) | Per-detection-call timeout. | 30 seconds |
| If PII Detection Is Unavailable | If the detector is unavailable (errors or times out), reject the request or continue with only local regex masking applied. Separate from the “Reject” handling above, which rejects when PII is detected. Dropdown labelled “If PII detection is unavailable” with options “Reject if PII detection is unavailable” and “Continue with regex masking only”. | Reject if PII detection is unavailable |

## Expert Witnesses

Expert Witnesses attaches one or more purpose-trained external safety classifiers: a sibling to Judge and Jury, but a pure classifier rather than a general reasoning LLM. Different witnesses can sit on different seams (for example, Azure Prompt Shields on the request and Azure Content Safety on the response). A pure classifier is typically faster and cheaper than a full reasoning LLM call, so reach for Expert Witnesses when a purpose-trained detection service already exists for what you’re screening (prompt injection, jailbreaks, harm categories) rather than asking a general LLM (Judge or Jury) to reason about it from scratch.

- With an existing EnkryptAI account, prefer the current saved-guardrail provider over the legacy policy-detect one: it’s the actively maintained endpoint, bound to a guardrail policy you configure directly in EnkryptAI’s own console.

- The named cloud-security providers (Azure Prompt Shields, Azure Content Safety, Lakera) make sense when you already use that vendor elsewhere in your stack.

- generic is for a custom or self-hosted classifier that speaks the OpenAI-moderation-style response shape, nothing else.

- Fail Open on Provider Error stays off by default, so a guardrail outage blocks calls instead of silently letting them through. Turn it on only if availability matters more than this particular check on that seam.

Each witness:

| Field | What it does |
| (unlabeled name field) | Editable inline in the card header as a plain text input with no visible field label. Placeholder text reads “Witness N”. |
| (flattened) | The connector configuration below, flattened onto the same object (no nesting on the wire). |

The connector configuration:

| Field | What it does | Default |
| Enabled | Master switch, as a toggle. | On |
| Provider | Which provider’s request/response wire format to use, from a dropdown (see below). | EnkryptAI |
| Endpoint URL | Absolute URL of the provider’s detection endpoint. | Not set |
| API Key Secret | Secret reference for the provider API key. | Not set |
| Timeout (Seconds) | Per-call timeout. | 10 seconds |
| Fail Open on Provider Error | On allows the call through when the provider errors or times out (fail-open); off blocks it (fail-closed). | Off |
| Custom Headers | Static headers sent on every provider call, in addition to the auth header. The literal {key} in a value is replaced by the resolved API key: used for provider-specific routing headers such as EnkryptAI’s X-Enkrypt-Deployment. (The EnkryptAI Deployment/Guardrail/Policy fields below manage their own header and are hidden from this generic list to avoid a double editor.) | None added |
| Model | Optional model field injected into the request body. Required by the EnkryptAI AI Proxy (shown only when that provider is selected); unset for providers that take no model. | Not set |

Which seam a witness checks, the request before it reaches the LLM or the response before it reaches the client, is fixed by whether you place it on the request-side or response-side canvas node. Unlike Prompt Guard, Judge, and Jury, Expert Witnesses have no custom-rejection-response editor; a flagged verdict always uses the default rejection.

### Provider values

| Value | Description |
| enkryptai (default) | EnkryptAI Guardrails detection API (/guardrails/detect): a pure classifier, no LLM call. |
| enkryptai_guardrail | EnkryptAI’s current saved-guardrail detect endpoint (/guardrails/guardrail/detect), bound by a saved guardrail selected via the X-Enkrypt-Guardrail header. Preferred over enkryptai_policy_detect. |
| enkryptai_ai_proxy | EnkryptAI AI Proxy (/ai-proxy/chat/completions). The deployment named in X-Enkrypt-Deployment binds both the guardrail policy and the model; the proxy runs its own completion, so as a witness it incurs an extra model call. |
| enkryptai_policy_detect | EnkryptAI’s legacy saved-policy detection API (/guardrails/policy/detect), deprecated by EnkryptAI in favour of enkryptai_guardrail; retained for back-compat. |
| generic | A generic OpenAI-moderation-style API returning a flagged/blocked verdict. |
| lakera | Lakera Guard (Check Point) detection API (POST /v2/guard). |
| azure_prompt_shields | Azure AI Content Safety Prompt Shields (text:shieldPrompt): prompt-injection/jailbreak detection on the request. |
| azure_content_safety | Azure AI Content Safety harm-category moderation (text:analyze): severity-scored Hate/Sexual/SelfHarm/Violence detection. |

## Judge

The Judge block evaluates a request before it reaches the LLM, in parallel with routing, so a request can be screened for policy or safety concerns without adding sequential latency. Reach for it when you need judgment calls beyond what a fixed regex pattern or a narrow classifier can make: standard content-moderation categories, or your own custom criteria.

- Builtin Content Moderation covers standard categories (hate, violence, sexual content, etc.) as a single classification call with nothing to write; it’s enough for most surfaces.

- Custom, domain-specific judgment calls for User-Defined LLM Judge instead: you write the criteria via Strictness, Focus Areas, and Custom Instructions, and a full LLM reasons about the request against them.

- Start Confidence Threshold around 0.7–0.8, then adjust from there: raise it if legitimate requests are getting rejected on borderline calls, lower it if clearly bad requests are slipping through as low-confidence approvals.

- For a reasoning model (e.g. Qwen3) that exhausts its token budget on reasoning before producing a decision, turn on Disable Thinking / Chain-of-Thought. Left off, the system already auto-retries with thinking disabled once that happens.

| Field | What it does | Default |
| Enabled | Master switch, as a toggle. | Off |
| Moderation Mode | Shown as a two-button toggle headed “Moderation Mode”: Builtin Content Moderation (uses the OpenAI Moderations API for content classification) vs User-Defined LLM Judge (uses a Chat Completions LLM with a custom system prompt), not a field literally labelled “Mode”. | Builtin Content Moderation |
| LLM Target | Provider/model/endpoint for the Judge call. Not shown under a single “LLM Target” label. Rendered as a “Moderation Provider” (builtin mode) or “Judge LLM Provider” (LLM mode) card with Provider / Endpoint / Model / API Key Secret / Timeout / Model Parameters controls (see the target fields below). | Not set |
| Generated System Prompt | System prompt (LLM mode only). Shown as an editable, auto-regenerated preview headed “Generated System Prompt” with a “Preview” badge. Edits are overwritten when the Strictness/Focus Areas/Confidence/Custom Instructions controls change. | Empty |
| Strictness Level | Used for prompt generation. Shown as a Strict/Balanced/Lenient button group headed “Strictness Level”. | Not set |
| Focus Areas | Injected into the system prompt. | None added |
| Confidence Threshold | Reject decisions below this confidence (0.0–1.0) are treated as approvals. | Not set |
| Custom Instructions | Custom domain-specific instructions appended to the system prompt. | Not set |
| Disable Thinking / Chain-of-Thought | Disable chain-of-thought for a reasoning model, as a checkbox: injects /no_think into the system prompt and adds "think": false to Ollama requests. Dashboard checkbox label is “Disable Thinking / Chain-of-Thought”. | Off |
| Rules | Rejection rules, evaluated in order; the first match triggers rejection. No match ⇒ approved. Not directly editable as a list. The dashboard synthesizes the rule set from higher-level controls: “Moderation Categories” (builtin mode) or “Decision Evaluation” (LLM mode), combined with the “Custom Rejection Response” card below. | None |

A judge rule’s name is generated automatically from whichever higher-level control produced it (a builtin-mode rule is named “Content flagged” or “Category: <cat>”; an LLM-mode rule “JSON field rejection” or “Regex rejection”), so you configure Moderation Categories or Decision Evaluation above rather than a rule list directly. The rejection response itself comes from the “Custom Rejection Response” card: Status Code (default 403), Response Body, Content-Type (default application/json).

Provider/model/endpoint target fields (shared by Judge, Jury, Decider, failover, and load-balancing targets):

| Field | What it does | Default |
| Provider | e.g. OpenAI, Anthropic, Google Gemini, picked from a dropdown. Labelled “Provider” in the dashboard, not “Provider Type”. | Not set |
| Model | Model identifier. | Not set |
| Endpoint | API endpoint URL. There is no free-text “Endpoint URL” field anywhere in the dashboard: the Judge panel shows a read-only “Endpoint (auto-selected)” field displaying the resolved endpoint’s name (derived from provider + mode); the Juror panel shows an “Endpoint *” dropdown of the provider’s known endpoints via the shared LLM provider selector. Both write the resolved path/URL to this field on the wire. | Not set |
| API Key Secret | Secret ID for the API key. | Not set |
| Model Parameters | Provider-specific model parameters (temperature, max_tokens, …). Shown as a “Model Parameters” section (LLM-mode only for Judge) with per-parameter sliders/inputs (e.g. Temperature, Max Tokens, Top P), not a field literally labelled “Parameters”. | Not set |
| Timeout (Seconds) | Request timeout. | 30 seconds |

## Jury

The Jury block evaluates the LLM’s response before it returns to the caller. It’s the mirror of Judge on the way out, catching problems that only reveal themselves once the model has generated an answer (a policy violation, an unwanted claim, whatever your jurors are built to catch).

- parallel, the default, is the strictest bar: every juror must approve independently, and since they run concurrently it costs no extra latency over the slowest one.

- serial exits early on the first rejection, so order cheaper or faster jurors first.

- majority trades some strictness for resilience against a single juror’s false positive: more than half approving is enough.

- Redrive Policy defaults to no retries. Change it only if the model should get a chance to fix its own answer using the Jury’s rejection reason, since each redrive step costs another full model call.

| Field | What it does | Default |
| Enabled | Master switch, as a toggle. | Off |
| Execution Mode | How jurors are executed (below). Dashboard label is “Execution mode”, not “Mode”. | Parallel |
| Jurors | One or more juror definitions. Shown as “Jurors (N)” with the roster list and an “Add juror” button. | None added |
| Redrive Policy | Maximum retry loops when the Jury rejects the response: the Jury rejection reason is appended to the conversation and the LLM is called again, up to this many times. Shown as a “Redrive policy” dropdown with preset options (“Return verdict immediately”, “Redrive once/twice/three times with jury feedback”) rather than a free number field. | Return verdict immediately (no retries) |
| Redrive Prompt Template | Prompt template for the retry message. Use {reason} as a placeholder for the Jury’s rejection reason. Shown only when redrive policy is set above “Return verdict immediately,” labelled “Redrive prompt template”. | A built-in template |
| Final Negative-Verdict Response | Custom rejection response after all retries are exhausted. Section is headed “Final negative-verdict response”, not “Rejection”. | Not set |

Execution modes:

| Value | Behaviour |
| parallel (default) | All jurors run in parallel; all must approve. |
| serial | Jurors run sequentially; stops on the first rejection. |
| majority | All jurors run in parallel; more than half must approve. |

Each juror:

| Field | What it does | Default |
| Enabled | Whether this juror participates, as a toggle. A disabled juror keeps its configuration but is skipped at runtime. | On |
| Name | Human-readable name. Labelled “Name” in the juror’s sidebar panel; the full-area juror editor instead labels the same field “Juror Name”. | Not set |
| LLM Target | Provider/model/endpoint for this juror. Shown under an “LLM Configuration” heading with the same Provider / Endpoint (dropdown) / Model / API Key Secret / Timeout (seconds) / Model Parameters controls described above. | Not set |
| View Generated System Prompt | System prompt for this juror. Unlike Judge’s always-visible “Generated System Prompt” preview, the juror editor hides this behind a collapsible <details> labelled “View Generated System Prompt”. | Not set |
| Decision Evaluation | How to interpret this juror’s response (below). Section heading is “Decision Evaluation”, not plain “Decision”. | JSON mode |
| Strictness Level, Focus Areas, Confidence Threshold, Custom Instructions, Disable Thinking | Same meaning as the equivalent Judge fields, and rendered with the same section headings (“Strictness Level” / “Focus Areas” / “Confidence Threshold” / “Custom Instructions”), except Disable Thinking, whose Juror checkbox is labelled plainly “Disable Thinking”. | Same as the Judge equivalents |

Decision evaluation modes:

| Value | Fields | Approval rule |
| json (default) | JSON Field Name (default “decision”) and Approve Value (default “approve”). | The named JSON field on the juror’s response equals the Approve Value. |
| regex | Approve Pattern and Reject Pattern (optional, checked first). | Response text matches the Approve Pattern (and does not match the Reject Pattern, when set). |

The Jury’s own version of this override, under its Final negative-verdict response heading:

| Field | Default |
| HTTP status code | 403 |
| Response body | A JSON jury_violation error |
| Content-Type | application/json |

## Related

- [Guardrails concept](/products/affinidi-trust-fabric/agent-stream/concepts/guardrails.md): The four-layer model, fail-closed behaviour, and the rejection taxonomy.

- [PII protection and NER IDs](/products/affinidi-trust-fabric/agent-stream/concepts/pii-protection.md): How masking and pseudonymization work conceptually.

- [Routing and variants](/products/affinidi-trust-fabric/agent-stream/reference/surfaces/routing-and-variants.md): The Decider, which shares the same provider/model/endpoint target shape used here.

- [OPA policies](/products/affinidi-trust-fabric/agent-stream/reference/policies/opa-policies.md): Access-control decisions that run alongside these content guardrails.

- [Core configuration](/products/affinidi-trust-fabric/agent-stream/reference/surfaces/surface-reference.md): Where Prompt Guard, Expert Witnesses, Judge, and Jury sit among a surface’s other optional configuration blocks.
