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 uses priority: 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, applying sanitizePII() from src/lib/piiSanitizer.ts to every string value.
  • Response-side masking: The maskResponsesOutput() method sanitizes the output_text and output fields using sanitizePIIResponse() 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=true to activate the guardrail globally.
  • Mode configuration: Use INPUT_SANITIZER_MODE=redact to 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:

  1. Sanitizes the payload using sanitizeRequest() from src/shared/utils/inputSanitizer.ts.
  2. Extracts raw message text via extractMessageContents().
  3. Scans the first 16 KB (MAX_INJECTION_SCAN_BYTES) of content for patterns matching DEFAULT_GUARD_PATTERNS or custom patterns supplied via options.customPatterns.
  4. 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:

  1. Extracts image parts via extractImageParts() from src/lib/guardrails/visionBridgeHelpers.ts.
  2. Limits concurrent images to the configured maxImages setting.
  3. Calls the vision model via callVisionModel() for each image in parallel using Promise.allSettled.
  4. 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.ts control global enablement.
  • Defaults: Constants in src/shared/constants/visionBridgeDefaults.ts define maxImages and 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-bridge to 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

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →