Skip to main content
Version: Latest (1.8.x dev)

Enforce AI policy as code

AI policy should make the same decision on every request, no matter which application sends it. DVARA evaluates policy before provider dispatch, refuses calls that break a rule, and records the decision.

This guide takes you from one working Open Source rule to a safe Enterprise rollout. Use the policy condition reference when you need every supported condition and its exact matching behaviour.

Choose the distribution

The YAML language is shared. Storage, lifecycle, and the available conditions differ.

CapabilityOpen SourceEnterprise platform
Model allowlist and denylistYesYes
Maximum requested output tokensYesYes
Tool allowlist and denylistYesYes
Active policy enforcementYesYes
Draft, Shadow, Archived, dry-run, version history, and rollbackNoYes
Data-residency, time-of-day, and budget conditionsNoYes
MCP server, tool, and argument conditionsNoYes, when the licensed MCP plane is active
CEL expressionsNoYes
Central editing and fleet distribution through FlightdeckNoYes

Open Source reads Active policies from gateway.yaml at startup. Restart the process after changing the file. The Enterprise platform stores and versions policies centrally, then sends changes to the data plane without a restart.

Enterprise LLM policy management works in the Development posture without a licence. A licence grants production rights and support and activates the MCP and A2A planes.

Block an unapproved model

Complete the Open Source quickstart, then add this global policy to gateway.yaml:

policies:
- id: approved-models-only
status: ACTIVE
dsl: |
version: "1"
rules:
- id: deny-legacy-model
description: Do not send new work to the retired model.
priority: 10
conditions:
model:
denylist: [mock/legacy]
action: DENY
deny_message: "This model is not approved. Use mock/approved instead."

The policy has no workspace, so it applies to every request. Add workspace: <workspace-id> beside id to limit it to one workspace. Restart the Open Source runtime after saving the file.

Send an allowed request:

curl -s http://localhost:8080/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "mock/approved",
"messages": [
{"role": "user", "content": "Explain this policy in one sentence."}
]
}'

The Open Source quickstart defaults require-api-key to false, so this local request intentionally has no Authorization header. Require and send a DVARA API key in shared environments.

The Mock provider returns a normal Chat Completions response:

{
"id": "mock-6d0d5f08d3dd4aa5b536ba765cb0be63",
"object": "chat.completion",
"created": 1789092000,
"model": "mock/approved",
"choices": [{
"index": 0,
"message": {"role": "assistant", "content": "This is a mock response"},
"finish_reason": "stop"
}],
"usage": {"prompt_tokens": 12, "completion_tokens": 5, "total_tokens": 17}
}

Now request the blocked model:

curl -s -w '\nHTTP %{http_code}\n' \
http://localhost:8080/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "mock/legacy",
"messages": [{"role": "user", "content": "Write a customer reply."}]
}'

DVARA stops the call before provider dispatch:

{
"error": {
"message": "This model is not approved. Use mock/approved instead.",
"type": "policy_violation",
"code": "policy_denied",
"trace_id": "01K4V5R9X21MDN8W5A6E7ZQ3TC"
}
}
HTTP 403

The trace ID changes on every request. The caller sees deny_message, so make it explain the safe alternative.

Verify the decision

When DVARA_AUDIT_FILE_PATH is configured, find the denial in the local JSONL audit file:

jq 'select(.eventType == "POLICY_DENIED") |
{eventType, workspaceId, policy: .payload.policy_id,
rule: .payload.rule_id, reason: .payload.reason}' \
var/audit.jsonl

The event identifies the policy and rule that stopped the call:

{
"eventType": "POLICY_DENIED",
"workspaceId": null,
"policy": "approved-models-only",
"rule": "deny-legacy-model",
"reason": "This model is not approved. Use mock/approved instead."
}

workspaceId is null because the example is global and the local request is anonymous. A workspace-scoped request carries the resolved workspace. The full event also carries the sequence, previous hash, HMAC, and signing time, making edits, removals, and reordering detectable.

Add request limits and tool controls

Open Source and Enterprise both support max_tokens and tools. Keep them in separate rules so the reason tells the caller which control failed.

version: "1"
rules:
- id: cap-output-size
priority: 20
conditions:
max_tokens:
limit: 2048
action: DENY
deny_message: "Request at most 2,048 output tokens."

- id: allow-read-only-tools
priority: 30
conditions:
tools:
allowlist: [search_catalog, get_order_status]
action: DENY
deny_message: "This application may offer only approved read-only tools."

The token rule matches only when the request supplies max_tokens above 2048; omitting max_tokens does not match it. The tool rule checks both tools offered in the request and tool calls already present in message history. Offering one unapproved tool is enough to deny the request before dispatch.

The policy condition reference contains copy-runnable request, denial, and audit examples for both controls.

Apply rules in a predictable order

Lower priority numbers run first; the default is 100. Conditions inside one rule use AND. The first matching DENY stops evaluation and provider dispatch, while WARN_AGENT records a warning and allows evaluation to continue. If no rule matches, DVARA allows the request.

Global and workspace policies share the same priority order. Scope does not add precedence, so give rules distinct priorities when order matters. Write action explicitly even though an omitted action defaults to DENY.

Author policies in Flightdeck

In the Enterprise Console, open Governance → Policies. Create a Draft, test the parts that dry-run can represent, then move it to Shadow to observe decisions on real traffic without blocking. Promote it to Active after reviewing divergence.

The Portal offers the same lifecycle for policies owned by the signed-in workspace. Operator-managed global policies remain visible only in the Console.

See Routes and policies in Flightdeck for the operator workflow and workspace policies for self-service authoring.

Test before enforcement

Flightdeck dry-run uses the model, workspace, API key, and one synthetic user message entered in the panel. It does not call a provider or store the synthetic request.

Use dry-run for model rules and CEL expressions based on the fields the panel supplies. It cannot reproduce requested token limits, offered tools, time, region, budget utilization, or MCP context. Test those conditions with representative governed traffic in Shadow before activation.

Promote and retire policies safely

The lifecycle has four states:

StatusRuntime effectUse it for
DraftNot evaluatedAuthoring and review
ShadowEvaluated, never changes the live decisionMeasuring false positives on real traffic
ActiveEvaluated and enforcedApproved governance controls
ArchivedNot evaluatedRetiring a policy without deleting its history

Entering Shadow or Active validates the DSL again. Archiving remains available even when an old policy no longer compiles, so you can always take a broken rule out of service.

Activation can show a conflict warning such as Activated, but it conflicts with policies already enforcing: …. The policy still activates. Review the named overlap, adjust priority or conditions, and archive the replaced policy only after the new policy behaves as intended.

Rollback restores an earlier snapshot as a new version rather than erasing history. Shadow-to-Active promotion changes status inside the existing workspace; it does not copy the policy elsewhere.

Move a policy between workspaces

Use the reviewed GitOps export, edit, preview, and import workflow. Give the copied policy a new ID, set its target workspaceId, and import it in merge mode. Use SHADOW in the target when you want evidence before enforcement.

There is no direct cross-workspace promotion button. See Import and export configuration for the complete workflow.

Recover from invalid policy configuration

The schema rejects unknown keys, malformed YAML, unsupported actions, and rules containing both conditions and an Enterprise CEL expression. Only DENY and WARN_AGENT are valid actions.

In Open Source, a bad policy is named and skipped while valid policies continue to load. Fix the named policy and restart; do not treat a partially loaded policy set as a successful deployment.

Flightdeck validates before save and keeps the editor open with a visible compiler message, for example Failed to create policy: … in the Console or The policy DSL is not valid: … in the Portal. The invalid policy is not saved. These are browser workflows; the removed Automation API is not part of this contract.

Monitor policy refresh

Enterprise data planes report each policy refresh with:

gateway_config_refresh_total{table="policies",status="degraded"}
gateway_config_refresh_duration_seconds{config_type="policies",status="degraded"}

The status label is success, degraded, or failure. degraded means at least one policy failed compilation: the bad policy is skipped and named in logs while valid policies continue to enforce. failure means the refresh itself failed and the previous policy set remains active. Alert on either non-success status and use observability to connect the signal to logs and audit evidence.

Continue the governance path