Authorization in 2026: revisit OpenFGA, SpiceDB and Cerbos before you hard-code another permission check
Picture a SaaS company in Nagpur that sells loan-origination software to cooperative banks across Vidarbha and Marathwada (an invented example, not a real client). About thirty-five engineers and a permission story that grew one if at a time. Officers see loan files of their branch, managers approve up to a sanction limit, bank admins see every branch, and a blocked user must never approve. Each rule lives in three places: the Java API, a reporting query and a React screen that hides buttons. When a large cooperative bank asks “who can approve what”, nobody can answer from one place. Three proposals land on the architecture review. One engineer wants OpenFGA because it is a CNCF project inspired by Google Zanzibar. A second wants SpiceDB because its schema language and consistency tokens look rigorous. A third wants Cerbos because the team already writes YAML and the rules sound like policies, not graphs. They are discussing these as three brands of the same thing. They are not.
Authorization services in 2026 come in at least two shapes. OpenFGA and SpiceDB are relationship-based access control (ReBAC) systems in the Zanzibar family: you define a model of types and relations, write relationship tuples into the service’s own datastore, and ask “does subject X have relation Y on object Z?” The service walks the graph. Cerbos is a stateless policy decision point (PDP): you keep your data where it is, send the principal, the resource and its attributes on every request, and Cerbos evaluates YAML policies with CEL conditions. Teams put both shapes in the same shopping cart because both answer “can this user do this?”, but ReBAC systems ask you to move relationship data into them, while a PDP asks you to move decision logic into it. This post compares where OpenFGA, SpiceDB and Cerbos stand on 14 October 2026: history, modelling, consistency, conditions, list filtering, SDKs, observability, security advisories and a decision guide. Facts come from GitHub releases, LICENSE files at tags, official docs and advisory records cross-checked against NVD and cve.org. The drill near the end is my own run of all three binaries on this Linux box with one invented lending model, labelled clearly for what was and was not run.
The short version
- Decide first which shape your problem has. Permissions that flow through relationships (bank, branch, team, folder, sharing) suit a ReBAC store. Rules over attributes you already hold (role, amount, region, time) suit a policy PDP. Many systems need a bit of both.
- OpenFGA (
openfga/openfga) is at v1.22.0 (7 October 2026, 12:59 AM IST). Apache 2.0. Zanzibar-inspired ReBAC with a modelling DSL, CEL conditions, HTTP and gRPC APIs, PostgreSQL, MySQL and SQLite (beta) storage and SDKs for Node.js, Go, .NET, Python and Java. CNCF project since 14 September 2022, Incubating since 28 October 2025. Auth0 FGA is the managed service built on it. - SpiceDB (
authzed/spicedb) is at v1.56.2 (11 September 2026, 11:31 PM IST). Apache 2.0. Zanzibar-inspired ReBAC with a schema language, CEL caveats, per-request consistency through ZedTokens, and CockroachDB, Cloud Spanner, PostgreSQL or MySQL datastores. Built by AuthZed, which also sells managed offerings. Not a CNCF project. - Cerbos (
cerbos/cerbos) is at v0.56.0 (30 September 2026, 12:05 PM IST). Apache 2.0. A stateless PDP that evaluates YAML policies with CEL conditions, answersCheckResourcesandPlanResources, and loads policies from disk, git, blob storage or a database. Cerbos Hub is the commercial control plane. Still on 0.x. - Security history differs by project. OpenFGA has 29 repository advisories since 2022, 11 of them in 2026, many of them authorization bypass or “improper policy enforcement” in specific model shapes, plus an OIDC audience issue (CVE-2026-55689) and a playground key exposure (CVE-2026-40293). SpiceDB has 18 advisories, including secrets leaking through metrics, logs or traces (CVE-2023-29193, CVE-2026-40091, GHSA-jf7w-wx43-32gc). I found no published CVE or GitHub advisory for Cerbos, but its default evaluation mode can silently skip a DENY rule whose CEL condition errors, which the drill shows.
Where things stand on 14 October 2026
Dates are from each project’s GitHub release published_at; times are converted from UTC to IST (UTC+5:30). Licences are from the LICENSE files at the tagged releases. None of the listed releases is marked prerelease.
Project
Release
Date (IST)
Notes
Licence
OpenFGA (openfga/openfga)
v1.22.0
7 October 2026, 12:59 AM
Current release; MySQL read-replica support (--datastore-secondary-uri), gRPC library bump for GO-2026-6443, minimum Go 1.26.0
Apache 2.0
OpenFGA
v1.21.0
20 September 2026, 9:57 PM
Adds experimental “Dynamic Conditions”
Apache 2.0
OpenFGA
v1.20.0
9 September 2026, 2:11 AM
Fixes GHSA-h7w8-xr72-cv4r; Go 1.26.8 toolchain
Apache 2.0
OpenFGA CLI (openfga/cli)
v0.8.1
24 September 2026, 9:08 AM
fga CLI used for model tests in the drill
Apache 2.0 (repo)
SpiceDB (authzed/spicedb)
v1.56.2
11 September 2026, 11:31 PM
Current release; fixes GHSA-jf7w-wx43-32gc (secrets in traces), namespace cache TTL, MemDB snapshot reads
Apache 2.0
SpiceDB
v1.56.1
26 August 2026, 11:33 PM
Patch release
Apache 2.0
SpiceDB
v1.56.0
25 July 2026, 1:16 AM
Adds pkg/embedded in-process library, permissionship counter metric, cached compiled caveats
Apache 2.0
SpiceDB
v1.54.0
19 June 2026, 4:57 AM
Previous minor; there is no v1.55.x tag
Apache 2.0
Cerbos (cerbos/cerbos)
v0.56.0
30 September 2026, 12:05 PM
Current release; removes long-deprecated config keys, fixes principal-policy deny precedence in the query planner
Apache 2.0
Cerbos
v0.55.0
13 August 2026, 1:44 PM
CEL library upgrade (stricter), strict evaluation mode, multiple JWTs in auxData
Apache 2.0
Cerbos
v0.54.0
20 July 2026, 3:08 PM
Logs CEL runtime errors and adds them to audit entries
Apache 2.0
Practical notes on the table. All three ship a checksums.txt with release binaries (OpenFGA and Cerbos also attach SBOMs); the drill verified SHA-256 for every archive it used. OpenFGA has released roughly every two to three weeks this year, often with an advisory fix, so “we are on 1.1x, it works” is not a safe position. SpiceDB skipped the 1.55 number; fix automation that expects consecutive minors. Cerbos is still 0.x, so read each release note for breaking changes, as v0.55.0 (CEL library) and v0.56.0 (removed config keys) both had.
Timeline
Date (IST)
Event
July 2019
“Zanzibar: Google’s Consistent, Global Authorization System” presented at USENIX ATC 2019
24 March 2021
First Cerbos tag (v0.0.0-alpha0); v0.0.1 follows on 18 May 2021
30 September 2021
SpiceDB v1.0.0
10 June 2022
OpenFGA v0.1.0
14 September 2022
OpenFGA accepted into the CNCF
13 April 2023
SpiceDB CVE-2023-29193: preshared key visible through the metrics port’s pprof cmdline
15 April 2023
OpenFGA v1.0.0
10 August 2024
OpenFGA CVE-2024-42473: Check bypass in v1.5.7 and v1.5.8
28 October 2025
OpenFGA moves to CNCF Incubating
9 April 2026
OpenFGA CVE-2026-40293: preshared key in playground HTML
15 April 2026
SpiceDB CVE-2026-40091: datastore DSN in startup logs
19 June 2026
OpenFGA CVE-2026-55689 (OIDC audience) and SpiceDB CVE-2026-55866 (caveat result under concurrency) published
13 August 2026
Cerbos v0.55.0 with strict evaluation mode
11 September 2026
SpiceDB v1.56.2 with GHSA-jf7w-wx43-32gc fix
29 September 2026
OpenFGA GHSA-h7w8-xr72-cv4r published (fixed in v1.20.0)
30 September 2026
Cerbos v0.56.0
7 October 2026
OpenFGA v1.22.0
14 October 2026
This post’s research and the three-engine drill
The history explains the shapes. The Zanzibar paper described how Google stores access control as relationships and checks them consistently at scale; SpiceDB and OpenFGA are two open-source answers to it. Cerbos started around the same time (first tag in March 2021) from a different question: how do we stop scattering role and attribute rules across services without moving our data?
First question: what are you actually trying to centralise?
Before comparing logos, the Nagpur team should write down which of these hurts most:
- Hierarchy and sharing. “Members of the branch that owns this file can view it”, “bank admins see all branches”. The answer depends on chains of relationships: ReBAC territory, OpenFGA or SpiceDB.
- Rules over attributes you already have. “Managers approve up to their sanction limit”, “no approvals after 8 PM”. The data sits in your loan table and identity token: PDP territory, Cerbos, or ReBAC conditions if the rules are few.
- “Show me only what I can see” lists. Designs break first on list screens. ReBAC systems answer with ListObjects or LookupResources; Cerbos returns a query plan you turn into a database filter.
- Identity is a separate problem. None of the three logs anyone in. Authentication and SSO belong to your identity provider, covered in the Keycloak and alternatives post.
If you install OpenFGA and pass every rule as attributes, you have built an expensive PDP. If you install Cerbos and push the whole bank and branch graph into every request, you have rebuilt a graph database in your API layer. The first design choice is where relationship data lives.
What each project actually is
OpenFGA 1.22.0
OpenFGA describes itself as a high-performance, flexible authorization engine inspired by Google Zanzibar. You create a store, write an authorization model in its DSL (or JSON), write relationship tuples such as user:ravi is officer of branch:sitabuldi, and call Check, ListObjects, StreamedListObjects or ListUsers. The DSL builds permissions with or, and, but not and X from Y. Conditions attach CEL expressions to tuples, evaluated against request context.
Operational facts from the README and docs:
- Storage: in-memory (development only), PostgreSQL 14+, MySQL 8 and SQLite (beta). PostgreSQL already supported a read replica; v1.22.0 adds it for MySQL, with a documented read-after-write risk.
- APIs and auth: HTTP and gRPC; authentication none, preshared key or OIDC.
- Tooling: the
fgaCLI (withfga model test), a Terraform provider, a Go library for embedding, and a local playground on port 3000 that the production guide tells you to disable. - Governance: CNCF Incubating. The README says Auth0 FGA has run it in production since December 2021.
OpenFGA fits when you want a Zanzibar-style service under foundation governance with a friendly DSL and good SDK coverage. Its trade-off shows in the advisory list: correctness in complex models (exclusions, wildcards, caching) is hard, and the project discloses and fixes such issues often.
SpiceDB 1.56.2
SpiceDB calls itself the most mature open-source project inspired by Zanzibar and stays closest to the paper’s vocabulary. A schema uses definition, relation and permission, combining relations with + (union), & (intersection), - (exclusion), -> (arrow) and .any / .all. Caveats are typed CEL expressions on relationships, which is how SpiceDB mixes ABAC into ReBAC. Writes return a ZedToken; reads can ask for minimize_latency, at_least_as_fresh, at_exact_snapshot or fully_consistent, SpiceDB’s answer to the paper’s “new enemy problem”.
Operational facts from the README and docs:
- Datastores: CockroachDB (recommended for high throughput or multi-region), Cloud Spanner, PostgreSQL (single-region), MySQL (only if you cannot use PostgreSQL, per docs) and in-memory for tests.
- APIs: gRPC first, optional HTTP gateway with OpenAPI, and a Watch API for relationship changes.
- Tooling: the
zedCLI, a playground, validation files with assertions for CI, an LSP, a Kubernetes operator, and since v1.56.0pkg/embeddedfor in-process checks. - Commercial edge: AuthZed sells Cloud, Dedicated and Enterprise products; audit logging is documented as exclusive to them.
SpiceDB fits when you need tight consistency control, very large relationship sets or multi-region datastores. Its trade-off is operational weight: a distributed service with its own datastore and dispatch layer, and configuration secrets that need care, as its advisories show.
Cerbos 0.56.0
Cerbos describes itself as an authorization layer with context-aware rules in YAML policies managed through git. The open-source Cerbos PDP is a stateless service that holds compiled policies in memory and answers two questions:
- CheckResources: can this principal perform these actions on these resources?
- PlanResources: which resources of kind X can this principal perform action Y on? The answer is a filter expression tree you convert into a database query, with adapters for Prisma, SQLAlchemy and other ORMs listed in the docs.
Policy kinds are resource policies, derived roles (roles computed from conditions, such as “same branch”), principal policies (per-user overrides), role policies and scoped policies for multi-tenant hierarchies. Conditions are CEL over request.principal, request.resource and auxiliary data such as JWT claims.
- Policy storage: disk, git (the docs’ preferred method), blob storage, Cerbos Hub, overlay, or SQLite3, PostgreSQL or MySQL for policies managed through the Admin API.
- Deployment: Kubernetes service, sidecar or daemonset, systemd or serverless. Default ports HTTP 3592 and gRPC 3593 on all interfaces.
- Safety defaults: Admin API off by default and authenticated when on; requests limited to 50 resources and 50 actions per resource.
- Testing:
cerbos compilevalidates policies and runs*_test.yamlsuites. - Commercial edge: Cerbos Hub adds playgrounds, fleet distribution and embedded PDP bundles.
Cerbos fits when your data already holds the facts and the hard part is keeping rules consistent, reviewable and testable across services. Its trade-off: it knows only what you send. If “admin of the bank that owns the branch that owns this file” needs three lookups, your application does them before calling Cerbos.
Model comparison: tuples and graphs vs policies and attributes
Concern
OpenFGA 1.22
SpiceDB 1.56.2
Cerbos 0.56
Shape
ReBAC service with its own tuple store
ReBAC service with its own relationship store
Stateless PDP; no business data stored
What you write
Model (DSL or JSON) plus tuples
Schema plus relationships
YAML policies (and optional schemas)
Where facts live
Inside OpenFGA’s datastore
Inside SpiceDB’s datastore
In your systems; sent per request
Attribute rules
Conditions (CEL) on tuples, context per request
Caveats (CEL) on relationships, context per request
CEL conditions are the core feature
Typical question
Check, ListObjects, ListUsers
CheckPermission, LookupResources, LookupSubjects
CheckResources, PlanResources
List filtering
Ask the service for object IDs
Ask the service for resource IDs, or bulk-check a page
Translate a query plan into your own SQL or ORM filter
Consistency concern
Cache staleness vs HIGHER_CONSISTENCY
ZedTokens and per-request consistency levels
Policy reload freshness; data freshness is yours
Change stream
ReadChanges
Watch
Not applicable (no relationship data)
Licence (tag)
Apache 2.0
Apache 2.0
Apache 2.0
Governance
CNCF Incubating
AuthZed-led open source
Cerbos-led open source
Symptom to shape
Symptom
Likely fit
Sharing, folders, teams, nested organisations, “inherit from parent”
OpenFGA or SpiceDB
Rules over amount, status, region, time, risk score that already sit in your tables
Cerbos
Strict “a revoked user must lose access before the next read” requirement
SpiceDB ZedTokens, or OpenFGA with caching off or HIGHER_CONSISTENCY on sensitive paths
List screens over millions of rows with mixed rules
Cerbos query plans if rules map to SQL; ReBAC lookups if access is mostly relationship-driven
Modelling the Nagpur example three ways
These snippets come from the drill, trimmed. Same rules each time: branch staff and admins of the owning bank can view a file; a listed approver can approve within their limit, unless blocked.
OpenFGA DSL:
model
schema 1.1
type user
type bank
relations
define admin: [user]
type branch
relations
define bank: [bank]
define manager: [user]
define officer: [user]
define member: officer or manager or admin from bank
type loan_file
relations
define branch: [branch]
define blocked: [user]
define approver: [user with within_limit]
define viewer: member from branch
define can_approve: approver but not blocked
condition within_limit(amount: int, limit: int) {
amount <= limit
}
SpiceDB schema:
definition branch {
relation bank: bank
relation manager: user
relation officer: user
permission member = officer + manager + bank->admin
}
caveat within_limit(amount int, limit int) {
amount <= limit
}
definition loan_file {
relation branch: branch
relation blocked: user
relation approver: user with within_limit
permission view = branch->member
permission approve = approver - blocked
}
Cerbos resource policy (derived role branch_staff checks request.principal.attr.branch == request.resource.attr.branch):
apiVersion: api.cerbos.dev/v1
resourcePolicy:
resource: "loan_file"
version: "default"
importDerivedRoles: [lending_roles]
rules:
- actions: ["view"]
effect: EFFECT_ALLOW
derivedRoles: [branch_staff, bank_admin_same_bank]
- actions: ["approve"]
effect: EFFECT_ALLOW
roles: [manager]
condition:
match:
all:
of:
- expr: request.principal.attr.branch == request.resource.attr.branch
- expr: request.resource.attr.amount <= request.principal.attr.approval_limit
- actions: ["approve"]
effect: EFFECT_DENY
roles: ["*"]
condition:
match:
expr: request.principal.id in request.resource.attr.blocked
Side by side the difference is plain. OpenFGA and SpiceDB store “Meera can approve LF-1001 up to ₹5 lakh” as a conditional relationship, and the request brings only the amount. Cerbos stores nothing about Meera; the request brings her branch, her limit and the file’s attributes. Both are valid; they move different responsibilities to different teams.
Consistency and caching
This is where Zanzibar-style systems earn their complexity, and where teams most often misread the docs.
- OpenFGA: queries accept
MINIMIZE_LATENCY(default) orHIGHER_CONSISTENCY. Caching is off by default, and then every query reads the datastore, so results are strongly consistent in either mode. Once you enable the Check or iterator caches,MINIMIZE_LATENCYmay miss a tuple you just wrote;HIGHER_CONSISTENCYskips the cache. Read replicas (PostgreSQL, and MySQL from v1.22.0) add another lag source. - SpiceDB: every write returns a ZedToken and most reads default to
minimize_latency. To make a check see a given write, passat_least_as_freshwith that token, and store the token with the resource when it changes.fully_consistentexists but costs latency. - Cerbos: there is no relationship data to go stale. The questions are how quickly a policy change reaches every PDP (git or blob poll interval, Hub, or Admin API reload) and whether the attributes you send are current. v0.56.0 fixed an Admin API reload with
wait=truethat returned before compilation finished.
The dangerous case for Nagpur is revocation: a manager moved out of a branch at 11 AM approves a file at 11:01 AM. In a ReBAC system, decide per API path whether that window is acceptable. In a PDP design, the window is however stale your own user data is when you build the request.
Conditions and ABAC: three CEL dialects
All three use Google’s Common Expression Language (CEL) for attribute rules, but they treat missing data differently, and the drill made that concrete:
- OpenFGA conditions have typed parameters. Docs cap tuple condition context at 32 KB, requests at 512 KB and CEL evaluation cost at 100 by default. A Check without the required
amountreturned HTTP 400 with avalidation_error, notallowed: false. - SpiceDB caveats are typed too. A check without context returned
**PERMISSIONSHIP_CONDITIONAL_PERMISSION**withmissingRequiredContext: ["amount"]. Treat that as “not allowed yet”. - Cerbos conditions are the core language. By default a CEL runtime error (missing attribute, mismatched types) makes the condition “not satisfied”. For an ALLOW rule that fails closed; for a DENY rule the deny is skipped. The engine docs say this plainly and offer
engine.strictEvaluation: true(defaultfalse) andcerbos compile --strict-evaluation. In the drill, a file withoutrisk_scorewas approved under defaults despite a “deny if risk score above 80” rule.
Three systems, three behaviours for the same mistake: an error, a third state, and a silent skip. Whichever you choose, write tests for missing attributes and make your client code fail closed on anything that is not an explicit allow.
Performance notes without invented benchmarks
I am not publishing latency or throughput numbers. Vendor pages carry their own claims (SpiceDB’s README links one from AuthZed’s blog); those are the vendor’s own runs, and the drill below measured no performance. What the docs say is still useful:
- OpenFGA: the production guide recommends a small pool of larger servers for better cache hit ratios, datastore pool settings matched to the database’s connection limit, caching only where staleness is acceptable, and RPC histograms for per-endpoint latency.
- SpiceDB: performance depends on the datastore and the dispatch cluster, where servers share sub-problems through a consistent hash ring. Very wide relations once triggered an overflow bug (CVE-2024-27101). For lists, docs compare LookupResources (small result sets) with CheckBulkPermissions over a page of candidates.
- Cerbos: evaluation is in-memory over compiled policies, so the costs are request size, policy count and the round trip. A sidecar removes a network hop at the cost of one PDP per pod.
Measure with your model and traffic. A Check that walks a five-level hierarchy with exclusions is a different workload from a flat RBAC check.
Language SDKs and integration patterns
Area
OpenFGA
SpiceDB
Cerbos
Official SDKs (docs)
Node.js, Go, .NET, Python, Java
gRPC clients for Go, Node, Python, Ruby, Java, .NET
Go, Java, JavaScript, .NET, PHP, Python, Ruby, Rust
Protocols
HTTP and gRPC
gRPC; optional HTTP gateway with OpenAPI
HTTP and gRPC
CLI
fga
zed
cerbos, cerbosctl
Embedded option
Go library
pkg/embedded (since v1.56.0)
Embedded PDP via Cerbos Hub (commercial)
Config as code
Terraform provider; model files in git
Schema files and validation YAML in git
Policies in git; cerbos compile in CI
Integration patterns that work regardless of choice:
- Enforce in one layer per request path. A middleware or interceptor calls the service and fails closed on timeout or error. A gateway can do coarse checks; object-level checks belong near the code that loads the object.
- Keep relationship writes in step with business writes. With OpenFGA or SpiceDB, creating a loan file also means writing its
branchrelationship. Choose outbox, change-data-capture or dual-write, and test the failure path. - Separate permissions from release toggles. Feature flags decide whether a feature exists for a cohort; authorization decides whether a person may act on a record. The feature flags post covers the other half.
- Test models and policies like code.
fga model test, SpiceDB validation files andcerbos compilesuites run without a cluster. Put them in CI from the first pull request.
Observability
- OpenFGA: Prometheus metrics on
0.0.0.0:2112/metricsby default, withopenfga_*series for caches and datastore plus gRPC server metrics;--metrics-enable-rpc-histogramsadds per-endpoint latency. OTLP tracing is off until--trace-enabled; the production guide suggests a low sample ratio and JSON logs. - SpiceDB: a metrics server (default
:9090) exposes/metricsand/debug/pprof. Since v1.56.0 a counter splits Check results by permissionship (spicedb_v1_check_permissionship_total). OpenTelemetry tracing and JSON logs are built in. Keep that port private: CVE-2023-29193 came from pprofcmdlineon it. - Cerbos: Prometheus metrics at
/_cerbos/metricsby default, OTLP metrics and traces through OpenTelemetry environment variables (traces stay off until an endpoint is set). Audit logs record decisions to a file, Kafka or Cerbos Hub, and each response carries acerbosCallIdto join with your application logs.
A minimum dashboard: request rate and latency by API, error rate separately from deny rate, cache hit ratio, datastore latency for the ReBAC options, and a count of “conditional” or “CEL error” outcomes. A sudden rise in denies after a deploy is as much an incident as a rise in 5xx.
Security (verified advisories)
I pulled each project’s GitHub security advisories through the API on 14 October 2026 and, for every CVE ID below, confirmed one NVD record and an HTTP 200 on cve.org. Severity labels are GitHub’s.
OpenFGA (selected from 29 advisories)
ID
Severity
Affected
Fixed
Notes
CVE-2024-42473 / GHSA-3f6g-m4hr-59h8
High
v1.5.7, v1.5.8
v1.5.9
Check bypass with but not, from and a userset
CVE-2026-40293 / GHSA-68m9-983m-f3v5
Medium
v0.1.4 to v1.13.1
v1.14.0
Preshared key embedded in /playground HTML when the playground is enabled and reachable
CVE-2026-48096 / GHSA-8396-jffm-qx4w
Medium
up to v1.15.1
v1.16.0
Cache key collision when shared and ListObjects iterator caches are enabled
CVE-2026-55689 / GHSA-hcxc-wf8j-23hv
Medium
up to v1.17.1
v1.18.0
OIDC aud not validated when no audience configured; 1.18.0 refuses to start without one
CVE-2026-61709 / GHSA-g3pg-frfm-pr2m
Medium
up to v1.18.0
v1.18.1
ListUsers returns an excluded user in a specific intersection, exclusion and wildcard shape
GHSA-5278-rrxc-mgf7 (no CVE in API)
Medium
v1.15.0 to before v1.19.0
v1.19.0
StreamedListObjects returns an excluded object when the subtract side errors
GHSA-h7w8-xr72-cv4r (no CVE in API)
Medium
before v1.20.0
v1.20.0
ListUsers with nested exclusion over a wildcard
The pattern matters more than any row. Most OpenFGA advisories are wrong answers for particular model shapes, especially exclusions (but not) with wildcards or list APIs. For an authorization service a wrong “yes” is a security bug, and the project treats it that way. If you use exclusions and enforce with ListObjects or ListUsers, stay close to the latest release.
SpiceDB (selected from 18 advisories)
ID
Severity
Affected
Fixed
Notes
CVE-2023-29193 / GHSA-cjr9-mr35-7xh6
High
before 1.19.1
1.19.1
Preshared key visible through /debug/pprof/cmdline on an exposed metrics port when passed as a flag
CVE-2024-27101 / GHSA-h3m7-rqc4-7h9p
High
before 1.29.2
1.29.2
Integer overflow with more than 65,535 relationships of one kind; missed results or panic
CVE-2026-40091 / GHSA-jf4f-rr2c-9m58
Medium
1.49.0 to 1.51.0
1.51.1
Datastore DSN with password printed in the startup log at info
CVE-2026-55866 / GHSA-4vrg-r928-h5vv
Low
after 1.34.0, before 1.54.0
1.54.0
Under concurrency, a caveated permission could return HAS instead of CONDITIONAL
GHSA-5784-6qcr-48fq (no CVE in API)
Low
up to 1.54.0
1.56.0
in_cidr caveat did not match IPv4-mapped IPv6 addresses
GHSA-jf7w-wx43-32gc (no CVE in API)
Low
1.54.0 to before 1.56.2
1.56.2
Traces include serve arguments, so datastore secrets passed as flags leak; rotate credentials
SpiceDB’s pattern is different: secrets leaking through metrics, logs or traces when passed as command-line flags. Pass secrets through environment variables or files, keep metrics ports private, and rotate credentials after upgrading; the Vault and OpenBao post covers the secrets side.
Cerbos
The repository advisories API returned no entries, the GitHub advisory database had nothing for github.com/cerbos/cerbos, OSV returned nothing for the Go module, and NVD keyword search for “cerbos” returned zero results on 14 October 2026. I am not claiming any CVE for Cerbos. Two non-advisory items still matter: the default behaviour that skips an erroring DENY rule (use strictEvaluation), and the v0.56.0 fix for principal-policy deny rules ignored by the query planner. If you use PlanResources with principal-policy denies, upgrade.
Shared controls
Whatever you pick: keep admin, playground and metrics ports private; authenticate the API (preshared key or OIDC with an audience for OpenFGA, preshared key for SpiceDB, mTLS or a private network for Cerbos); disable telemetry if policy requires (--telemetry-endpoint="" for SpiceDB, CERBOS_NO_TELEMETRY=1 for Cerbos); and treat every non-allow, error or timeout as deny.
Decision guide
Situation
Prefer
Avoid thinking
Nested organisations, sharing, teams, folders, inheritance
OpenFGA or SpiceDB
“We will pass the whole hierarchy as attributes”
Rules over attributes already in your database and tokens
Cerbos
“We must copy every loan row into a tuple store”
Foundation governance is a procurement requirement
OpenFGA (CNCF Incubating)
Assuming every open-source project is foundation-owned
Multi-region, strict revocation, very large relationship sets
SpiceDB (with CockroachDB or Spanner)
“Eventually consistent is fine for authorization” by default
Small team, one database, mostly RBAC with some ABAC
Cerbos
Running a distributed ReBAC cluster for ten roles
List screens must filter in SQL
Cerbos PlanResources
Calling Check per row in a loop
List screens over relationship-driven access
OpenFGA ListObjects or SpiceDB LookupResources
Assuming list APIs are as battle-tested as Check; read the advisories
Need both relationships and rich policies
ReBAC for relationships, PDP or conditions for rules; keep the boundary written down
Three engines with overlapping rules
For the invented Nagpur SaaS: start with Cerbos for approval limits and risk rules, because the loan table already holds those facts and the banks want reviewable policy files. Adopt OpenFGA or SpiceDB only if cross-bank sharing and delegation grow into real graphs; then OpenFGA if CNCF governance and simpler operations matter more, SpiceDB if consistency controls and multi-region datastores matter more.
Checklist
- Write down which permissions depend on relationships and which depend on attributes before choosing a product.
- Pin exact versions (OpenFGA, SpiceDB, Cerbos tags) and verify release checksums in your build.
- Confirm the LICENSE at the tag you deploy (all three are Apache 2.0 today) and note which features are commercial-only.
- Decide your consistency requirement per API path, especially for revocation, and configure caches or ZedTokens to match.
- For ReBAC, design how relationship writes stay in step with business writes, including failure and replay.
- For Cerbos, decide where policies live (git, blob, database) and how quickly every PDP must pick up changes.
- Add model or policy tests to CI (
fga model test, SpiceDB validation,cerbos compile), including missing-attribute cases. - Make clients fail closed on errors, timeouts, conditional results and anything that is not an explicit allow.
- Keep playground, admin, metrics and pprof ports private; pass secrets by environment or file, never as flags.
- Re-read advisories on upgrade day, especially if you use exclusions, wildcards, list APIs or caveats.
Common mistakes
- Treating OpenFGA, SpiceDB and Cerbos as three interchangeable products and running a bake-off on a flat RBAC example that favours none.
- Moving every attribute into relationship tuples, then fighting stale copies of data you already had.
- Sending the whole organisation graph to a PDP on every request because “Cerbos is simpler”.
- Leaving the OpenFGA playground enabled on a reachable host (CVE-2026-40293).
- Running OpenFGA with OIDC but no audience on versions before 1.18.0 (CVE-2026-55689).
- Passing SpiceDB datastore URIs or preshared keys as command-line flags and exposing metrics, logs or traces (CVE-2023-29193, CVE-2026-40091, GHSA-jf7w-wx43-32gc).
- Treating SpiceDB
CONDITIONAL_PERMISSIONor an OpenFGA condition error as “allowed”. - Relying on a Cerbos DENY rule whose condition can error on missing attributes, with strict evaluation off.
- Calling Check in a loop for list screens instead of using ListObjects, LookupResources or PlanResources.
- Using feature flags or UI button hiding as the authorization layer.
A drill I ran on 14 October 2026
Environment: shared Linux VM (x86_64), no Docker, no Kubernetes, no external database. Port 8766 left alone for the existing static file server. All drill servers bound to 127.0.0.1 on high ports and were stopped afterwards. Artefacts are under /workspace/revisit/drill38/. Times: about 6:28 AM to 6:33 AM IST.
What I did:
- Downloaded the official Linux amd64 archives for OpenFGA v1.22.0, the fga CLI v0.8.1, SpiceDB v1.56.2 and Cerbos v0.56.0; every SHA-256 matched the release’s
checksums.txt. - OpenFGA offline:
fga model teston the model above with eight tuples passed 7 of 7 checks and 1 of 1 ListObjects. A copy with one deliberately wrong assertion failed with exit code 1, as CI needs. - OpenFGA live (memory datastore, preshared key, playground off):
/storeswithout the key returned 401. Checks gave the expected answers: officer can view, outsider cannot, manager can approve ₹3 lakh but not ₹9 lakh, blocked approver cannot. A Check withoutamountreturned HTTP 400./metricsreturned 200. - SpiceDB live (memory datastore, key from an environment variable, telemetry off): unauthenticated schema read returned 401. Using the write’s ZedToken as
atLeastAsFresh, checks matched OpenFGA’s, and the check without context returnedCONDITIONAL_PERMISSION. LookupResources returned the file; the permissionship counter showed 3 HAS, 3 NO and 1 CONDITIONAL. - Cerbos tests:
cerbos compileran 20 tests, all passing. The default suite deliberately expects ALLOW for a file withoutrisk_score(the erroring deny rule is skipped); the strict suite expects DENY. The default suite run with--strict-evaluationgave 9 passes and 1 failure. - Cerbos live (disk storage, decision audit to file): CheckResources allowed ₹3 lakh, denied ₹9 lakh, and allowed the file without
risk_scorewhile loggingError evaluating CEL expression ... no such key: risk_score. PlanResources returned a conditional filter tree./_cerbos/metricsreturned 200.
What I did not run: no real datastores or replicas, no multi-node dispatch, no Kubernetes, no OIDC or TLS, no Cerbos Hub, no SDK code, no load test and no advisory reproduction. No latency or throughput number comes from this drill.
The useful finding is not speed but failure semantics: the same missing attribute produced an error in OpenFGA, a third state in SpiceDB and a silently skipped deny in Cerbos under defaults. That alone justifies a fail-closed client and a missing-attribute test, whatever you choose.
Unlearn
- Unlearn the idea that “authorization service” is one product category. ReBAC stores and policy PDPs move different things.
- Unlearn that a Zanzibar-style system is automatically consistent. Caches, replicas and consistency modes decide what you see.
- Unlearn that CEL conditions behave the same everywhere. Missing data is an error, a third state or a skipped rule, depending on the product.
- Unlearn hiding buttons in the UI as access control.
- Unlearn passing secrets as command-line flags to infrastructure services.
Re-learn
- Re-learn the Zanzibar vocabulary: objects, relations, subjects, tuples, and the new enemy problem.
- Re-learn RBAC and ABAC as policy patterns you can express, review and test as code.
- Re-learn version pins from GitHub releases, with IST timestamps and checksums recorded in your runbook.
- Re-learn advisory reading for authorization engines: a wrong “yes” in a rare model shape is a security bug.
- Re-learn that list filtering is part of authorization design, not an afterthought.
Closing
Authorization in 2026 is still a wording trap. OpenFGA and SpiceDB are Zanzibar-inspired relationship stores that walk a graph you keep inside them, OpenFGA under CNCF governance and SpiceDB with finer consistency control and heavier datastores. Cerbos is a policy decision point that keeps no business data and evaluates YAML and CEL rules over the facts you send. The Nagpur review becomes productive only after separating those jobs. Learn what each shape centralises. Unlearn three-way bake-offs on a toy RBAC example. Re-learn consistency, conditions and failure semantics on the exact version you deploy. Practise with a small model, a test file and a deliberately missing attribute on a loopback port. Apply the shape that matches where your permission data actually lives, and stop hard-coding the next permission check.
Sources
OpenFGA
- openfga/openfga releases
- OpenFGA v1.22.0
- OpenFGA v1.21.0
- OpenFGA v1.20.0
- OpenFGA v1.0.0
- OpenFGA README (v1.22.0)
- OpenFGA LICENSE (Apache 2.0)
- OpenFGA documentation
- OpenFGA concepts
- OpenFGA configuration language
- OpenFGA conditions
- OpenFGA query consistency
- OpenFGA ListObjects
- OpenFGA ListUsers
- OpenFGA configuration (authn, metrics, tracing)
- OpenFGA running in production
- OpenFGA testing models
- OpenFGA SDK install
- OpenFGA CLI v0.8.1
- CNCF project page: OpenFGA
- GHSA-3f6g-m4hr-59h8 / CVE-2024-42473
- GHSA-68m9-983m-f3v5 / CVE-2026-40293
- GHSA-8396-jffm-qx4w / CVE-2026-48096
- GHSA-hcxc-wf8j-23hv / CVE-2026-55689
- GHSA-g3pg-frfm-pr2m / CVE-2026-61709
- GHSA-5278-rrxc-mgf7
- GHSA-h7w8-xr72-cv4r
- CVE-2024-42473 (cve.org)
- CVE-2026-40293 (cve.org)
- CVE-2026-48096 (cve.org)
- CVE-2026-55689 (cve.org)
- CVE-2026-61709 (cve.org)
SpiceDB
- authzed/spicedb releases
- SpiceDB v1.56.2
- SpiceDB v1.56.0
- SpiceDB v1.0.0
- SpiceDB README (v1.56.2)
- SpiceDB LICENSE (Apache 2.0)
- SpiceDB schema language
- SpiceDB consistency
- SpiceDB caveats
- SpiceDB datastores
- SpiceDB observability
- SpiceDB validation, testing and debugging
- SpiceDB Watch API
- SpiceDB client libraries
- SpiceDB Kubernetes operator
- SpiceDB and Zanzibar
- GHSA-cjr9-mr35-7xh6 / CVE-2023-29193
- GHSA-h3m7-rqc4-7h9p / CVE-2024-27101
- GHSA-jf4f-rr2c-9m58 / CVE-2026-40091
- GHSA-4vrg-r928-h5vv / CVE-2026-55866
- GHSA-5784-6qcr-48fq
- GHSA-jf7w-wx43-32gc
- CVE-2023-29193 (cve.org)
- CVE-2024-27101 (cve.org)
- CVE-2026-40091 (cve.org)
- CVE-2026-55866 (cve.org)
Cerbos
- cerbos/cerbos releases
- Cerbos v0.56.0
- Cerbos v0.56.0 release notes
- Cerbos v0.55.0 release notes
- Cerbos README (v0.56.0)
- Cerbos LICENSE (Apache 2.0)
- Cerbos policies
- Cerbos resource policies
- Cerbos derived roles
- Cerbos principal policies
- Cerbos scoped policies
- Cerbos conditions
- Cerbos validating and testing policies
- Cerbos engine configuration (strict evaluation)
- Cerbos server configuration
- Cerbos storage configuration
- Cerbos audit configuration
- Cerbos observability
- Cerbos telemetry
- Cerbos API
- Cerbos Admin API
- Cerbos Kubernetes sidecar deployment
Background
- Zanzibar: Google’s Consistent, Global Authorization System (Google Research)
- Zanzibar at USENIX ATC 2019
- Common Expression Language specification
Internal
Releted Posts
Data pipeline orchestration in 2026: revisit Apache Airflow, Dagster and Prefect before you schedule another DAG
Picture a two-wheeler loan company in Coimbatore with about ninety branches (an invented example, not a real client). Its data team of five runs about 140 cron jobs on two Linux VMs: shell scripts that pull collections files from a payment gateway SFTP, Python scripts that clean them, and SQL that loads a PostgreSQL reporting database every night.
Read moreFeature flags in 2026: revisit Unleash, Flagsmith and GrowthBook before you hard-code another release toggle
Picture a test-preparation and online classes platform in Jaipur (an invented example, not a real client). Students use an Android app, an iOS app and a React web app.
Read moreLakehouse table formats in 2026: revisit Apache Iceberg, Delta Lake and Apache Hudi before you pick another format
Picture a payments and lending analytics platform in Hyderabad (an invented example, not a real client). Card authorisations and EMI schedules still land in PostgreSQL 16.
Read more