OpenFGA vs SpiceDB vs Google Zanzibar: the short answer
Google Zanzibar is not a product you install beside your vector database. It is the reference architecture: a globally distributed authorization system built around relationships, graph evaluation, consistency, caching, and client cooperation.
OpenFGA and SpiceDB are public, Zanzibar-inspired systems that let engineering teams build a similar style of fine-grained authorization service. For enterprise RAG, either can be a strong choice, but the right decision depends less on the brand name than on the shape of your authorization workload.

If your RAG system only needs a simple tenant filter, neither tool may be necessary. If users inherit access through nested groups, folders, projects, workspaces, or delegated agents, a relationship graph becomes much more attractive. If the system must list every document a user can access before semantic retrieval, reverse lookups and freshness controls matter as much as ordinary permission checks.
That is the comparison this guide focuses on.
What is the central distinction?
What makes ReBAC useful for RAG?
Which product should you choose?
Why enterprise RAG turns authorization into a retrieval problem
A normal RAG pipeline asks a relevance question:
Which chunks are most similar to this query?
An enterprise RAG pipeline has to answer a second question before it can safely use those chunks:
Which of those chunks may this principal see right now?
Those are different decisions. A vector index knows about embeddings, distances, and metadata. It does not automatically understand that Alice can access a document because she belongs to the engineering group, that the document inherits access from a parent workspace, or that her access was revoked thirty seconds ago.

That is why document-level authorization in RAG is not a prompt-engineering problem. Telling the model not to reveal confidential content is too late if the confidential chunk has already entered the context window.
The authorization boundary belongs between identity and retrieval, or immediately after candidate retrieval, before context assembly. In practice, a secure pipeline looks more like this:
- Authenticate the human, service, or agent making the request.
- Resolve the principal and tenant context from trusted identity data.
- Evaluate the relationship-based permission for the source document.
- Apply the result to vector retrieval, candidate filtering, or both.
- Send only authorized context to the LLM.
- Record the decision, policy/model version, freshness boundary, and retrieved source IDs.
This is also why a vector metadata filter is not automatically an authorization system. A filter such as tenant_id = acme can be useful, but it is only as trustworthy as the server-side identity binding and policy decision that produced it.
What Google Zanzibar actually contributes
Google’s Zanzibar paper describes a large internal authorization system used across Google’s services. Its importance is not just the tuple syntax. Zanzibar combines a relationship model with a distributed serving architecture, graph evaluation, caching, consistency controls, quotas, and client-side protocols.
A simple relationship can be represented conceptually as:
document:report-42#viewer@user:alice
That says Alice is a viewer of the document. But enterprise permissions rarely stop there. A document may inherit access from a folder. A folder may be shared with a group. A user may be a member of that group through another group. Effective access is therefore computed from a graph rather than copied into a flat ACL for every user.

The crucial distinction is between a stored relationship and an effective permission. The stored data might say:
document:report-42belongs tofolder:finance;folder:financeis visible togroup:auditors;user:aliceis a member ofgroup:auditors.
The authorization service then computes whether Alice can view the document.
The paper reports historical measurements from Google’s production system, including trillions of ACLs, millions of authorization requests per second, sub-10-millisecond p95 latency, and availability above 99.999% over a multi-year period. Those figures describe Google’s workload and implementation at the time of the paper. They are not benchmarks for OpenFGA, SpiceDB, or your future RAG service.
For a fuller explanation of tuples, usersets, zookies, and the difference between the model and the serving system, see Vertex Frontier’s Google Zanzibar architecture guide. This article will use that foundation and move directly to the product and RAG decisions.
Zanzibar, OpenFGA, and SpiceDB are not interchangeable names
It is tempting to write āZanzibar versus OpenFGA versus SpiceDBā as if three products were competing in the same category. That framing is misleading.
- Google Zanzibar is a published design and a Google-internal authorization system.
- OpenFGA is a public, open-source authorization solution inspired by Zanzibar and currently described by the project as a CNCF incubating project.
- SpiceDB is an open-source, Zanzibar-inspired authorization database/service developed by AuthZed.
The shared idea is relationship-based authorization. The implementation details are not automatically shared. A model language, API behavior, datastore choice, consistency preference, cache strategy, deployment model, or managed-service boundary can differ substantially. Check the current release documentation before treating any API name, default, supported datastore, product tier, or integration as stable.
The project’s current OpenFGA homepage identifies OpenFGA as open source and a CNCF incubating project. That status is a project fact, not evidence that OpenFGA and SpiceDB have identical governance, APIs, or operational guarantees.
The decision that matters: what must your RAG authorization service answer?
Before comparing products, classify the calls your application actually needs.

1. Single-object checks
āCan Alice read document:report-42?ā is the familiar authorization call. It is necessary, but it is not the whole RAG problem.
A post-filtered RAG pipeline may retrieve twenty candidate chunks, map each one to its parent document, and issue a batch of checks. The important details become batch behavior, latency, over-fetching, and what happens when a check fails or times out.
2. Reverse lookups
āWhich documents can Alice read?ā is a different workload. It is useful when the vector store supports metadata filtering and the number of authorized documents is manageable.
It is also useful for interfaces such as a permission-aware document browser, an index-building job, or an agent that needs to discover the resources available to a principal.
This is one reason Zanzibar-derived systems appeal to teams with graph-shaped permissions: they index relationships for lookup in both directions rather than treating authorization as a policy evaluation loop over every row.
3. Relationship traversal
Can access come from a parent folder, group, workspace, project, organization, or delegated owner? If yes, you are not just storing a boolean on a document. You are evaluating a graph.
4. Freshness-sensitive checks
How quickly must a revocation take effect? If a user is removed from a group, can a cached answer remain valid for a few seconds? What about a legal hold, an HR document, a security incident, or an agent acting on behalf of a user?
The answer changes the consistency mode, cache policy, event architecture, and test plan.
| RAG authorization question | Why it matters | Decision pressure |
|---|---|---|
| Can this principal view this document? | Protects each candidate before context assembly. | Check latency, batching, and fail-closed behavior. |
| Which documents can this principal view? | Enables pre-filtered semantic search and permission-aware listings. | Reverse lookup, cardinality, and filter size. |
| Did access change before this answer? | Limits stale-authorization and revocation exposure. | Consistency tokens/preferences, cache rules, and source lag. |
| Where did this relationship come from? | Makes synchronization and incident investigation possible. | Source of truth, event replay, idempotency, and audit evidence. |
Product selection should follow the authorization queries your RAG architecture must answer, not the other way around.
OpenFGA for enterprise RAG
OpenFGA’s official authorization concepts describe a relationship-based model inspired by Zanzibar. Its official RAG authorization guide is especially useful because it treats the vector database as a separate retrieval component and shows where authorization fits around it.
The guide models folders, documents, owners, and viewers. A document can inherit viewer access from its folder while still supporting a direct document-level grant. That is a realistic enterprise pattern: permissions are often inherited, but exceptions exist.

An explicit ReBAC model for the document boundary
The following is an OpenFGA model sketch adapted from the structure in the official RAG guide. It is included to make the relationship graph concrete. Validate the schema against the OpenFGA version and model language you deploy; do not paste it into SpiceDB unchanged.
Illustrative OpenFGA DSL model ā verify before deployment:
model
schema 1.1
type user
type folder
relations
define owner: [user]
define viewer: [user] or owner
type document
relations
define folder: [folder]
define owner: [user]
define viewer: [user] or owner or viewer from folderThe important design choice is not the exact spelling. It is the inheritance path: a document can be visible because of a direct grant, ownership, or the viewer relation on its parent folder. Every vector chunk derived from that document should retain a stable document_id that the retrieval layer can map back to this authorization object.
OpenFGA’s two documented RAG patterns
Post-filtering retrieves candidates first, then checks each candidate, or a batch of candidates, against the authorization model. OpenFGA documents BatchCheck for this use case.
This pattern is attractive when the vector store returns a reasonably small candidate set. It is also easy to add to an existing retriever because the authorization step sits between retrieval and generation.
The cost is that unauthorized documents consume retrieval capacity. If you need five authorized results and two-thirds of the candidates are denied, you must over-fetch. That raises query cost and can still leave the LLM with fewer or weaker results.
Pre-filtering asks the authorization service for the objects the user may access, then passes those document IDs into the vector query as a metadata filter. OpenFGA documents this pattern with ListObjects.
Pre-filtering can produce a cleaner security and ranking boundary because the vector search considers only authorized objects. But it can become expensive or impractical when the user is allowed to access a very large portion of the corpus, or when the authorized ID list is too large for the vector store’s filter mechanism.
OpenFGA is therefore a natural fit when you want a clear authorization model, a framework-agnostic RAG integration, and explicit check/list operations. The hard work remains outside the permission check: keeping relationships synchronized, binding the request to the correct principal, and ensuring the vector filter cannot be widened by user input.
Here is the same boundary as framework-neutral pseudocode. It is intentionally not an SDK example: method names and response shapes vary by client library and release.
RAG authorization boundary ā illustrative pseudocode:
principal = authenticate(request)
query = request.query
candidates = vector_store.search(
query=query,
filter=trusted_tenant_filter(principal)
)
checks = [
{"user": principal.id,
"relation": "viewer",
"object": chunk.document_id}
for chunk in candidates
]
allowed = authorization_service.batch_check(checks)
context = [
chunk for chunk, decision in zip(candidates, allowed)
if decision == "allowed"
]
if authorization_service.is_unavailable:
context = [] # fail closed for protected context
return llm.answer(query, context=context)The batch_check name above is a placeholder for the client operation you verify in your chosen product and SDK. The security property is the ordering: identity is trusted first, candidates are tied to source resources, authorization is evaluated, and only then is context assembled.
SpiceDB for enterprise RAG
SpiceDB takes a similar graph-based approach, with an open-source authorization service designed around relationship data and permission computation. Its secure RAG tutorial demonstrates pre-filtering and post-filtering with authorized object IDs and connects the workflow to Pinecone, OpenAI, and LangChain.

The equivalent SpiceDB schema
The following .zed schema expresses the same folder-to-document inheritance model shown in the OpenFGA sketch above. In SpiceDB, relation declarations describe stored relationships, while permission declarations compute the subjects allowed to perform an action. The -> arrow follows the document’s folder relation to the folder’s computed view permission.
SpiceDB .zed schema ā equivalent inheritance model:
definition user {}
definition folder {
relation owner: user
relation viewer: user
permission view = viewer + owner
}
definition document {
relation folder: folder
relation owner: user
relation viewer: user
permission view = viewer + owner + folder->view
}This is a schema example, not a complete production authorization model. Validate it with the current SpiceDB schema reference and your deployed release. The conceptual equivalence is the important part: OpenFGA uses define expressions such as viewer from folder, while SpiceDB separates stored relation values from computed permission values and uses an arrow such as folder->view for inheritance.
The tutorial reflects an important production reality: enterprise RAG authorization is not only about whether a user can read a document. It is also about making the permission result useful to the retrieval system.
The associated vendor demonstration of ReBAC authorization with a LangChain-SpiceDB integration shows two users receiving different answers from the same document corpus because their relationship graphs expose different documents. That is a useful illustration of the boundary: the model is not asked to guess who should see what; the retrieval pipeline is given an authorized set before generation.
The demonstration is vendor-produced, so it should be read as an integration example rather than an independent performance study. Still, it captures the core implementation pattern well.
SpiceDB’s consistency lens
SpiceDB’s consistency documentation discusses the danger of stale authorization decisions and the āNew Enemy Problemā: a subject loses access, but a stale read or cache still allows the old relationship to be used.
SpiceDB uses ZedTokens to let callers express a minimum freshness boundary for a read. That does not mean every request must use the strictest possible consistency. It means the application can make freshness part of the request contract rather than treating it as an invisible cache detail.
For RAG, this distinction matters. A cached answer may be acceptable for a low-risk internal knowledge question, while a response involving a revoked legal document or a sensitive personnel record may require a stronger freshness boundary, or a full refusal when freshness cannot be established.
The product choice is not āeventual consistency versus perfect consistency.ā The useful question is: Which operations need which freshness guarantee, and how will you test that guarantee at the retrieval boundary?
OpenFGA vs SpiceDB: a practical comparison
The following table is deliberately decision-oriented. It avoids claiming a universal winner because the answer depends on the model, deployment, and RAG workload.
| Criterion | OpenFGA | SpiceDB | Google Zanzibar |
|---|---|---|---|
| What it is | Public Zanzibar-inspired authorization service. | Open-source Zanzibar-inspired authorization database/service. | Google’s internal global authorization system and published design. |
| RAG integration | Official guide covers pre-filtering with ListObjects and post-filtering with BatchCheck. | Official tutorials cover authorized ID filtering, Pinecone, and LangChain patterns. | Reference architecture, not a drop-in RAG product. |
| Freshness model | Use documented consistency options and verify exact behavior for the deployed version/datastore. | ZedTokens and configurable consistency are central to the documented design. | Zookies and causal consistency are part of the published system design. |
| Best comparison question | Do we want a clear public model/API and a framework-agnostic integration path? | Do we want SpiceDB’s relationship database, lookup model, and tunable freshness controls? | Which Zanzibar design principles must our system reproduce? |
| Main operational risk | Assuming a model/API feature solves synchronization or fail-closed retrieval automatically. | Treating consistency configuration as a background database detail rather than a security decision. | Assuming the paper’s Google-scale architecture is directly portable to an open-source deployment. |
The table is a starting point, not a benchmark. Product APIs, release behavior, datastore support, managed offerings, and licensing should be checked against the versions you plan to deploy. This article intentionally does not publish a price ranking: hosted tiers, infrastructure costs, support plans, and operating costs change, and a fair comparison requires the same workload and deployment assumptions.
A practical decision matrix
Use this matrix as a starting point for a proof of concept, not as a universal ranking. The same product may be a good fit for one authorization shape and a poor fit for another.
| Your main requirement | Start by evaluating | Do not assume |
|---|---|---|
| Simple document checks | Check latency, batch behavior, and post-filtering. | That a graph service is necessary if a trusted tenant filter solves the real problem. |
| Large authorized-object listings | Reverse lookup semantics, pagination, and vector-filter limits. | That a list of IDs will remain small enough for every vector database. |
| Strict revocation freshness | Consistency controls, tokens, cache behavior, and a revocation test. | That asynchronous synchronization has an acceptable lag without measurement. |
| Complex inherited access | Model expressiveness for folders, groups, projects, and delegation. | That roles or vector metadata can represent every relationship cleanly. |
| Minimal infrastructure | Managed options and whether a simpler policy is sufficient. | That managed authorization removes source sync, identity, and audit responsibilities. |
A comparison protocol you can actually run
If the decision is important enough to affect production architecture, run both systems against the same synthetic corpus instead of borrowing a vendor benchmark. Record the software release, client SDK version, datastore, hardware, graph shape, cache state, concurrency, and consistency setting.
| Test dimension | Hold constant | Measure | Why it changes the decision |
|---|---|---|---|
| Graph shape | Same users, groups, nesting, folders, and inheritance depth. | Check and lookup latency by graph shape. | A shallow demo can hide fan-out and recursive traversal costs. |
| RAG workload | Same corpus, chunk-to-document mapping, query set, and target K. | Authorized result count, rejection rate, p50/p95/p99 latency. | Post-filtering can change both ranking quality and context availability. |
| Freshness | Same grant, revoke, write, and read sequence. | Time until the decision reflects the change. | Revocation behavior is a security property, not just a latency metric. |
| Failure handling | Same injected timeout, stale event, missing metadata, and replay scenario. | Returned context, error classification, and recovery time. | The safest system is the one whose failure behavior is explicit and testable. |
Before publishing a result, confirm that the test used the same authorization model, users, groups, graph depth, document corpus, vector database, candidate count, datastore, consistency setting, and cache conditions.
Run both cold-cache and warm-cache cases, and record p50, p95, and p99 latency, authorization rejection rate, revocation propagation time, and failure recovery time.
Publish the results only with the raw conditions and test data. Without those details, use this protocol as a decision aid rather than claiming that one product is faster or more scalable.
An enterprise RAG ReBAC architecture blueprint
A permission service is one layer in the system, not the entire security architecture. The source systems still own documents and business relationships; the synchronization layer carries changes; the authorization service evaluates the graph; and the RAG gateway prevents unauthorized context from reaching the model.
The most important boundary is the RAG gateway. It should derive the principal and tenant scope from trusted identity, never from an arbitrary request field. It should map every chunk to a source resource, enforce the authorization decision before context assembly, and record enough evidence to explain why a document was included or denied.
The vector database security hardening guide covers the database and deployment controls around this flow. The ReBAC service does not replace network isolation, credential management, encryption, deletion, backups, or vector-store access controls.
Pre-filtering, post-filtering, or both?
There is no single retrieval pattern that wins for every corpus.

Pre-filtering
The authorization service returns an authorized object set. The vector database receives those IDs as a server-generated filter.
Use it when:
- users typically access a small or moderate set of documents;
- exact top-K results matter;
- the vector database handles trusted ID filters efficiently;
- the application can safely bind every vector record to a stable source document ID.
Be careful when:
- the authorized list becomes very large;
- group membership changes faster than the index or filter cache;
- a filter is assembled from user-supplied tenant or group values;
- the vector engine treats a malformed filter as no filter.
Post-filtering
The vector database returns candidates. The authorization service filters them before the LLM sees them.
Use it when:
- the candidate set is small;
- the user can access a large fraction of the corpus;
- the vector store cannot express a large authorized-ID filter efficiently;
- you need a straightforward retrofit around an existing retriever.
Be careful when:
- the denial rate is high and the model receives too little context;
- the pipeline does not over-fetch;
- latency grows linearly with candidate count;
- unauthorized content is logged, cached, or embedded into traces before filtering.
Hybrid enforcement
Many production systems should use both. Apply a coarse, trusted tenant or workspace boundary during vector search, then perform a fine-grained relationship check on the returned documents.
That design reduces candidate volume without making the vector filter the sole source of truth. The relationship service still makes the final document-level decision.
The part most comparisons underplay: relationship synchronization
The permission check is often the easy part. Keeping the graph current is the system you actually have to operate.
Suppose SharePoint changes a folder permission. Or a user leaves a group in your identity provider. Or a document moves from a general workspace into a restricted project. Several representations may now need to change:
- The source system’s permission record.
- The authorization graph.
- The document and chunk metadata in the vector store.
- Any permission-aware cache.
- The audit trail and downstream search indexes.
A useful architecture starts by naming one source of truth for business relationships. The authorization service can be the decision point without being the master database for every piece of business metadata.
A transactional outbox or CDC pipeline can publish relationship changes reliably. Events should be idempotent, replayable, observable, and associated with a source version or sequence. A failed consumer should not quietly create a permanent permission gap.

A practitioner discussion in r/microservices about SpiceDB/OpenFGA integration surfaced exactly these concerns: whether to synchronize asynchronously, how to recover from connector failures, why a separate relational source of truth can be valuable, and how cache recalculation affects perceived behavior. Those comments are anecdotal, not product guarantees, but they point to the right engineering questions.
A safer permission-sync checklist
Before production, answer these questions in writing:
- Which system owns each relationship?
- What event represents a grant, revoke, move, delete, or group change?
- Can the event be delivered twice without changing the outcome?
- How do you replay a day of events or rebuild the graph from source data?
- How do you detect a relationship that exists in the source but not in the authorization service?
- What happens to retrieval while the graph is behind?
- What happens to old chunks after a document is deleted?
- Can a user request a broader tenant or group filter than their identity allows?
- Which version of the permission state is recorded with the answer?
The answer should not be āthe cache will update eventually.ā You need a measurable propagation target and a defined fail-closed behavior when the target cannot be met.
Enterprise permission synchronization checklist
The authorization graph and retrieval index are only as current as the pipeline that feeds them. Before production, review this checklist with the team that owns the source systems, not only the team operating OpenFGA or SpiceDB.
A checked box is not proof that the pipeline is correct. Each item should have an owner, an observable signal, and a recovery procedure. This is where many implementations need a richer source system alongside the authorization service: the graph answers authorization questions, while the business system retains the broader document and relationship record.
Consistency is a RAG security property
A stale permission decision is not merely a performance quirk. It can become a data-disclosure path.
Imagine this sequence:
- Alice belongs to the finance group.
- The RAG index contains a finance document.
- Alice is removed from the group.
- A stale authorization read still says Alice is a viewer.
- The retriever sends the document to the LLM.
The vector search was working correctly. The model may answer accurately. The security decision was still wrong.

Google’s Zanzibar paper frames freshness as part of the authorization protocol. SpiceDB documents ZedTokens for expressing a freshness boundary. OpenFGA also exposes consistency-related behavior that must be evaluated against the actual deployment. The product names differ, but the design lesson is shared: freshness has to travel with the decision.
For a low-risk internal search, a slightly stale read may be acceptable if the threat model allows it. For a privileged workflow, a revocation-sensitive response, or an agent that can take an external action, the policy should be stricter. Do not use one global consistency setting because it is convenient.
A practical policy matrix might classify operations as:
- Standard search: bounded staleness may be acceptable; monitor the lag.
- Sensitive document retrieval: require a stronger freshness boundary and re-check before context assembly.
- Agent action or data export: require the strictest available check, short-lived authorization, and an auditable decision.
- Unknown or unavailable state: return no protected context rather than silently broadening access.
This complements the broader enterprise RAG security architecture guidance, but the product comparison adds the operational question: which system can express and verify the freshness contract your use case needs?
Negative-test matrix for permission-aware RAG
A happy-path test proves that an authorized user can receive an answer. It does not prove that the boundary works. Run negative tests with synthetic documents and identities before connecting sensitive data.
| Negative test | Expected result | Evidence to retain |
|---|---|---|
| Revoked user asks about a previously visible document. | No protected context is returned after the approved freshness boundary. | Revoke event, decision freshness, result IDs, and audit event. |
| User changes `tenant_id` in the request. | Tenant scope remains derived from trusted identity. | Original identity scope, rejected input, and final filter. |
| Document moves to a restricted folder. | Previous access is not silently retained beyond the declared lag. | Move event, graph revision, retrieval decision, and lag. |
| Deleted document still has vectors. | No authorized result is returned from the stale chunks. | Delete event, index state, and denied result. |
| Authorization service times out. | Protected context is denied or replaced by an explicitly approved bounded fallback. | Timeout classification, response, and alert. |
| Malformed or empty vector filter. | The query fails closed; it does not become unrestricted. | Filter validation result and query scope. |
| Cached answer belongs to another privilege scope. | The cache is not reused across incompatible identity or authorization scopes. | Cache key, principal scope, and denial event. |
| Agent requests an out-of-scope resource. | The request is denied and the delegation chain is logged. | Human, agent, tool, resource, and decision identifiers. |
These are test cases, not claims that a particular product automatically passes them. The application, synchronization layer, vector database, and authorization service must be tested together.
A before-and-after enterprise RAG design

Before: the attractive but unsafe shortcut
A team imports documents from several business systems into one vector index. Each chunk contains a title, department, and source URL. The query service receives a user token and a department field. It embeds the question, retrieves the nearest chunks, and asks the LLM to āavoid confidential information.ā
The design fails in several ways:
- the user-controlled department value may widen the search;
- group and folder inheritance is not represented;
- a revoked user may still match a cached result;
- chunks do not have a stable authorization identity;
- a document can be deleted in the source system while its vectors remain searchable;
- the logs show the question but not the permission decision actually enforced.
After: a relationship-aware retrieval boundary
The improved design authenticates the principal, derives tenant scope server-side, maps every chunk to a stable source document, and asks OpenFGA or SpiceDB for an authorization decision. It uses a trusted coarse filter where possible, then re-checks candidate documents before context assembly.
Permission changes flow from a declared source of truth through an idempotent, replayable pipeline. The system records the source version, authorization model version, decision, freshness boundary, and document IDs. A timeout or missing metadata results in zero protected context, not an unrestricted search.
The difference is not that the second design uses more sophisticated vocabulary. It has a clear boundary and an observable failure mode.
Common mistakes when implementing ReBAC for RAG

Mistake 1: Treating Google Zanzibar as a deployable product
The paper is an architectural reference. It does not give you Google’s internal storage, caches, indexes, quotas, client libraries, or production isolation model.
Better approach: Use the paper to understand the design constraints, then compare OpenFGA and SpiceDB using their current documentation and your own workload tests.
Mistake 2: Checking permissions after the LLM call
Post-generation redaction cannot undo the fact that a secret entered the prompt, model trace, cache, or provider boundary.
Better approach: Enforce the decision after retrieval and before context assembly at the latest.
Mistake 3: Using vector metadata as the source of truth
Metadata can accelerate filtering, but it can drift, be malformed, or be manipulated by an ingestion bug.
Better approach: Keep a stable source-resource identity and derive trusted filters from the authorization decision. Re-check candidates when the risk warrants it.
Mistake 4: Assuming asynchronous sync is automatically safe
Async propagation can be a good architecture. Silent gaps, non-replayable events, and unclear lag are not.
Better approach: Make event delivery observable, idempotent, replayable, and bounded by an explicit freshness target.
Mistake 5: Testing only the allow path
A demo where Alice sees her documents proves very little.
Better approach: Test revocations, cross-tenant queries, nested groups, missing metadata, stale caches, deleted documents, malformed filters, partial authorization outages, and agent delegation.
Mistake 6: Comparing latency without comparing the graph
A shallow graph with ten objects tells you nothing about a deeply nested enterprise graph with large groups and hot resources.
Better approach: Benchmark graph depth, fan-out, candidate count, cache warmth, consistency mode, concurrency, datastore, and synchronization lag together.
Mistake 7: Giving an agent the user’s identity without modeling delegation
An agent may act on behalf of a user, but it may also have its own workload identity and tools. Passing a user ID through the prompt is not delegation control.
Better approach: Model the human, application, agent, tool, tenant, and resource as separate principals or relationship subjects where appropriate. For the wider identity problem, see our guide to non-human identity and delegated authority.
A decision guide: when should you choose OpenFGA or SpiceDB?

Start with OpenFGA whenā¦
- you want a public Zanzibar-inspired authorization service with a clear model/API path;
- your team values the official documentation around RAG pre-filtering and post-filtering;
- you need a framework-agnostic authorization layer rather than a vector-database-specific feature;
- your team is comfortable validating consistency and datastore behavior for its chosen deployment.
Start with SpiceDB whenā¦
- you want an authorization database/service centered on relationship queries and graph evaluation;
- reverse lookups and permission-aware resource discovery are central to the product;
- tunable consistency and ZedTokens are important to the revocation model;
- the ecosystem, tooling, or managed options around AuthZed fit your operating model.
Choose neither yet whenā¦
- access is a simple, stable tenant or role filter;
- the team has not defined a source of truth for permissions;
- the vector index cannot preserve a reliable document identity;
- there is no plan for revocation, deletion, audit, or outage behavior;
- the team is hoping an authorization product will solve an identity-governance problem by itself.
The best architecture may also combine a ReBAC service with ordinary database controls, vector-store isolation, attribute checks, and application-level policy. ReBAC is not a replacement for authentication, network security, encryption, tenant isolation, or least-privilege service identities.
A practical implementation workflow

Step 1: Define the protected resource
Start with the source document, not the chunk. Give every chunk a stable parent document ID, tenant ID, source system, version, and deletion state. Permissions should attach to the resource whose access rules the business actually owns.
Step 2: Write the relationships in plain language
Examples include:
- a user is a member of a group;
- a group can view a workspace;
- a document belongs to a workspace;
- a user owns a document;
- an agent may act for a user within a project;
- a support role can view a case but not export its attachments.
If you cannot describe the graph clearly, do not choose the authorization engine yet.
Step 3: Choose the retrieval boundary
Measure the authorized-document cardinality and candidate rejection rate. Select pre-filtering, post-filtering, or a hybrid pattern based on those measurementsānot on a tutorial’s default.
Step 4: Build the synchronization contract
Document the source of truth, event schema, replay mechanism, idempotency key, lag metric, reconciliation job, and fail-closed behavior.
Step 5: Bind identity server-side
Never trust a tenant, group, or permission filter supplied by the client. Derive the principal and allowed scope from verified identity and server-side policy.
Step 6: Add negative tests before launch
At minimum, test:
- revoked group membership;
- a document moved between workspaces;
- a deleted source document with stale vectors;
- a user changing the tenant ID in the request;
- an empty or malformed filter;
- an authorization timeout;
- a stale semantic-cache hit;
- a nested-group permission;
- an agent trying to exceed delegated scope.
Step 7: Record the decision, not only the query
A useful audit event includes the principal, tenant, resource IDs, requested action, allow/deny result, authorization model version, source version or consistency token, retrieval IDs, and failure reason where applicable. Minimize sensitive prompt and document content in logs.
For the retrieval-store side of this design, pair the authorization layer with the controls in the vector database security hardening guide. The authorization service and vector database protect different parts of the system.
Final verdict
OpenFGA and SpiceDB are not shortcuts around authorization design. They are tools for making relationship-based authorization explicit, centralized, testable, and reusable across applications.
Google Zanzibar remains valuable as the reference architecture because it shows why large-scale authorization is more than a tuple store: it is a consistency and serving problem as well as a modeling problem. OpenFGA and SpiceDB bring that family of ideas into deployable public systems, but they do not remove the work of choosing a source of truth, synchronizing relationships, protecting retrieval, handling revocations, and testing failure.
For enterprise RAG, the strongest decision rule is simple:
Choose the system that matches your permission graph and freshness contract, then design the retrieval boundary around it.
If your permissions are genuinely relationship-heavy and your RAG corpus contains confidential, inherited, or delegated access, a Zanzibar-inspired service is worth serious evaluation. If your needs are only a stable tenant filter, start simpler. The goal is not to add a graph because it sounds sophisticated. The goal is to make it impossible for semantic relevance to outrank authorization.
Frequently asked questions
Is OpenFGA the same as Google Zanzibar?
Is SpiceDB better than OpenFGA for RAG?
Should RAG authorization happen before or after vector search?
Can vector metadata filters replace OpenFGA or SpiceDB?
How do I keep an authorization graph synchronized with a document system?
What should happen when the authorization service is unavailable?
Was this article helpful?









[…] a workload-specific side-by-side, OpenFGA vs SpiceDB vs Google Zanzibar for RAG, including pre-filtering, post-filtering, and reverse lookups ā see the dedicated comparison […]
[…] teams evaluating which Zanzibar-inspired engine to deploy alongside an MCP authorization layer, OpenFGA vs SpiceDB for RAG and agentic retrieval covers the tradeoffs that matter when the same engine must govern both tool calls and document […]
[…] and you’re choosing an engine for your vector store’s permission layer, OpenFGA vs SpiceDB vs Zanzibar for vector-store authorization walks through the RAG-specific tradeoffs: pre-filtering cost, reverse-lookup cardinality, and […]
[…] the authorization answer depends on relationships, folders, groups, inheritance, delegation, the OpenFGA vs SpiceDB vs Zanzibar comparison walks through how each engine fits a permission-aware RAG pipeline, and where metadata filters stop […]