Last updated 2026-06-29
Hierarchies & Inheritance
This guide models parent/child resource hierarchies so that access flows down a tree: grant someone a role on a workspace once, and they automatically get the right access on every resource inside it — with zero per-resource tuples.
What you'll learn: how the tuple_to_userset
rewrite makes a resource inherit from its parent, how to write the
parent link, a step-by-step walk of the Check
evaluation, and how to extend the same pattern to deeper folder/document
trees. This is the Linear-issue and folder-tree pattern.
The resource namespace
The built-in resource namespace inherits from a parent workspace
via tupleToUserset:
parent := this()owner := this()editor := union(this(), computed("owner"), tupleToUserset("parent", "admin"))viewer := union(this(), computed("editor"), tupleToUserset("parent", "member"))-
parentstores the link to the enclosing object — a userset likeworkspace:W#member. -
editor= direct editors ∪ the resource'sowner∪ every admin of the parent workspace. -
viewer= direct viewers ∪ everyone who is aneditor∪ every member of the parent workspace.
tupleToUserset("parent", "admin") reads as: "for every userset
stored under the parent relation, evaluate admin on
the object it points to." That single primitive is the inheritance edge.
Linking an issue to its workspace
Write one parent tuple from the issue to the workspace. The
subject is a userset pointing at the workspace's member relation
— the conventional anchor the rewrite walks from.
resource:issue-42#parent@workspace:W#membercurl -X POST http://localhost:8080/workspace.v1.AuthzService/WriteRelationTuples \ -H "Authorization: Bearer $WS_SERVICE_TOKEN" \ -H "Content-Type: application/json" \ -d '{"updates":[ {"op":"INSERT","tuple":{ "namespace":"resource","object_id":"issue-42","relation":"parent", "subject":{"set":{"namespace":"workspace","object_id":"W","relation":"member"}}}} ]}'That is the only tuple the issue needs. No per-user grants on the issue — viewers and editors are derived from the workspace.
Walking the Check
Suppose bob is an admin of workspace W
(workspace:W#admin@bob) and the issue has only the
parent tuple above. Ask whether bob can edit the
issue:
curl -X POST http://localhost:8080/workspace.v1.AuthzService/Check \ -H "Authorization: Bearer $WS_SERVICE_TOKEN" \ -H "Content-Type: application/json" \ -d '{"namespace":"resource","object_id":"issue-42","relation":"editor","subject_user_id":"bob"}'{ "allowed": true }Check evaluates the editor rewrite, left to right:
-
this()— is there a directresource:issue-42#editor@bob? No. -
computed("owner")— isbobthe issue'sowner? No direct owner tuple. No. -
tupleToUserset("parent", "admin")— read theparenttuples onissue-42. There is one, pointing atworkspace:W. Recurse intoCheck(workspace, W, admin, bob). -
workspace:W#admin@bobis a stored tuple — theadminleaf matches. Allowed.
A plain workspace member (not admin) would fail the
editor check at step 3 but pass a viewer check,
because viewer inherits the parent's member relation.
The role hierarchy on the workspace and the inheritance edge compose cleanly.
Deeper hierarchies: folders and documents
The same parent link chains. Model a folder as a
resource, put the folder under a workspace, and put a document
under the folder:
resource:folder-design#parent@workspace:W#memberresource:doc-spec#parent@resource:folder-design#viewer
Here doc-spec's parent points at the folder's
viewer relation. A Check(resource, doc-spec, viewer, bob)
recurses into Check(resource, folder-design, viewer, bob), which
in turn recurses up to the workspace via the folder's own parent
tuple. Access granted at the top of the tree reaches the leaf document, and
moving a subtree is one tuple change.
editor walks the parent's admin, and
viewer walks the parent's member. When you chain your
own levels, choose the relation on the parent that should confer access at the
child — as in doc-spec#parent@resource:folder-design#viewer above.
Where to next
- Sharing Resources — add direct per-object shares on top of inheritance
- Roles & RBAC — the workspace roles that get inherited
- Authorization Model — full rewrite reference and Check semantics