Skip to main content

Policy engine

Xenovia uses Open Policy Agent (OPA) to evaluate Rego policies. Policies are attached per proxy and evaluated synchronously on every request. There are two independent policies per proxy:
  • Request-stage policy — evaluated before the request is forwarded to the upstream LLM.
  • Response-stage policy — evaluated after the LLM responds, before the response is returned to the agent.
Policies are fetched from the control plane and cached in Redis for 5 minutes. Compile results are cached by (proxy_id, policy_hash). Eval timeout is 200ms; compile timeout is 2s.

Decision actions

Request stage

Response stage

For redact and redact_response, specify which fields to redact with the redact_fields set. Supported patterns:

Policy input schema

Both request-stage and response-stage policies receive the same base input. The policy engine exposes input with the following fields:

Request-stage input

Response-stage additional fields

Writing policies

Policies are written in Rego under the package xenovia.policy. The policy engine reads the deny set and the redact_fields set.

Minimal allow-all policy

Block a specific tool

Require approval for production writes

Redact PII from requests

Block based on intent score

Response-stage: block low-confidence outputs

Allowlist specific models

Policy blocks in your application

When a request is blocked, the runtime returns 403 Forbidden. The response body follows the standard error format:
The OpenAI SDK raises this as PermissionDeniedError. The X-Xenovia-Trace-Id header is present on blocked responses — use it to look up the full trace.

Approval workflow

For actions that require human review rather than automatic blocking, use the intent plugin’s escalate action (configured per proxy in the platform). When an escalation fires:
  1. The request is blocked with 403.
  2. An async notification is sent to the control plane with the full request context.
  3. Operators review the trace — including the session, turn number, messages, tools, and intent score — and approve or deny.
The escalation reason is logged server-side only and is not returned to the agent. A bounded pool of 32 goroutines handles escalation notifications to prevent backpressure during traffic spikes.