Skip to main content
Xenovia evaluates Open Policy Agent (OPA) Rego policies on every request to an agent. Policies are created in the control plane, attached to an agent, and enabled. Each agent can run policies at two stages:
  • Request stage — before the request is forwarded to the upstream model.
  • Response stage — after the model responds, before the reply reaches your app.
Enabled policies are compiled to a per-agent snapshot, pushed to the runtime, and revalidated by each runtime instance about every five minutes. OPA evaluates with a 2-second compile timeout and a 200-millisecond per-stage evaluation timeout.

The decision contract

A policy returns a single decision object. This is the only value the runtime reads — allow/deny rules or a bare redact_fields set are ignored.
  • Write package xenovia. The control plane strips your package line and re-packages the rule under a system namespace, so the package name you choose does not matter.
  • action is required; rule is a short identifier recorded on the trace. Any other key (including reason) is ignored by the runtime.
  • Multiple complete decision rules must have mutually exclusive conditions. If two can be true at once, OPA reports a conflict and the request fails closed. Guard each rule with distinct conditions (for example on input.agent_role).
The Rego is compiled with opa check --strict when you save it. Compilation errors are returned as 422 with the OPA diagnostics.

Actions

When several enabled policies run at the same stage, the strongest action wins: block over escalate over redact over allow (and block_response over redact_response at the response stage). An unrecognised action ranks below allow and can never block.

Redaction

Return the labels to strip in redact_fields:
Supported labels: email, ssn, phone, creditcard (Luhn-validated), ip, url. Redaction applies to message text, tool descriptions, and the Responses instructions field at the request stage, and to the reply text at the response stage. Streamed replies are buffered and checked before any content is sent, so response redaction applies to them too.

Monitor vs enforce

Enforcement is a property of the policy, set in the dashboard (Monitor or Enforced) or through the API, not something you express in Rego:
  • Enforced policies apply their action.
  • Monitor policies are evaluated and their would-be decision is recorded on the trace, but the action is never applied. Use monitor mode to trial a policy against live traffic before enforcing it.

Policy input

Both stages receive the same base fields; each stage adds its own.

Base fields (both stages)

Request stage adds

Response stage adds

Examples

Block a tool

Require the agent’s model to be on an allowlist

input.model is the model configured on the agent, so this rule guards against an agent routed to an unapproved model rather than a client choosing one.

Cap output tokens (request stage)

Block a refusal in the reply (response stage)

Templates

Instantiate a shipped template with its parameters instead of writing Rego. Thirteen templates were removed because the runtime never produces the inputs they read, or because they always escalated: pii-pattern-block, rapid-call-rate-block, repeat-denial-escalation, anomalous-tool-pattern-escalation, tenant-boundary-match, external-disclosure-consent-gate, block-shadow-model-fallback, low-confidence-escalation, high-risk-tool-human-approval, financial-threshold-escalation, eu-ai-act-fundamental-rights-review, destructive-tool-confirmation, and workflow-intent-aggregate-escalate. Policies you already created from them are unchanged; review whether they can still fire.

Guardrails

Guardrails replace the former intent check. A guardrail is a named check that Xenovia’s guardrail model evaluates at one or more stages (model_request, model_response, tool_call, tool_result, memory_write, context_summary), with an action (allow, block, redact, or escalate) and, like a policy, Enabled and Monitor or Enforce. The agent’s scope guardrail (kind scope) describes in plain language what the agent is for; requests outside it are flagged. A guardrail can be limited to one agent role. tool_result, memory_write, and context_summary guardrails run only when your code sends those stages with the Python SDK.

What a block looks like to your app

A blocked request returns 403. The runtime uses the gateway error envelope:
The OpenAI SDK raises this as PermissionDeniedError. The message is deliberately generic; the rule that fired is recorded on the trace, not returned to the caller. The X-Xenovia-Trace-Id response header identifies the trace.

Escalations

escalate is for actions you want flagged for human attention. When a policy escalates:
  1. The call is blocked with 403: a request-stage escalation returns request blocked by policy, and a response-stage escalation suppresses the reply with response blocked by policy.
  2. An escalation is recorded, carrying the agent and matched rule.
  3. Any alert rule watching for escalations fires (see Alerts).
A guardrail with action escalate records an escalation the same way but lets the request proceed.
There is no approve/deny queue today. A policy escalation blocks the call and raises a signal; a guardrail escalation raises the signal and lets the call proceed. Neither pauses the request for an operator to release. Review escalations in Sessions and in Alerts, resolve them with a note, then adjust the policy or guardrail. Reasons are recorded server-side and are never returned to the agent.