Guardrails
A guardrail is a check that runs against every chat request before the provider is called. PromptGate ships four built-in guardrails and a 4-level inheritance model for configuring them.
The built-ins
Section titled “The built-ins”Content guardrails (text-level, run pre- / post-provider):
| Guardrail | What it does | Modes |
|---|---|---|
| PII Filter | Detects emails, IBANs, credit cards, SSNs, phone numbers, IPs, custom regexes — plus optional LLM-based contextual detection of person names and addresses | mask / block |
| Prompt Injection | Scans for known jailbreak / instruction-override patterns | block |
| Keyword Blocklist | Project-defined word/phrase list | block |
| Content Length | Min / max input length cap | block (rejects with 422) |
| Reversible Redaction | Tokenise PII before the LLM call, restore on the way back | egress |
| Secret Scanner | Block / redact 18 well-known credential patterns (AWS / GitHub / Slack / OpenAI / …) | block / redact |
Policy guardrails (context-level, run at auth middleware time):
| Guardrail | What it does | Block status |
|---|---|---|
| IP Allowlist | Reject the request when the client IP doesn’t match any configured CIDR. IPv4 + IPv6 via Symfony’s IpUtils::checkIp |
403 |
| Time Window | Reject outside the configured weekly hour windows (per-day, with timezone, overnight wrap-around supported) | 403 |
Policy guardrails implement the same GuardrailContract (for registry / UI / cascade) plus the dedicated PolicyGuardrailContract (request-level precheck() instead of text-level process()). The auth middleware calls GuardrailService::runPolicy() right after resolving the token — no message content needed.
4-level inheritance
Section titled “4-level inheritance”Guardrail rules are defined at four scopes. They merge from broadest to narrowest:
Global (admin → Guardrails) ↓Project (project sidebar → Guardrails) ↓Endpoint (endpoint wizard → Guardrails tab) ↓Token (API token detail → Guardrails tab)For each guardrail key (e.g. pii_filter):
- Global config is the default.
- Project config overrides global per field. Setting
pii_filter.enabled = falseat project scope turns it off everywhere in that project, even if global has it on. - Endpoint config overrides project per field.
- Token config overrides everything per field — useful for multi-tenant patterns where one bearer token = one of your end-users and they each need different strictness (
tenant-Aneeds PII redaction,tenant-Bis allowed PII because they signed a DPA, etc.).
Merging is per field — within a guardrail’s config object, only the keys present at a level override; the rest cascade up. Same semantics as token-level rate-limits and budgets (per-field override, never min(), never both — a permissive token can grant more headroom, a strict one tightens it).
Where guardrails run
Section titled “Where guardrails run”In the AI Gateway / AI Wrapper request pipeline:
1. Auth + scope check2. Rate limit3. Budget check4. >>> Guardrails <<< ← here5. Input schema validation6. Prompt apply7. Provider call8. Output schema validation9. LogGuardrails run on the concatenated content of every message in the request (concatenated with \n). They do not see the system prompt that PromptGate prepends afterwards.
If a guardrail throws (block mode), the request is rejected with 422 before any provider work happens — no tokens, no cost.
The UI
Section titled “The UI”Project Guardrails
Section titled “Project Guardrails”Project sidebar → Guardrails. Shows a card per guardrail key with:
- The current effective state (enabled / disabled, mode, types, words, etc.)
- Source —
Inherited(from global) /Project(locally configured) - Configure / Toggle actions
Global Guardrails
Section titled “Global Guardrails”Top-right user menu → Guardrails. Same UI, but configures the gateway-wide defaults that every project inherits unless overridden.
Live-save
Section titled “Live-save”Toggling a guardrail on/off persists immediately via AJAX (POST /projects/{project}/guardrails/policy). A toast confirms the save. No “Save” button.
The configure modal also persists on save — you tweak the JSON-ish form, click apply, the rule is written back. No reload.
Configuration storage
Section titled “Configuration storage”Rules live in guardrail_configs:
id | scope ('global' | 'project' | 'endpoint') | scope_id (project_id or endpoint_id; null for global) | rules (json: { "pii_filter": {...}, "prompt_injection": {...}, ... })One row per scope. The rules JSON is a map keyed by guardrail key, each value being whatever shape the guardrail expects.
Behaviour summary
Section titled “Behaviour summary”| Configuration | What happens |
|---|---|
enabled: false |
Guardrail skipped entirely. |
enabled: true, mode: "mask" (PII only) |
Runs, redacts matched substrings with [<TYPE> REDACTED], request continues. |
enabled: true, mode: "block" |
Runs, throws 422 on first match — request rejected. |
| Custom config (PII custom_patterns, blocklist words, etc.) | Merged with built-ins. |
The 422 response body:
{ "ok": false, "error": "Request blocked: E-Mail detected in input."}Code is the HTTP status; the message names which guardrail / which rule fired.
Per-page reference
Section titled “Per-page reference”- PII Filter — full type list, regex patterns, custom-regex tester, contextual LLM mode.
- Prompt Injection — pattern catalogue, false-positive notes.
- Keyword Blocklist — case-folding, word vs substring matching.
- Content Length — min/max behaviour, multi-byte handling.
Adding a custom guardrail
Section titled “Adding a custom guardrail”Implement the GuardrailContract interface:
namespace App\Services\Guardrails;
interface GuardrailContract{ public function key(): string; public function label(): string; public function description(): string; public function process(string $text, array $config): string;}process() receives the concatenated message text and the rule config. Either:
- Return the (possibly modified) text — request continues.
- Throw
RuntimeException(..., 422)— request blocked.
Register in GuardrailService::__construct:
$this->register(new MyCustomGuardrail());A first-class plugin path is on the roadmap (see Plugins).
Next: PII Filter — the most-used guardrail.
© Akyros Labs LLC. All rights reserved.