How the Domain Policy Engine in OmniRoute Governs Routing Decisions Independently

The domain policy engine in OmniRoute operates as a centralized, declarative decision-making component that evaluates API requests against lockout, budget, and fallback policies before any provider routing occurs, ensuring complete separation from the routing-combo and executor logic.

The domain policy engine in the OmniRoute repository provides self-contained governance over how AI model requests are processed and routed. Unlike traditional systems that scatter policy logic across transport layers, this engine sits entirely within the domain layer at src/domain/policyEngine.ts, deterministically evaluating whether requests may proceed, which providers are preferred, and what constraints apply—all before network I/O begins.

Core Architecture of the Domain Policy Engine

The engine follows a strict evaluation pipeline that processes requests through normalization, validation, and policy resolution stages.

Request Normalization and PolicyRequest Construction

Every incoming API call is first normalized into a PolicyRequest object containing the model name, API-key ID, client IP, and optional provider preference. According to the OmniRoute source code at src/domain/policyEngine.ts (lines 15‑21), this normalization ensures all subsequent evaluations operate on a consistent data structure regardless of the entry point.

Pre-Routing Validation Checks

Before policy evaluation begins, the engine executes mandatory lockout and budget validations. The checkLockout function inspects the client IP or identifier for active lockout states at lines 50‑61, immediately returning a denial verdict if the client is locked, as implemented in src/domain/lockoutPolicy.ts (lines 73‑94). Similarly, checkBudget verifies API-key quota limits at lines 63‑73, referencing budget constraints defined in src/domain/costRules.ts.

Fallback Chain Resolution

When primary models are unavailable, resolveFallbackChain builds a prioritized list of alternative models. As defined at lines 76‑86 in src/domain/policyEngine.ts, this function attaches the fallback chain to the verdict metadata, enabling downstream components to execute graceful degradation without re-evaluating policies.

Class-Based Policy Evaluation

The PolicyEngine class maintains an in-memory registry of Policy objects. When evaluate() is called (lines 101‑195), it:

  • Sorts enabled policies by priority value
  • Filters policies using glob matchers against model patterns
  • Executes actions based on policy type (routing, access, or budget)
  • Accumulates preferred providers, applied policies, and token limits into a comprehensive result

The final PolicyVerdict returned at lines 47‑88 and 133‑191 indicates whether the request is allowed, the denial reason if applicable, and any adjustments such as preferred provider lists or max-token overrides.

Architectural Independence from Routing Logic

The domain policy engine guarantees independence from the routing-combo and executor layers through several architectural constraints.

Pure Function Design for Side-Effect-Free Evaluation

All validation checks—including checkLockout, checkBudget, and resolveFallbackChain—are implemented as pure functions that do not trigger provider calls or external side effects. This functional isolation ensures that policy evaluation remains deterministic and testable without requiring network infrastructure.

Deterministic Verdicts Before Network I/O

The engine produces a definitive allowed boolean flag and structured reason codes before any routing logic executes or HTTP requests leave the system. This pre-network governance prevents wasted resources on requests that violate budget or lockout constraints.

Composable Policies Without Router Modification

New policies integrate via PolicyEngine.addPolicy() without touching the combo router. The engine automatically merges policies based on priority values, keeping routing logic untouched while allowing complex rule composition. This extensibility point at lines 101‑195 demonstrates how the engine accommodates custom business rules independently of transport concerns.

Separation of Concerns Across Domain Modules

Policy logic is modularized into dedicated domain files: lockout tracking lives in src/domain/lockoutPolicy.ts, budget enforcement in src/domain/costRules.ts, and fallback resolution in src/domain/fallbackPolicy.ts. This compartmentalization prevents policy-related bugs from leaking into the combo or executor layers, as each module handles a specific governance concern.

Implementing Policy Checks in Code

Developers interact with the policy engine through both functional and class-based APIs.

Evaluating a Single Request

Use the evaluateRequest function to check individual API calls against active policies:

import { evaluateRequest } from '@/domain/policyEngine';

const verdict = evaluateRequest({
  model: 'gpt-4o',
  apiKeyId: 'key_123',
  clientIp: '203.0.113.42',
});

if (!verdict.allowed) {
  console.log(`Denied (${verdict.policyPhase}): ${verdict.reason}`);
} else {
  console.log('Allowed – fallback chain:', verdict.adjustments.fallbackChain);
}

This function returns either a denial message indicating the failing phase (lockout or budget) or an allowed verdict with the computed fallback chain metadata.

Configuring Custom Policies with the PolicyEngine Class

For advanced scenarios, instantiate the PolicyEngine class to register custom routing policies:

import { PolicyEngine, Policy } from '@/domain/policyEngine';

const preferClaude: Policy = {
  id: 'p1',
  name: 'Prefer Claude for Claude‑compatible models',
  type: 'routing',
  enabled: true,
  priority: 10,
  conditions: { model_pattern: 'claude-*' },
  actions: { prefer_provider: ['anthropic'] },
};

const engine = new PolicyEngine();
engine.loadPolicies([preferClaude]);

const result = engine.evaluate({ model: 'claude-2.1' });

console.log(result.preferredProviders); // → ['anthropic']

The engine sorts policies by priority and applies glob matching to model patterns, returning preferred providers without executing network calls.

Bulk Evaluation for Model Selection

When multiple model alternatives exist, use evaluateFirstAllowed to find the first permissible option:

import { evaluateFirstAllowed } from '@/domain/policyEngine';

const models = ['gpt-4o', 'gpt-3.5-turbo', 'claude-2'];
const base = { apiKeyId: 'key_123', clientIp: '203.0.113.42' };

const { model, verdict } = evaluateFirstAllowed(models, base);
if (model) {
  console.log(`First allowed model: ${model}`);
} else {
  console.log(`All denied – reason: ${verdict.reason}`);
}

This utility evaluates models sequentially until finding one that passes all policy checks, optimizing for the first available valid option.

Summary

The domain policy engine in OmniRoute provides independent governance over routing decisions through these key characteristics:

  • Centralized evaluation at src/domain/policyEngine.ts processes all requests before they reach routing layers
  • Pure function architecture ensures deterministic, side-effect-free policy validation without network dependencies
  • Declarative policy composition allows new rules to be added via PolicyEngine.addPolicy() without modifying routing code
  • Modular domain structure separates lockout, budget, and fallback concerns into distinct modules
  • Pre-routing verdicts return definitive allow/deny decisions with reasons and metadata before any provider calls occur

Frequently Asked Questions

Why is the domain policy engine considered independent from the routing combo?

The engine operates entirely within the domain layer using pure functions that produce deterministic verdicts before any network I/O or routing logic executes. By residing in src/domain/policyEngine.ts and interacting with separate modules like src/domain/lockoutPolicy.ts and src/domain/costRules.ts, it maintains zero dependencies on the combo router or executor implementations, ensuring routing decisions respect policies without entangling the transport layer with business rules.

How does the engine handle budget and lockout checks?

The engine executes checkLockout (lines 50‑61) to inspect client IPs for active lockouts and checkBudget (lines 63‑73) to verify API-key quotas against definitions in src/domain/costRules.ts. Both checks occur immediately after request normalization and return denial verdicts with specific phase identifiers (lockout or budget) if constraints are violated, preventing the request from ever reaching provider selection logic.

Can custom policies be added without modifying the router?

Yes. The PolicyEngine class exposes loadPolicies() and addPolicy() methods that accept Policy objects defining conditions, actions, and priorities. These policies are evaluated at lines 101‑195 in src/domain/policyEngine.ts through a priority-sorted execution loop that requires no changes to the combo router or executor code, enabling business-specific routing rules to be injected purely through domain configuration.

What happens during fallback chain resolution?

When a requested model might be unavailable, resolveFallbackChain (lines 76‑86) constructs a prioritized list of alternative models based on domain rules in src/domain/fallbackPolicy.ts. This chain attaches to the PolicyVerdict as metadata, allowing downstream components to attempt subsequent models if the primary fails, all while maintaining the original policy constraints and without requiring re-evaluation of lockout or budget rules for each fallback attempt.

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 →