Skip to content

Policy Engine Architecture

Omnia’s policy engine provides guardrails for AI agents at two distinct enforcement layers. This document explains why policies exist, how the two policy types differ, and how context flows through the system to enable fine-grained access control.

AI agents can invoke tools, call LLM providers, and act on behalf of users. Without guardrails, an agent could:

  • Call tools it shouldn’t have access to
  • Exceed cost or usage limits
  • Act without knowing who the end user is
  • Bypass compliance requirements

Policies solve this by introducing declarative, Kubernetes-native access control that operators configure once and the platform enforces automatically.

Omnia separates policy enforcement into two layers, each with a distinct enforcement mechanism:

graph TB
subgraph "Network Layer"
AP[AgentPolicy] -->|Generates| IAP[Istio AuthorizationPolicy]
IAP -->|Enforces at| SIDECAR[Envoy Sidecar]
end
subgraph "Application Layer"
TP[ToolPolicy] -->|Evaluates via| PROXY[Policy Proxy Sidecar]
PROXY -->|CEL Rules| UPSTREAM[Tool Service]
end
CLIENT[Client Request] --> SIDECAR
SIDECAR -->|Allowed| FACADE[Facade]
FACADE -->|Tool Call| PROXY
PROXY -->|Allowed| UPSTREAM

AgentPolicy operates at the Istio/Envoy level. The operator controller translates each AgentPolicy into an Istio AuthorizationPolicy, which Envoy enforces before the request reaches the application. This provides:

  • Tool allowlist/denylist — restrict which tool registries and tools an agent can invoke
  • JWT claim mapping — extract claims from the user’s JWT and propagate them as X-Omnia-Claim-* headers
  • Enforcement modesenforce blocks violations; permissive logs without blocking

Because enforcement happens at the network level, there is no application code to bypass.

ToolPolicy (Application-Level, Enterprise)

Section titled “ToolPolicy (Application-Level, Enterprise)”

ToolPolicy operates at the application level via a policy proxy sidecar deployed alongside each tool service. It provides:

  • CEL deny rules — evaluate request headers and body using Common Expression Language expressions
  • Required claims — verify that specific JWT claims are present before allowing the request
  • Header injection — add static or CEL-computed headers to the upstream request
  • Audit logging — structured logs for every policy decision with optional field redaction

ToolPolicy is an Enterprise feature.

For policies to make decisions based on who is calling and what they’re calling, identity and request context must flow through every service boundary:

sequenceDiagram
participant Client
participant Istio as Istio Sidecar
participant Facade
participant Runtime
participant Proxy as Policy Proxy
participant Tool as Tool Service
Client->>Istio: WebSocket + JWT
Istio->>Istio: Validate JWT, extract claims
Istio->>Facade: Forward + x-user-id, x-user-roles, x-user-email
Facade->>Facade: Build PropagationFields from headers
Facade->>Runtime: gRPC + X-Omnia-* metadata
Runtime->>Proxy: HTTP + X-Omnia-* headers
Proxy->>Proxy: Evaluate CEL rules against headers + body
Proxy->>Tool: Forward + injected headers

The following headers are propagated across service boundaries:

HeaderSourceDescription
x-omnia-agent-nameFacadeName of the AgentRuntime
x-omnia-namespaceFacadeKubernetes namespace
x-omnia-session-idFacadeCurrent session identifier
x-omnia-request-idFacadePer-request trace identifier
x-omnia-user-idIstioAuthenticated user identity
x-omnia-user-rolesIstioComma-separated user roles
x-omnia-user-emailIstioUser email address
x-omnia-providerRuntimeLLM provider type
x-omnia-modelRuntimeLLM model name
x-omnia-tool-nameRuntimeTool being invoked
x-omnia-tool-registryRuntimeToolRegistry containing the tool
x-omnia-claim-*AgentPolicyMapped JWT claims (e.g., x-omnia-claim-team)
x-omnia-param-*RuntimePromoted scalar tool parameters

AgentPolicy’s claimMapping section controls which JWT claims are extracted and forwarded:

claimMapping:
forwardClaims:
- claim: team
header: X-Omnia-Claim-Team
- claim: org.region
header: X-Omnia-Claim-Region

Claims support dot-notation for nested values (e.g., org.region extracts {"org": {"region": "us-east"}}). Headers must use the X-Omnia-Claim- prefix, enforced by CRD validation.

Both policy types support a mode that controls whether violations are blocked or only logged:

Policy TypeEnforce ModePermissive/Audit Mode
AgentPolicyenforce — Istio blocks the requestpermissive — Istio allows but logs
ToolPolicyenforce — proxy returns 403audit — proxy allows but logs the would-deny decision

Both policy types also support onFailure to control what happens when policy evaluation itself fails (e.g., a CEL expression error):

  • deny (default) — treat evaluation failures as denials
  • allow — permit the request despite the error

The policy proxy emits structured JSON logs for every deny decision and, when audit mode is active, for would-deny decisions:

{
"msg": "policy_decision",
"decision": "deny",
"wouldDeny": true,
"mode": "audit",
"policy": "refund-limits",
"rule": "max-refund-amount",
"message": "Refund amount exceeds $500 limit",
"path": "/v1/refund",
"method": "POST"
}

ToolPolicy’s audit.redactFields option allows sensitive field names to be masked in log output.

The ToolPolicy proxy runs as a sidecar container in the tool service pod. It intercepts HTTP requests, evaluates CEL rules, and either forwards or denies:

graph LR
subgraph "Tool Pod"
PROXY[Policy Proxy :8443] -->|Forward| TOOL[Tool Service :8080]
end
RUNTIME[Runtime] -->|HTTP| PROXY
PROXY -->|403 Denied| RUNTIME
subgraph "Control Plane"
CTRL[Operator Controller] -->|Watch| TP[ToolPolicy CRD]
CTRL -->|Configure| PROXY
end

The proxy:

  1. Receives the request with all X-Omnia-* headers
  2. Checks required claims
  3. Evaluates CEL deny rules in order (first match stops)
  4. If allowed, evaluates header injection rules and adds computed headers
  5. Forwards to the upstream tool service