- Request stage — before the request is forwarded to the upstream model.
- Response stage — after the model responds, before the reply reaches your app.
The decision contract
A policy returns a singledecision 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 yourpackageline and re-packages the rule under a system namespace, so the package name you choose does not matter. actionis required;ruleis a short identifier recorded on the trace. Any other key (includingreason) is ignored by the runtime.- Multiple complete
decisionrules 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 oninput.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 inredact_fields:
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. Redaction is not applied mid-stream; a streamed response that matches a redact_response rule is passed through unredacted and the event is recorded.
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 proxy’s model to be on an allowlist
input.model is the model configured on the proxy, so this rule guards against a proxy pointed at an unapproved model rather than a client choosing one.
Cap output tokens (request stage)
Block a refusal in the reply (response stage)
What a block looks like to your app
A blocked request returns403. The runtime uses the gateway error envelope:
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 request-stage rule (or the intent guardrail) escalates:
- The request is blocked with
403(request escalated for human review). - An escalation signal is recorded, carrying the proxy, score, and matched rule.
- Any alert rule watching for escalations fires (see Alerts).