Migrating from OPA / Rego
Move an existing OPA/Rego deployment onto Vengtoo in four stages, without a big-bang cutover:
Discover → Translate (into a non-prod environment) → Shadow (prove parity) → Cutover (promote → flip)
You never enforce with Vengtoo until the translation is proven to make the same decisions the incumbent bundle already makes. The safety comes from doing translation and validation in a non-prod environment, then promoting the proven model to production.
This flow has been run end-to-end against a real OPA/Rego healthcare policy set (role permissions loaded from data documents, clinician/patient relationship rules, a billing-lock guardrail, and a legacy compatibility rule) with full decision parity confirmed on every test case.
Prerequisites
- A Vengtoo API key scoped to a non-prod environment, never translate straight into production.
- Your
.regopolicy files and the data documents they load (data.*: this is usually where the real role bindings and permission tables live, not in the Rego itself). - Ideally, your existing OPA test suite (
opa test) or decision logs: this becomes the coverage set for Stage 3.
Stage 1 - Discover
Rego splits into logic (the rules) and data (what OPA loads alongside it). Most deployments put the actual grants in the data, and the Rego is a thin matcher. Discover both:
- Rules: all
.regofiles. Look for the entry decision, usuallydefault allow = falseplus one or moreallow { ... }/allow if { ... }rules, and anydenyrules. - Data: the JSON/YAML documents loaded as
data.*(role bindings, permission tables, resource ownership). This is where the actual subject → role and role → permission assignments usually live. - Input shape: the
inputobject your PEP sends (input.subject,input.action,input.resource,input.context). This is the request contract you need to reproduce. - A coverage set: real
(input) → decisionexamples for Stage 3. Best sources, in order: your ownopa testcases, your decision logs, then a generated grid if you have neither.
Stage 2 - Translate
| Rego construct | Vengtoo equivalent |
|---|---|
input.subject / input.user | subject (external_id = the same id) |
input.action | an action on a resource type |
input.resource.type | a resource type (+ its actions) |
allow { input.role == "admin" } | a role + an ALLOW policy on that role |
data.role_bindings[user][_] == "editor" | role assignments (subject → role) |
deny { ... } | a DENY policy at higher priority (DENY beats ALLOW) |
data.permissions[role] (role → permission table) | policies per role, assigned to that role |
| a fixed-value guard in the rule body (time, IP, a status field) | a Vengtoo condition: resource_attrs, context_attrs, time_window, ip_allowlist all work cleanly here |
A data-driven role permission translated into an ALLOW policy with a literal condition:
{
"name": "clinician-west-appointment",
"effect": "ALLOW",
"resource_types": [{ "resource_type_id": "<appointment-type-id>", "actions": ["view", "update"] }],
"conditions": { "resource_attrs": [{ "key": "clinic_id", "op": "eq", "value": "clinic-west" }] }
}
What doesn't translate automatically
- Relationship checks (
input.resource.owner == input.subject.id,input.resource.assigned_clinician_id == input.subject.id). Vengtoo's ABAC conditions compare an attribute to a fixed value, not to another entity's attribute: there's no way to express "this field must equal whoever's asking" as one reusable condition. The practical fix: generate an instance-level direct grant to the specific subject at the point the relationship is established (e.g. when a record is assigned), not a general policy. This is real, ongoing integration work, not a one-time translation step. - Custom Rego functions, comprehensions,
some/everyloops: re-express the intent by hand; there's no generic way to port arbitrary imperative logic. - External data lookups inside policy (
http.send, a DB call in the rule body): in Vengtoo, resolve the value in your PEP and pass it as a subject/resource attribute on the request. Policy conditions read attributes; they don't fetch anything themselves. - Anything computed relative to another field: e.g. a deny rule that blocks an edit more than N hours after a resource's own timestamp. This isn't just "no dynamic comparison," it's arithmetic between two different fields, and it's not expressible at all today. Flag it explicitly and decide whether it still needs to be enforced elsewhere in application code. Don't let it silently disappear in translation.
- Partial-set / multi-value rules used for list/data filtering: this is Vengtoo's Search / partial-evaluation surface, not a boolean policy. Model it separately.
Hand the actual model creation to /vengtoo-policies once you've worked out the mapping:
this guide gives you the mapping, that skill builds the resource types, roles, policies,
and conditions.
Stage 3 - Shadow: prove parity before anything depends on it
Replay your Stage 1 coverage set through Vengtoo's evaluation endpoint in the non-prod environment, and diff every decision against what the incumbent bundle actually returned:
curl -X POST https://api.vengtoo.com/access/v1/evaluation \
-H "Authorization: Bearer $VENGTOO_API_KEY" -H "Content-Type: application/json" \
-d '{"subject":{"type":"user","external_id":"..."},"resource":{"type":"...","external_id":"..."},"action":{"name":"..."},"context":{}}'
Categorize every result:
- Match: good.
- False allow (Vengtoo allows, the incumbent denied): the dangerous class; a policy is
too broad or a
DENY/condition is missing. Fix before anything else. - False deny (Vengtoo denies, the incumbent allowed): will break real access; usually a missing policy or a broken role assignment.
Don't move to Stage 4 until parity holds, or every remaining divergence is explained and intended (e.g. you're deliberately fixing a bug the old rules had).
Stage 4 - Cutover
- Promote the validated model from your non-prod environment to production: this carries drift detection and provenance, so you ship the exact model you proved, not a hand-rebuild.
- Wire the enforcement point: the flip is either a config change or a code swap, not
new integration logic:
- In-app SDK enforcement: replace the OPA
Decision()/sdk.Decisioncall with the Vengtoo SDK'sCheck()/Evaluate(). - Gateway/MCP enforcement: point your gateway at Vengtoo's
ext_authzendpoint instead of the OPA sidecar. See the Kong, Envoy, agentgateway, or IBM ContextForge guides for the actual wiring.
- In-app SDK enforcement: replace the OPA
- Roll out gradually: flip one route, service, or tenant at a time, and watch the decision log for divergence as you expand. Keep the OPA bundle running as a fallback.
- Rollback = point back at OPA. It keeps running until you deliberately decommission it, so cutover is always reversible.
Note: this is the reverse of Vengtoo's own OPA export
Vengtoo exports its own policies to OPA bundles for signing/distribution
(/policies/bundle/opa). That's the opposite direction and unrelated to migration. This
guide is about translating an external Rego ruleset into the Vengtoo model, not importing
a Vengtoo-generated bundle.
Run it with an AI coding agent
The /vengtoo-migrate skill runs this entire flow: it reads your Rego rules and data
documents, does the mapping above, builds the model via /vengtoo-policies, and runs the
shadow-compare for you.