← Back to blog

Cedar vs OPA for AI Agent Access Control: Why Deny-Wins Matters

Published 2026-04-15

The problem with RBAC for agents

Traditional RBAC assigns roles to humans. AI agents don’t have a fixed identity — they execute on behalf of users, but their tool access scope is often broader than any individual user’s permissions.

When an agent can call ec2:DescribeInstances, cloudwatch:GetMetricData, rds:DescribeDBInstances, and ssm:GetParameter, the question isn’t “what role does this agent have?” — it’s “which of these actions is allowed for this request, from this tenant, for this purpose, at this time?

That’s what policy-as-code was built for.

Why Cedar over OPA

Both Cedar and OPA can express fine-grained policies. We chose Cedar for three reasons:

1. Deny-wins semantics. In Cedar, an explicit forbid always beats a permit. In OPA, policy evaluation order matters and can be subtle. For agentic systems where the blast radius of a mistake is high, deny-wins is the right default.

2. WASM compilation. Cedar compiles to WASM, which runs directly in Cloudflare Workers via @cedar-policy/cedar-wasm. No separate policy server, no network hop. Policy evaluation is synchronous and ~1ms.

3. Structured entities. Cedar policies operate on typed entity graphs — User::"alice" is a principal, Action::"ec2:Describe" is an action, Resource::"prod" is a resource. The schema prevents a whole class of policy authoring mistakes that OPA’s Rego allows.

The guard-gate implementation

EAIP’s guard-gate Cloudflare Worker loads Cedar policies from Workers KV on cold start. Every /invoke request evaluates:

permit(
  principal in Team::"${teamId}",
  action in [Action::"agent:invoke"],
  resource is Agent
) when {
  resource.tenant == principal.tenant &&
  context.injection_scan_passed == true
};

The forbid rules cover cross-tenant access, actions outside business hours (optional), and any resource where resource.sensitivity == "classified" for teams without clearance.

The entire evaluation runs before any model call. If Cedar returns deny, the request never reaches the agent runtime.