Using SpiceDB Schema‑Defined Computed Permissions to Build Role‑Like Groupings
SpiceDB lets you build role‑like groupings with computed permissions—unions, intersections, and exclusions—directly in the schema. This article shows a working example, how to test it, and common pitfalls to avoid.
07 Aug 2025, 05:42 UTC

Why Compute Permissions Instead of Storing Them?
SpiceDB keeps explicit relation tuples in its relationship store. A permission is a computed set that expands at query time into the union, intersection, or exclusion of those tuples. This means you avoid duplicating grants, keep a single source of truth, and get instant propagation of changes.
Defining a Simple Role Hierarchy
Below is a minimal schema that creates three user roles and two permissions that compose them. The file schema.spice can be loaded with zed schema write (or via the Playground).
# schema.spice
# Define the resource type
resource document
{
relation owner: user;
relation editor: user;
relation viewer: user;
# Permissions are computed from relations
permission edit = owner + editor;
permission view = edit + viewer;
}
Explanation of the syntax:
relationdeclares a set of tuples that can be added withzed relationship create.permissiondeclares a computed set. The+operator is a union.- By referencing
editinsideview, we automatically grant view access to owners and editors.
Testing the Permission Logic
Assume we have a user alice and a document doc1. The following steps illustrate the dynamic nature of computed permissions.
- Add a tuple:
zed relationship create document:doc1#editor@user:alice(requiresspicedb:relationship:createpermission). - Check view permission:
zed check document:doc1#view@user:alice.- Expected result:
PERMISSIONSHIP_HAS_PERMISSIONbecausealiceis aneditorandeditis part ofview.
- Expected result:
- Remove the tuple:
zed relationship delete document:doc1#editor@user:alice. - Re‑check:
zed check document:doc1#view@user:alice.- Expected result:
PERMISSIONSHIP_NO_PERMISSION– the check reflects the updated store immediately.
- Expected result:
Adding Exclusion Logic
Sometimes you need an allow‑with‑exception rule. For example, a user can view a document unless they are explicitly banned. Extend the schema:
relation banned: user;
permission view = edit + viewer - banned;
Now, adding a tuple document:doc1#banned@user:bob will prevent Bob from viewing doc1, even if he is an editor or viewer.
Hierarchical Resource Inheritance
SpiceDB supports traversing to parent objects via relation->permission. This is handy for folder structures:
resource folder
{
relation owner: user;
permission view = owner;
}
resource document
{
relation parent_folder: folder;
permission view = parent_folder->view;
}
In this model, a user who owns a folder automatically gets view access to all documents inside that folder. The traversal is performed at check time, no materialized view needed.
Common Pitfalls and Limits
- Deep or Cyclic Expressions: Very complex or recursive permission definitions can increase latency. SpiceDB enforces a maximum recursion depth (currently 64). Design flat permission trees where possible.
- Exclusion Misuse: A broad
-set can inadvertently strip access from legitimate users. Always test exclusion branches explicitly and consider adding adebugpermission that lists the excluded set. - Schema Deployment: Changing a permission expression is retroactive; existing tuples stay, but the outcome of permission checks changes immediately. Review downstream logic before pushing changes.
- Version Differences: Syntax and operator precedence may vary slightly between SpiceDB releases. Verify against the version you are running.
Verifying Behavior in Production
When rolling out a new permission, add a unit test that:
- Creates a tuple that should grant the permission.
- Runs
zed checkand assertsHAS_PERMISSION. - Deletes the tuple, re‑checks, and asserts
NO_PERMISSION. - Tests an exclusion scenario if applicable.
Automating these checks ensures that any future schema change does not break expected behavior.
Practical Takeaway
Use SpiceDB’s computed permissions to model role hierarchies and inheritance declaratively. Keep the schema simple, test exclusions, and remember that changes are immediate but require careful review of dependent logic.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.