Policy Engines vs. Relationship Engines
The authorization toolbox has two broad shapes:
- Policy engines — OPA (Rego) and Cedar, evaluate policies expressed in a DSL against input data passed on each request. The engine is stateless; you supply the data.
- Relationship engines — OpenFGA, SpiceDB, Ory Keto, all in the Zanzibar tradition — store relationship tuples in a database and answer queries against the stored graph. The engine is the database.
When a policy engine fits
- Decisions are mostly attribute-driven — claims, resource metadata, request context.
- All the data needed for the decision is in hand at request time — claims in the token, fields on the resource, request context — so the engine doesn't need to fetch anything to answer.
- You already run one for infrastructure or admission policy — extending it to cover application rules avoids a second decision surface.
OPA's graduated CNCF status and broad ecosystem make it the default choice in this category.
When a relationship engine fits
- Decisions depend on relationships that change at write time — group membership, document sharing, folder hierarchy, multi-tenant ownership.
- The data behind the decision is too large or too dynamic to ship on every request — millions of memberships, deeply nested hierarchies — so the engine needs to own the store.
- You need reverse queries — "list every resource this user can read" — which inherently require a stored graph.
- Permissions are per-resource and per-user, not just per-attribute.
Policy as code in OpenFGA
The "policy in Git, reviewed via PR" workflow isn't unique to policy engines. The OpenFGA model DSL is the policy: types, relations, and conditions live in a text file you commit, review, and deploy like any other code. The same model backs authorization decisions across services, languages, and domains — one source of truth instead of policy logic re-implemented per service.
You can use both
Pairing them is common: a relationship engine for application authorization, a policy engine at the infrastructure or admission layer. Inside the application, OpenFGA covers most attribute-driven rules with conditions and contextual tuples, so a second engine isn't always needed.