Relationship-Based Access Control (ReBAC): Complete Guide

Relationship-based access control decides whether someone may act on a resource by asking how they are connected to it. A user can open a document because they belong to a team that can view the folder the document sits in. No single fact mentions the user and the document together; the permission exists only in the chain. Permissions as paths through a graph of relationships is the idea Google built into Zanzibar to authorize Drive, YouTube, and hundreds of other services, and it is the design a generation of open-source authorization services now implements.
This guide defines ReBAC and places it next to RBAC and ABAC, then works through how a check is evaluated, the architecture of a Zanzibar-style system, how to implement one, and where the model gets hard.
What is relationship-based access control (ReBAC)?
Relationship-based access control (ReBAC) is an authorization model that grants access according to the relationships between a subject and a resource, either direct or through intermediate objects. Policies are written in relationship vocabulary (owner, member, parent, viewer), and each decision answers a question about the graph those relationships form: does a path of the permitted kinds connect this user to this object?
The paradigm comes from social network systems, where access follows interpersonal relationships such as friend or friend-of-a-friend. Philip Fong's "Relationship-Based Access Control: Protection Model and Policy Language" (CODASPY 2011) credits Carrie Gates with coining the name in 2007 and gives the paradigm a formal protection model. The idea reached internet scale with Google's "Zanzibar: Google's Consistent, Global Authorization System" (USENIX ATC 2019), which describes one authorization system shared by hundreds of client services, including Calendar, Cloud, Drive, Maps, Photos, and YouTube. Zanzibar generalized relationships beyond people: a document's link to its parent folder is as much a part of the policy as a user's membership in a group. Open-source systems including SpiceDB, OpenFGA, Ory Keto, and Permify are inspired by the paper.
ReBAC is easiest to understand next to the two models most teams already know. Role-based access control (RBAC) was standardized from the NIST RBAC model as ANSI/INCITS 359-2004; attribute-based access control (ABAC) is defined in NIST SP 800-162 (2014, updated 2019).
The table reads as three competitors, but the models compose more than they compete. A role is a relationship between a user and an organization, so RBAC fits inside ReBAC as member or admin relations on an organization object. Zanzibar-style systems in turn accept attribute conditions on relationships, such as OpenFGA's conditions and SpiceDB's caveats, for rules like time-limited grants. ReBAC earns its place when the access policy follows the structure of the data: sharing, nesting, ownership, and tenancy.
How does relationship-based access control work?
A ReBAC system has three moving parts. An authorization model declares the object types, the relations each type can have, and how one relation derives from others. Relationship tuples are the facts, stored as data. A check evaluates whether a user holds a relation on an object by following the model's rules through the tuples.
Here is a small model for documents in shared folders, written in the OpenFGA modeling language:
model
schema 1.1
type user
type group
relations
define member: [user, group#member]
type folder
relations
define owner: [user]
define parent: [folder]
define viewer: [user, group#member] or owner or viewer from parent
type doc
relations
define owner: [user]
define parent: [folder]
define editor: [user, group#member] or owner
define viewer: [user, group#member] or editor or viewer from parentEach define line combines three kinds of rule, which map onto the userset rewrite rules in the Zanzibar paper. The bracketed types are direct grants: tuples written explicitly for this relation. A bare relation name such as editor is a computed userset, meaning every editor is also a viewer. viewer from parent is a tuple-to-userset rule: find the object's parent, then check viewer on that parent. That last rule is what makes folder permissions flow down to documents without copying them.
The tuples supply the actual relationships:
Now ask whether user:anne is a viewer of doc:q3-plan. No tuple grants it directly. The editor branch fails, since Ben is the only owner and no editor tuple exists. The viewer from parent branch reads the document's parent, folder:roadmaps, and asks whether Anne is a viewer there. The folder grants viewer to group:eng#member, so the question becomes whether Anne is a member of group:eng, and a stored tuple says she is. The check returns allowed, and the reason is a three-hop path: Anne, member of eng, whose members view roadmaps, which is the parent of the plan.

The policy is small and the data does the work. Adding someone to group:eng, or moving the document to another folder, changes who can see it without touching the model. The model declares which kinds of path count as access; the tuples supply the paths; and a check is a bounded search over a graph, the same multi-hop shape as any other relationship question.
Relationship-based access control architecture
Zanzibar's paper remains the reference architecture, and the open-source systems differ mainly in packaging and storage choices. The components are consistent across them.

Schema. Zanzibar calls it a namespace configuration: per object type, the relations and their rewrite rules. SpiceDB separates stored relations from computed permissions in its schema language, so a write can only target a relation and permissions are always derived. That split is a useful discipline whatever system you choose.
Relationship store. Zanzibar keeps tuples in Spanner, whose TrueTime timestamps give every write a place in a global causal order. Open-source implementations run on conventional databases, typically PostgreSQL or MySQL, with distributed SQL options for multi-region deployments.
Evaluation servers and indexes. A check is evaluated by recursively expanding the rewrite rules into sub-checks, run concurrently; once one branch determines a subtree's result, the rest of that subtree is cancelled. Because popular objects and groups appear in many checks, Zanzibar servers form a distributed cache of intermediate results keyed by snapshot timestamp and deduplicate concurrent identical requests. Recursive pointer chasing still degrades with deeply nested or very large groups. The paper describes group membership as "a reachability problem in a graph" and handles the hard cases with Leopard, an index that flattens group-to-group membership so a check becomes a set intersection, kept current by consuming the stream of tuple changes.
Consistency tokens. The paper's central correctness concern is the new enemy problem. Alice removes Bob from a folder, then asks Charlie to move new documents into it; if a check ignores the order of those two changes, Bob may see documents he should never have seen. Zanzibar's answer is the zookie, an opaque token encoding a timestamp. The application stores a zookie with each content version and passes it on later checks, which must then be evaluated at a snapshot at least that fresh. SpiceDB's ZedTokens are the direct counterpart, offered alongside per-request modes that trade freshness for latency.
APIs. Zanzibar exposes read, write, watch, check, and expand. Expand returns the full tree of users and usersets behind a relation, which Google's clients used to build search indexes over access-controlled content. Later systems added reverse lookups as first-class calls: OpenFGA's ListObjects and ListUsers, SpiceDB's LookupResources and LookupSubjects.
The application remains the enforcement point in every deployment. It calls the authorization service before serving or mutating data and acts on the answer. What the service centralizes is the decision and the data behind it, so that every product surface answers "can this user see this?" the same way. The engineering difficulty sits in consistency and latency. A ReBAC model is easy to write; serving millions of checks per second at a fresh snapshot is what much of Zanzibar's paper is about.
How to implement relationship-based access control
Most teams adopt ReBAC inside an existing product, where relationships already live in the application's database and an older permission system is already enforcing access. The steps below follow that path from modeling to cutover.
Model from the access questions. List the sharing and inheritance behavior the product needs: who can view, edit, share, delete, and administer each object type, and what flows down from workspaces, folders, or organizations. Separate the rules that follow relationships from those that depend on attributes such as time, IP range, or plan tier, since the latter belong in conditions or in application logic. Then define the stored relations for each type (owner, parent, member) and define permissions as expressions over them. Keep permissions out of tuples; a stored can_delete relation is a role in disguise and loses the inheritance the model exists to provide. Test the model before any production traffic touches it. OpenFGA runs model tests with fga model test, and SpiceDB supports schema validation and assertions against sample relationships.
Decide where relationship truth lives. Most relationships already exist in the application's database as foreign keys and join tables: a document's folder_id, a team_members table. Backfill tuples from those tables, then keep the two stores in agreement. This is the dual-write problem: a crash between the database write and the tuple write leaves the systems disagreeing about who has access. Common answers are a transactional outbox, a durable workflow engine that retries both writes to completion, or making the authorization service the only home for relationships that have no other use.
Enforce at the request path, with the right consistency. Call check before each protected read or write, and batch checks where an endpoint evaluates many objects. In SpiceDB, store the ZedToken returned with a content change and pass it on subsequent checks of that content, so a revocation cannot be outrun by a stale cache. OpenFGA has no equivalent token; it offers a per-request higher-consistency mode that bypasses its cache, for checks that follow a recent write or revocation. In either system, use the latency-favoring mode for checks where brief staleness is acceptable.
Plan for lists and search separately. A page that shows "documents you can see" needs a different strategy from a single check. Pre-filter with a reverse lookup when the accessible set is small relative to the collection; post-filter search results with batched checks when it is large; and for heavy search workloads, maintain an access-aware index from the service's change stream.
Roll out in shadow mode. Run ReBAC checks alongside the existing authorization code, log disagreements, and switch enforcement only once the disagreements are explained. Each mismatch is either a model bug, a missing tuple, or a latent bug in the old code, and all three are cheaper to find before cutover.
Benefits of relationship-based access control
Each benefit below follows from one design choice: access is stored as relationship data and derived by traversal.
Fine-grained sharing without role explosion. Per-object grants are a single tuple each. Sharing a document with one contractor does not require minting a role scoped to that document, and revoking it removes one fact.
Policy that follows the data's structure. Inheritance through folders, projects, and organizations is expressed once in the model and applied by traversal. Moving an object or restructuring a team updates access as a consequence of the move, with no separate permission migration.
One model across services. Zanzibar's motivation was a uniform data model and configuration language for hundreds of Google services, so that sharing behaves the same in each product and applications can coordinate access when one embeds another's objects. Centralizing the decision gives smaller organizations the same property across their own services.
Explainable decisions. Because access is a path, a decision has a concrete reason that can be inspected. Expand returns the userset tree behind a relation, which OpenFGA's documentation recommends for debugging and for understanding why a user has a particular relationship.
Composes with roles and attributes. Roles become relations on an organization object, and attribute rules attach as conditions on relationships. Teams adopting ReBAC rarely discard their existing role vocabulary; they re-express it in a model that also handles sharing and hierarchy.
Challenges and limitations of ReBAC
Most of ReBAC's costs come from the same design choice as its benefits. Storing access as relationship data makes that data a correctness-critical dependency, and deriving access by traversal puts graph search on the request path.
Modeling mistakes are expensive to undo. A model is a schema for authorization data, and changing a relation's meaning after millions of tuples reference it is a data migration. Getting the stored relations right early, and keeping permissions computed, limits how often that happens.
Synchronization becomes a correctness problem. Once relationships live in two places, every inconsistency between them is an authorization bug: a removed team member who keeps access, or a new hire who sees nothing. The outbox and workflow-engine patterns above need the same monitoring and replay tooling as any other data pipeline.
Deep and wide graphs cost latency. Every level of nesting and every large group multiplies the sub-checks a request fans out into. Caching, request deduplication, and Leopard-style denormalization exist because naive evaluation does not hold up at scale, and each adds operational surface.
Consistency tokens have to be threaded through the application. In token-based systems such as Zanzibar and SpiceDB, the new enemy guarantee holds only if the application stores and forwards tokens correctly. Code paths that skip the token fall back to whatever staleness the default mode allows.
Reverse and whole-graph questions fall outside the check path. A ReBAC service is built to answer one decision at a time on the request path. "What can this user see?" already requires exploring outward from the user across every relevant object type, and OpenFGA's list endpoints return what they find within a configured deadline and result limit, a reasonable design for an online API that leaves very large result sets to dedicated indexes. Access reviews, offboarding audits, and incident response go further: every path by which any contractor reaches a restricted document, or everything a compromised account could reach through nested groups. Those are analytical traversals over the full relationship graph, often joined with data the authorization service never holds, such as employment status from HR or sensitivity labels from a data catalog.
The relationships behind those questions usually already exist as tables: the application's membership, ownership, and folder tables, an export of the service's grant tuples, and directory and HR data landed in a warehouse or lakehouse. With users and groups modeled as principals, folders and documents as resources, CAN_VIEW edges taken from the grants, and each CHILD_OF edge pointing from a resource to its parent folder, the review question becomes one graph traversal, bounded at five levels of group nesting and four of folder nesting:
MATCH path = (u:Principal)-[:MEMBER_OF*0..5]->(:Principal)
-[:CAN_VIEW]->(:Resource)<-[:CHILD_OF*0..4]-(d:Resource)
WHERE u.kind = 'user' AND u.employment_type = 'contractor'
AND d.kind = 'document' AND d.sensitivity = 'restricted'
RETURN u.email, d.name, pathEach returned path is the reason for the access, ready for the reviewer to act on in the authorization model.
PuppyGraph runs this kind of traversal over the tables where those relationships already live, alongside the authorization service rather than in place of it.

PuppyGraph is a distributed graph query engine that queries tables in place in SQL databases, data warehouses, and data lakes and lakehouses, including direct reads of open table formats like Iceberg and Delta Lake, with no ETL into a separate graph database and the same tables still queryable with SQL. Teams connect the sources, define a graph schema that maps tables to nodes and edges, and query with openCypher and Gremlin.



Conclusion
Relationship-based access control treats authorization as a question about a graph: whether a path of permitted relationship types connects a user to a resource. That framing handles sharing, nesting, and inheritance, the cases where role and attribute models strain, and it composes with both. The model is the simple part. The engineering lies in keeping relationship data consistent with the application, evaluating deep traversals within a latency budget, respecting the causal order of changes, and answering reverse and organization-wide questions that single checks do not cover.
Try the forever-free PuppyGraph Developer Edition and book a demo with the team to see how openCypher and Gremlin queries trace access paths across warehouse and lakehouse tables, with no graph-specific ETL, so an access review can follow every route from a user to a resource.

