MainSDK
Search⌘K
Integrations

Open Policy Agent

Use Open Policy Agent for MainSDK access and policy, with Rego bundles evaluated in process - main.yaml, testing, limits and swapping.

2 interfaces2 driversWritten guide
Updated 2026-10-28

Open Policy Agent (OPA) is an open-source, general-purpose policy engine that uses the Rego language. In MainSDK it can drive access, for your app's permissions, and policy, for everything MainSDK decides. Pick it when your company already writes Rego and keeps OPA bundles, so your rules carry over unchanged.

Interfaces it serves

Pick Open Policy Agent in main.yaml for any of these. Your calls stay the same, and you can swap later.

Before you start

  1. Write your policies in Rego and keep them in git next to main.yaml.
  2. Build a bundle of those policies with the OPA tooling. For policy, MainSDK evaluates the compiled bundle in process; there is no OPA server to run for it.

Configure

Permissions:

main.yaml
access:
  driver: opa
  models: [rbac, abac]
  unreachable: { deny: true, cache: 30s }
  # [to be decided] connection keys for the access driver

Policy decisions:

main.yaml
policy:
  engine: opa
  placement: server
  mode: observe                  # the default for new policies
  opa: { bundle: ./policies/rego }

Use it

app/api/orders/[id]/route.ts
import { access } from "mainsdk";

const decision = await access.check({ subject: user, action: "orders.update", resource: order });
if (!decision.allow) return new Response("Forbidden", { status: 403 });
app/api/reports/[id]/export/route.ts
import { policy } from "mainsdk";

const d = policy.decide({
  subject: { type: "user", id: user.id },
  action: { name: "export.data" },
  resource: { type: "report", id: report.id },
});
if (d.decision === "deny") console.log(policy.explain(d.id));

Local development and testing

The bundle runs in process, so local development needs nothing extra. mainsdk test policy and mainsdk test access run your tests; policyTest from @mainsdk/testing asserts a decision and its rule, and createTestApp() puts access on its fake. chaos.on("access", { outage: true }) shows access denying when the engine is unreachable.

What MainSDK adds on top

  • Deny when no policy permits, with no bypass path.
  • policy decisions in process, no network round trip per decision; the same shared decision suite passes on OPA and Cedar.
  • Lower layers only tighten; a broken bundle keeps the last good policy set (MAIN-POLICY-0009).
  • Every decision written to the audit log with the policy version.

See Permissions and Policy decisions.

Limits and notes

  • The OPA drivers are phase 1 tickets in the access spec (milestone 0.3) and the policy spec (milestone 0.2).
  • OPA has no relations: calling access.relate() with driver: opa fails mainsdk check with MAIN-ACCESS-0004, which lists drivers that support them.
  • policy never calls a remote OPA per decision.
  • access.raw() and policy.raw() return the engine; mainsdk check lists every use.

Swap to or from it

main.yaml
policy:
  engine: cedar   # was opa

FAQ

Do I need to run an OPA server? Not for policy: the bundle is evaluated in process.

Can I keep my existing OPA bundles? Yes. They run in place (byo-policy).

Next

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet