OmniRoute Guardrails Configuration: How to Secure PII, Block Prompt Injection, and Enable Vision Bridging
OmniRoute's guardrails are configured through the centralized registry in src/lib/guardrails/registry.ts, where each guardrail—PII-masker, prompt-injection, and vision-bridge—extends BaseGuardrail and implements preCall and postCall methods controlled via environment variables, database settings, and per-request headers.
The OmniRoute routing engine ships with a built-in guardrail framework that intercepts requests before they reach upstream providers and sanitizes responses before they return to clients. These guardrails are registered at server startup and consulted by the chat handler pipeline, providing configurable protection against data leakage, malicious inputs, and incompatible vision payloads. Understanding how to configure each guardrail ensures you can enforce security policies without compromising routing performance.
Understanding the Guardrail Architecture
All guardrails in OmniRoute inherit from a common abstract class and are managed through a singleton registry pattern.
The BaseGuardrail Abstract Class
Every guardrail extends the BaseGuardrail class defined in src/lib/guardrails/base.ts. This contract requires implementations to provide:
preCall(payload, context): Executed before the request is forwarded to the model provider; can modify the payload or block the request.postCall(response, context): Executed after receiving the upstream response; can modify the response content.- Priority levels: Lower numbers execute earlier (e.g., PII-masker uses
priority: 10, vision-bridge usespriority: 5).
Both methods return a GuardrailResult object containing block (boolean), modifiedPayload or modifiedResponse, and metadata for observability.
The Centralized Registry
The guardrailRegistry singleton in src/lib/guardrails/registry.ts maintains the ordered list of active guardrails. During server initialization in src/server-init.ts, the function registerDefaultGuardrails() instantiates the three built-in guardrails and inserts them into the registry. The chat handler in src/sse/handlers/chat.ts iterates over this registry for every request, executing preCall methods sequentially until a block occurs or all guardrails complete.
Configuring the PII-Masker Guardrail
The PII-masker guardrail redacts personally identifiable information from both requests and responses before they leave the OmniRoute infrastructure.
Implementation Details
Located in src/lib/guardrails/piiMasker.ts, the PIIMaskerGuardrail class implements:
- Request-side masking: The
cloneAndMaskRequestPayload()function walks the JSON payload recursively, applyingsanitizePII()fromsrc/lib/piiSanitizer.tsto every string value. - Response-side masking: The
maskResponsesOutput()method sanitizes theoutput_textandoutputfields usingsanitizePIIResponse()before streaming to the client.
// src/lib/guardrails/piiMasker.ts
export class PIIMaskerGuardrail extends BaseGuardrail {
async preCall(payload, _context) {
const result = cloneAndMaskRequestPayload(payload);
return { block: false, modifiedPayload: result.payload };
}
async postCall(response, _context) {
// sanitizes response fields
return { block: false, modifiedResponse: sanitized };
}
}
Configuration Options
Enable or disable PII redaction through these mechanisms:
- Environment variable: Set
PII_REDACTION_ENABLED=trueto activate the guardrail globally. - Mode configuration: Use
INPUT_SANITIZER_MODE=redactto specify redaction behavior (as opposed to other sanitization modes). - Default state: The guardrail is enabled by default with
priority: 10, ensuring it runs early in the pre-call chain.
Configuring the Prompt-Injection Guardrail
The prompt-injection guardrail detects malicious system-override attempts and other injection patterns in user inputs.
Detection Logic
The core detection routine evaluatePromptInjection() in src/lib/guardrails/promptInjection.ts performs the following steps:
- Sanitizes the payload using
sanitizeRequest()fromsrc/shared/utils/inputSanitizer.ts. - Extracts raw message text via
extractMessageContents(). - Scans the first 16 KB (
MAX_INJECTION_SCAN_BYTES) of content for patterns matchingDEFAULT_GUARD_PATTERNSor custom patterns supplied viaoptions.customPatterns. - Aggregates severity scores and compares against the configured
blockThreshold.
// src/lib/guardrails/promptInjection.ts
export function evaluatePromptInjection(body, options = {}, context = {}) {
const scanText = joinedContents.slice(0, MAX_INJECTION_SCAN_BYTES);
const customDetections = detectWithPatterns(scanText, patterns);
// Merges detections and determines block/warn/log action
}
The PromptInjectionGuardrail class wraps this logic, returning a block result when the threshold is exceeded.
Mode and Threshold Settings
Configure the guardrail's behavior through these parameters:
| Option | Default | Configuration Source |
|---|---|---|
mode |
"warn" |
Environment variable INJECTION_GUARD_MODE or database flag |
blockThreshold |
"high" |
getThreshold() function |
enabled |
true |
INPUT_SANITIZER_ENABLED environment variable |
Available modes:
block: Rejects the request with a 400-level error when patterns exceed the threshold.warn: Allows the request but logs the detection.log: Silently records detections without modifying the request flow.
Disabling Per Request
Send the header x-omniroute-disabled-guardrails: prompt-injection to bypass the guardrail for specific requests. The registry checks this header in src/lib/guardrails/registry.ts before executing any guardrail's preCall method.
Configuring the Vision-Bridge Guardrail
The vision-bridge guardrail enables non-vision models to process image inputs by converting images to text descriptions using a configured vision model.
How It Works
The VisionBridgeGuardrail in src/lib/guardrails/visionBridge.ts uses getComboVisionBridgeDecision() to analyze routing tables and determine if the selected model lacks vision capabilities. If all targets support vision, the guardrail skips processing to minimize latency.
When images require processing:
- Extracts image parts via
extractImageParts()fromsrc/lib/guardrails/visionBridgeHelpers.ts. - Limits concurrent images to the configured
maxImagessetting. - Calls the vision model via
callVisionModel()for each image in parallel usingPromise.allSettled. - Replaces image blocks with textual descriptions using
replaceImageParts().
// src/lib/guardrails/visionBridge.ts
if (imageParts.length === 0) return { block: false };
const results = await Promise.allSettled(
limitedParts.map(async (p, i) => {
const desc = await callVision(p.imageUrl, config);
return `[Image ${i + 1}]: ${desc}`;
})
);
return { block: false, modifiedPayload: modifiedBody, meta: { processed: results.length } };
Configuration Sources
- Database settings: Flags stored in
src/lib/db/settings.tscontrol global enablement. - Defaults: Constants in
src/shared/constants/visionBridgeDefaults.tsdefinemaxImagesand timeout values. - Priority: Executes with
priority: 5, running before the PII-masker but after any higher-priority custom guardrails. - Disable header: Use
x-omniroute-disabled-guardrails: vision-bridgeto skip processing for specific requests.
Integrating Guardrails into the Request Lifecycle
Guardrails are wired into the request flow through explicit registration and handler integration.
Server Initialization
In src/server-init.ts, the following code loads guardrails at startup:
// src/server-init.ts
import { registerDefaultGuardrails } from "./lib/guardrails";
registerDefaultGuardrails();
This function instantiates the PII-masker, prompt-injection, and vision-bridge guardrails and inserts them into the singleton registry.
Pipeline Execution
The chat handler in src/sse/handlers/chat.ts executes guardrails in two phases:
Pre-call phase:
// src/sse/handlers/chat.ts
for (const guardrail of guardrailRegistry.list()) {
const result = await guardrail.preCall(payload, context);
if (result.block) return earlyResponse(result);
if (result.modifiedPayload) payload = result.modifiedPayload;
}
Post-call phase:
After receiving the upstream response, the handler iterates through guardrails again, applying postCall() methods (notably the PII-masker) to sanitize output before streaming to the client.
Practical Configuration Examples
Disable prompt-injection for a single request:
curl -X POST http://localhost:3000/api/chat/completions \
-H "Content-Type: application/json" \
-H "x-omniroute-disabled-guardrails: prompt-injection" \
-d '{"model": "gpt-4", "messages": [{"role": "user", "content": "Test"}]}'
Send an image through a non-vision model (automatically triggers vision-bridge):
await fetch("/api/chat/completions", {
method: "POST",
body: JSON.stringify({
model: "gpt-4o-mini",
messages: [{
role: "user",
content: [{ type: "image_url", image_url: { url: "data:image/png;base64,..." } }]
}]
})
});
Summary
- OmniRoute guardrails extend
BaseGuardrailinsrc/lib/guardrails/base.tsand register viasrc/lib/guardrails/registry.tsto process requests in priority order. - PII-masker (
src/lib/guardrails/piiMasker.ts) is controlled byPII_REDACTION_ENABLEDand runs at priority 10 to sanitize both requests and responses. - Prompt-injection (
src/lib/guardrails/promptInjection.ts) scans the first 16 KB of input usingevaluatePromptInjection(), supportingblock,warn, andlogmodes viaINJECTION_GUARD_MODE. - Vision-bridge (
src/lib/guardrails/visionBridge.ts) automatically converts images to text when routing to non-vision models, respectingmaxImageslimits and DB configuration settings. - All guardrails can be disabled per request using the
x-omniroute-disabled-guardrailsheader, and the pipeline executes insrc/sse/handlers/chat.tsbefore and after upstream provider calls.
Frequently Asked Questions
How do I completely disable the PII-masker guardrail in OmniRoute?
Set the environment variable PII_REDACTION_ENABLED=false before starting the server. Alternatively, configure INPUT_SANITIZER_MODE to a value other than redact (e.g., validate or off) depending on your sanitization requirements. Since the guardrail checks these flags in its constructor and preCall method, changes require a server restart to take effect.
What is the maximum payload size scanned for prompt injection attacks?
OmniRoute limits prompt injection scanning to the first 16 KB (MAX_INJECTION_SCAN_BYTES) of extracted message content. This prevents performance degradation on large payloads while catching attacks that typically appear in the initial system prompt or user message. If your use case requires scanning larger contexts, you must modify the constant in src/lib/guardrails/promptInjection.ts and rebuild the application.
Can I use the vision-bridge with custom vision models instead of the default?
Yes. The callVisionModel() function in src/lib/guardrails/visionBridge.ts resolves the vision model endpoint from database settings (src/lib/db/settings.ts) and constants in src/shared/constants/visionBridgeDefaults.ts. Configure your custom vision model credentials and endpoint in the settings table, and the guardrail will route image processing through your specified model before replacing image blocks with descriptions in the main request payload.
How are guardrail results reflected in API responses?
When a guardrail modifies a payload or response, the modifiedPayload or modifiedResponse fields are passed downstream. For observability, the chat handler includes guardrail metadata in the response headers or body (depending on your configuration), indicating which guardrails executed, whether they modified content, and detection counts (e.g., PII entities redacted). Blocked requests return a 400-level error with a message generated by the blocking guardrail, such as "Request rejected: suspicious content detected" from the prompt-injection guard.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →