Cedar vs Rego vs OpenFGA: Policy Language Comparison
A deep technical comparison of Cedar, Rego, OpenFGA DSL, and Cerbos YAML/CEL policy languages. Covers syntax, performance benchmarks, formal verification, tooling, and integration patterns with TypeScript examples for each language.
Abstract
The policy language you choose for authorization shapes your entire security architecture. It determines how policies are written, tested, deployed, and audited. Switching languages later means rewriting every policy. This post provides a deep technical comparison of the four major authorization policy languages -- Cedar, Rego, OpenFGA DSL, and Cerbos YAML/CEL -- covering syntax, performance characteristics, formal verification, tooling, and integration patterns with TypeScript. The goal is to help you make an informed language choice before the switching cost becomes prohibitive.
Note: This is Part 4 of the External Authorization Systems series. The main post covers platform selection and architecture patterns. This post focuses specifically on the policy languages themselves.
Why Policy Language Matters
Authorization logic is security-critical code. When it lives scattered across application services as if-else statements and middleware checks, it becomes impossible to audit, test comprehensively, or reason about. Separating policy from code addresses this directly.
Separation of Policy from Code
A dedicated policy language creates a clear boundary: application code describes what the user wants to do, and the policy engine decides whether they can. This separation means:
- Security teams can review policies without reading application code
- Policy changes deploy independently from application releases
- Audit trails capture policy decisions in a standardized format
- Policy testing becomes systematic rather than scattered across integration tests
Auditability and Compliance
SOC 2, HIPAA, and GDPR audits require demonstrable access control. When policies are expressed in a dedicated language and stored in version control, auditors can review the complete authorization logic for a system without understanding the underlying application code. This is a meaningful advantage over authorization logic embedded in application code.
Formal Verification
Cedar ships with Cedar Analyzer, first-party tooling for authorization-focused property analysis and formal reasoning over policy sets (for example, proving that no policy grants access unless the principal meets a condition). Other ecosystems apply formal methods too, but among widely used authorization policy languages, a maintained analyzer at this scope is uncommon. The guarantees are different in kind from exhaustive testing alone.
Cedar (AWS)
Cedar is a declarative policy language developed by Amazon and used by AWS Verified Permissions. It was designed from the ground up for authorization, with formal verification as a core design goal.
Syntax and Model
Cedar uses a permit/forbid model. Every policy either permits or forbids an action. If any forbid policy matches a request, the request is denied regardless of how many permit policies also match. This "deny overrides" behavior is built into the language semantics, not an application-level convention.
Key characteristics of Cedar:
- Entity types and schema: Cedar requires a typed schema defining entity types, their attributes, and relationships. The
isoperator (Cedar language 3.0+, RFC 5) enables type-based matching within policies. - Permit/forbid model: Explicit deny overrides. This makes reasoning about policy outcomes predictable.
- Conditions with
whenandunless: Boolean conditions that reference principal, resource, action, and context attributes. - No side effects: Cedar policies are pure functions. They cannot modify state, call external services, or produce side effects. This is what makes formal verification possible.
Formal Verification
Cedar Analyzer can answer questions like:
- "Is it possible for a non-admin to delete a production resource?"
- "Do any two policies in this policy store conflict?"
- "Does every permitted action require the principal to belong to at least one group?"
These are not test cases. They are mathematical proofs over the entire policy space. The verification is implemented using automated reasoning (SMT solvers) and works on the policy set as a whole, not on individual test inputs.
Performance
Cedar is written in Rust and compiles policies to an efficient internal representation. The Teleport SPEF benchmarks measured Cedar at 28.7x-35.2x faster than OpenFGA and 42.8x-80.8x faster than Rego for policy evaluation.
An important qualification: these benchmarks compare fundamentally different operations. Cedar evaluates local attribute-based policies against a request. OpenFGA traverses a relationship graph. Rego evaluates Datalog-style queries over a document model. Comparing their raw evaluation speed is like comparing the speed of a SQL query to a graph traversal -- the architectures solve different problems. Cedar's speed advantage is real for ABAC workloads, but it does not mean Cedar is "better" than OpenFGA for relationship-based access control.
Integration with AWS Verified Permissions
Cedar is the native policy language for AWS Verified Permissions (AVP). AVP provides a managed policy store, batch authorization APIs, and native integration with Cognito, API Gateway, and AppSync. Cedar can also be used outside AWS through the open-source Cedar SDK.
Rego (OPA)
Rego is the policy language for Open Policy Agent (OPA), a CNCF graduated project. Unlike Cedar's authorization-specific design, Rego is a general-purpose policy language that can express authorization rules, Kubernetes admission control, Terraform plan validation, and infrastructure compliance checks.
Syntax and Model
Rego is inspired by Datalog, a declarative logic programming language. Policies are expressed as rules that evaluate to true or false. The input document contains the request context, and data contains external data loaded into OPA.
Key characteristics of Rego:
- Datalog-inspired: Rules are implicitly AND-joined (all conditions in a rule body must be true). Multiple rule definitions with the same name are OR-joined.
- Document model: Input and data are JSON documents. Policies navigate these documents using path expressions.
- General-purpose: Rego can express any policy, not just authorization. This flexibility comes at the cost of a steeper learning curve.
- Partial evaluation: OPA can partially evaluate policies, pre-computing results where possible and leaving placeholders for unknown values. This enables efficient query compilation.
- Built-in functions: Rego includes functions for string manipulation, time operations, HTTP requests, JSON Web Token validation, and more.
Learning Curve
Rego's Datalog roots make it unfamiliar to most application developers. The implicit conjunction (AND) within rule bodies, the implicit disjunction (OR) across rule definitions, and the unification semantics require a different mental model than imperative or even most declarative languages. Proficiency typically takes 30-40 hours of dedicated study.
The import rego.v1 directive (introduced for forward compatibility) changes some default behaviors, which adds to the confusion for developers reading Rego examples from different periods.
The OPA/Styra Situation
Apple acqui-hired Styra's co-founders and core team. Styra DAS (the commercial OPA management platform) is transitioning to community-maintained open source as enterprise commercial support winds down. OPA itself remains under CNCF governance, and the community is active. However, enterprise users who relied on Styra DAS for policy lifecycle management now need alternative tooling. This situation has accelerated evaluation of alternatives like Cerbos, Cedar, and Permit.io among some OPA users.
Ecosystem Strength
Despite the Styra transition, Rego has the broadest ecosystem of any policy language:
- Kubernetes: OPA Gatekeeper for admission control
- Terraform: Conftest and Rego policies for plan validation
- Envoy: OPA as an external authorization filter
- API gateways: Kong, Traefik, and others support OPA integration
- CI/CD: Policy checks in build pipelines
This ecosystem breadth is Rego's strongest advantage. If you need a single policy language across infrastructure and application authorization, Rego is the most versatile choice.
OpenFGA DSL
OpenFGA DSL is the modeling language for OpenFGA, the open-source Zanzibar-inspired authorization engine that powers Auth0/Okta FGA. Unlike Cedar and Rego, which express rules, OpenFGA DSL expresses relationships and type definitions.
Syntax and Model
OpenFGA DSL defines authorization models as type systems. Access is determined by the existence of relationships between entities, not by evaluating conditions against attributes.
Key characteristics of OpenFGA DSL:
- Type system: Every entity has a type. Relations are defined between types. This creates a strongly-typed authorization model.
- Relationship operators: Direct assignment (
[user]), computed relations (editor or owner), and tuple-to-userset mappings (member from department). - No attribute conditions in the model: The core model expresses structural relationships, not conditional logic. This is a deliberate design choice aligned with the Zanzibar paradigm.
Conditions (Schema 1.1+)
Starting with schema 1.1, OpenFGA added support for conditions using Google's Common Expression Language (CEL). Conditions can be attached to relationship type restrictions:
This is a significant addition, but conditions in OpenFGA are still scoped to relationship assignments, not arbitrary policy rules. The business hours check from the example scenario would need to be a condition attached to the relationship, not a standalone policy rule.
Comparison with SpiceDB Schema
SpiceDB uses a similar but distinct schema language. Both are Zanzibar-inspired, but they differ in syntax and some capabilities:
SpiceDB's schema uses definition instead of type, permission instead of define, and the + operator for union. SpiceDB also implements the full Zanzibar consistency model with ZedTokens, which OpenFGA does not.
When to Use OpenFGA DSL
OpenFGA DSL excels when authorization is primarily about "who has what relationship to what." Applications with sharing models (Google Docs-style "User X can edit Document Y"), hierarchical structures (folder permissions inherited by documents), and organizational models (department-based access) map naturally to OpenFGA's relationship model.
It is less suited for attribute-based conditions. If your authorization rules depend heavily on time, location, risk scores, or other contextual attributes, OpenFGA requires either using conditions (limited) or layering an ABAC system alongside it.
Cerbos (YAML + CEL)
Cerbos takes a different approach: policies are written in YAML with CEL (Common Expression Language) for conditions. There is no custom language to learn. If you know YAML and basic expression syntax, you can write Cerbos policies.
Syntax and Model
Cerbos uses a resource-centric model. Policies are attached to resource types and define which roles can perform which actions, optionally with conditions.
Key characteristics of Cerbos YAML/CEL:
- Resource-centric: Policies are organized by resource type. Each resource type has its own policy file.
- YAML structure: The policy structure is declarative YAML. No custom grammar or parser to learn.
- CEL expressions: Conditions use Google's Common Expression Language, which is also used by Kubernetes, Firebase, and Google Cloud IAM. CEL is a well-documented, broadly supported expression language.
- Derived roles: Roles computed at request time based on context. For example, a user with role "employee" becomes "department_manager" if they manage the resource's department.
Derived Roles
Derived roles are a distinctive Cerbos feature. They allow dynamic role computation without requiring the application to pre-compute roles:
These derived roles can then be referenced in resource policies, creating a two-layer system: the application provides base roles, and Cerbos computes context-specific roles at evaluation time.
Trade-offs
The YAML/CEL approach trades expressiveness for accessibility. Cerbos policies are straightforward for developers to read and write without specialized training. The learning curve is roughly 5-10 hours, compared to 30-40 hours for Rego. However, YAML does not support advanced features like Rego's partial evaluation, Cedar Analyzer-style property analysis, or OpenFGA's relationship graph traversal. For complex authorization logic, YAML policies can become deeply nested and harder to maintain.
Same Scenario in All Four Languages
To make the comparison concrete, here is the same authorization rule implemented in all four languages, including edge cases.
The rule: "Editors can edit documents in their department during business hours. Department managers can edit any document in their department at any time. No one can edit archived documents."
Cedar
Cedar handles the deny override naturally. The forbid policy for archived documents overrides both permit policies regardless of role or time.
Rego
In Rego, the archived check must be explicitly included in each allow rule (using not is_archived). There is no built-in deny-overrides mechanism -- the application must query both allow and any deny rules.
OpenFGA DSL
OpenFGA models the structural relationships but cannot express the business hours condition within the DSL. The time-based restriction would need to be handled at the application layer or through a condition:
This illustrates a fundamental difference: OpenFGA answers "who has what relationship," while time-based conditions require additional context.
Cerbos YAML/CEL
Cerbos supports explicit EFFECT_DENY rules that override EFFECT_ALLOW rules, similar to Cedar's forbid mechanism. The derived roles (department_editor, department_manager) are defined separately and handle the department matching logic.
Performance Benchmarks
Performance matters for authorization -- it sits in the critical path of every API request. But benchmark numbers need careful interpretation.
Raw Numbers
The Teleport SPEF framework measured the following on randomly generated inputs:
Why Direct Comparison Is Misleading
These engines solve fundamentally different problems:
- Cedar evaluates a set of policies against a single request with known attributes. The evaluation is stateless and local.
- OpenFGA traverses a relationship graph to determine if a path exists between a subject and an object. The speed depends on graph depth and breadth.
- Rego/OPA evaluates Datalog-inspired queries against a document model. Rego's generality means it does more work per evaluation than a purpose-built authorization evaluator.
Comparing their speeds is like comparing a hash table lookup to a graph BFS to a SQL query. Each is fast for its intended operation.
Real-World Performance Considerations
In practice, authorization latency depends on more than raw evaluation speed:
- Network overhead: A sidecar PDP adds microseconds. A centralized PDP adds milliseconds. For most applications, network latency dominates evaluation time.
- Caching: Caching authorization decisions (with appropriate TTL) reduces the impact of slower evaluation engines. A cached decision is fast regardless of the engine.
- Batch evaluation: Cedar (via AVP) and Cerbos support batch authorization -- checking multiple permissions in a single request. This amortizes network overhead.
- Relationship graph size: OpenFGA and SpiceDB performance depends on the depth of the relationship graph. Shallow graphs are fast; deeply nested hierarchies require more traversal.
Tip: Benchmark your specific authorization patterns, not generic inputs. A system with 50 ABAC policies will have different performance characteristics than one with 5 million relationships.
Tooling and Developer Experience
Policy language usability depends on more than syntax. The surrounding tooling -- CLI tools, testing frameworks, playgrounds, and IDE support -- shapes the day-to-day developer experience.
CLI Tools
Testing
All four languages support policy testing, but the approaches differ:
Cedar: The Cedar CLI includes a check-parse command and the analysis tools can verify policy properties. Integration testing uses the Cedar SDK to evaluate policies against test fixtures.
Rego: OPA has a built-in testing framework. Tests are written in Rego itself, which is both powerful and adds to the learning curve:
OpenFGA: OpenFGA provides model validation and assertion testing through the CLI and the playground. Tests verify that specific relationships produce expected authorization outcomes.
Cerbos: Cerbos has a dedicated test framework using YAML test files:
Playground and IDE Support
Documentation Quality
- Cedar: Well-structured reference documentation. The academic paper (OOPSLA 2024) provides deep technical background but is not required for practical use.
- Rego: Extensive documentation, but the volume can be overwhelming. The learning path from beginner to proficient is not always clear.
- OpenFGA: Clear, example-driven documentation with step-by-step modeling guides. The playground integration makes concepts tangible.
- Cerbos: Practical, developer-oriented documentation with progressive tutorials and migration guides from OPA.
Integration Patterns
How each language integrates with application code matters for adoption and maintenance. Here are TypeScript integration examples for each.
Cedar (via AWS Verified Permissions)
Rego (via OPA REST API)
OpenFGA (via SDK)
Cerbos (via SDK)
Decision Matrix
The following table summarizes the key differences across all four languages. Use it as a starting point, not a final answer -- your specific requirements should drive the choice.
*Styra DAS (the managed OPA option) is transitioning to community-maintained open source.
Decision Flowchart
Common Pitfalls
Choosing Based on Benchmarks Alone
Cedar's performance numbers are compelling, but performance is rarely the deciding factor for policy language selection. If your authorization model is relationship-based, Cedar's speed advantage is irrelevant because Cedar does not natively support relationship graph traversal. Choose the language that matches your authorization model first, then optimize for performance.
Underestimating Language Lock-In
Switching policy languages means rewriting every policy, retraining every developer, and rebuilding every integration test. This is not a framework swap -- it is closer to changing your database query language. Invest time in evaluating the language before committing to it.
Ignoring the Ecosystem
A policy language does not exist in isolation. Consider the SDK quality for your language stack, the community activity, the documentation maturity, and the long-term governance model. Rego's broad ecosystem (Kubernetes, Terraform, Envoy) makes it valuable even if another language has better syntax for your specific use case.
Using Code Where a Table or Diagram Would Work Better
Authorization decision logic is better communicated through decision tables and flowcharts than through pseudo-code. When documenting your authorization model for stakeholders, express the rules as tables or diagrams rather than code samples. Save the code for the actual policy implementation.
Skipping Policy Testing
Every major policy language supports testing. Cedar has SDK-based testing, Rego has a built-in test framework, OpenFGA has assertions, and Cerbos has YAML test suites. Untested authorization policies are a security liability. Treat policy testing with the same rigor as application code testing.
Conclusion
Each policy language reflects a different philosophy about authorization:
- Cedar prioritizes safety and analyzability. If you need first-party property-style analysis over policies (Cedar Analyzer), it is the strongest fit among these four languages.
- Rego prioritizes generality. If you need a single language for infrastructure policy, Kubernetes admission control, and application authorization, Rego's breadth is unmatched.
- OpenFGA DSL prioritizes relationship modeling. If your authorization is fundamentally about "who has what relationship to what," OpenFGA's type system maps directly to your domain.
- Cerbos YAML/CEL prioritizes accessibility. If you want the fastest path from "we need externalized authorization" to "we have policies in production," Cerbos minimizes the learning curve.
The right choice depends on your authorization model, not on benchmark numbers or language popularity. Define your authorization requirements first -- what models you need (RBAC, ABAC, ReBAC), what guarantees matter (formal verification, deny overrides), and what ecosystem you operate in (AWS, Kubernetes, multi-cloud). Then choose the language that fits those requirements.
For a broader view of platform selection and architecture patterns, see the main post in this series.
References
- Cedar Policy Language Documentation - Official Cedar language reference, grammar specification, and policy writing guides
- Cedar: A New Language for Expressive, Fast, Safe, and Analyzable Authorization - Amazon Science paper on Cedar's design, formal verification approach, and benchmarks
- Open Policy Agent Documentation - Official OPA documentation including Rego language reference and policy testing guides
- OpenFGA Documentation - OpenFGA modeling language reference, conditions, and integration guides
- OpenFGA Conditions - CEL-based conditions in OpenFGA schema 1.1+
- Cerbos Documentation - Cerbos policy authoring, derived roles, and testing framework documentation
- Cerbos Derived Roles - Detailed guide on Cerbos derived roles for dynamic role computation
- Teleport - Security Benchmarking Authorization Policy Engines - SPEF framework benchmarking Rego, Cedar, OpenFGA, and Teleport ACD
- Permit.io - Policy Engine Showdown: OPA vs. OpenFGA vs. Cedar - Comparative analysis of policy engine syntax, performance, and use cases
- Oso - OPA vs Cedar vs Zanzibar: Policy Engine Guide - Paradigm comparison between major policy engine approaches
- Cloud Native Now - Apple Buys Styra Brains, OPA Remains Open - Coverage of the OPA ecosystem transition following the Styra acqui-hire
- AuthZed SpiceDB GitHub Repository - SpiceDB schema language reference and Zanzibar implementation details
External Authorization Systems
A comprehensive guide to external authorization platforms for distributed systems. Covers platform selection, policy language comparison, cloud-native authorization with AWS, and relationship-based access control with SpiceDB and Auth0 FGA.