Last updated 2026-06-29

Sharing Resources

This guide models direct per-object sharing — "share this task with dad" — and shows how a single resource combines direct shares, group shares, and parent inheritance into one effective access set.

What you'll learn: how to grant and revoke access on an individual resource with one tuple each, how the three sources of access union together, and which WriteRelationTuples / ReadRelationTuples calls back them.

Direct sharing: one tuple per grant

A directly-shared resource has no parent workspace — access is whatever was granted on the object itself. This is the personal-assistant pattern: a task a user shares with specific people.

resource:task#viewer@user:dad # dad can view
resource:task#editor@user:partner # partner can edit

Subjects here are concrete users, so these are plain this() leaf tuples — no rewrite needed for the grant to take effect. Write them with AuthzService.WriteRelationTuples:

curl -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":"task","relation":"viewer",
"subject":{"user_id":"dad"}}}
]}'

Then "can dad view this task?" is a single Check:

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":"task","relation":"viewer","subject_user_id":"dad"}'
{ "allowed": true }

Three sources, one union

The viewer relation on a resource is a union of everything that can confer view access:

viewer := union(this(), computed("editor"), tupleToUserset("parent", "member"))

So a single document can draw viewers from all three at once:

resource:doc#viewer@user:dad # 1. direct share with a person
resource:doc#viewer@group:family#member # 2. share with a whole group
resource:doc#parent@workspace:W#member # 3. inherit from a parent workspace
Source Tuple Resolved by
Direct (person) resource:doc#viewer@user:dad this() leaf
Group resource:doc#viewer@group:family#member this() leaf → recurse into the group
Inherited resource:doc#parent@workspace:W#member tupleToUserset("parent","member")

Check(resource, doc, viewer, <user>) returns allowed if any source grants it: the user is shared directly, or is a member of family (resolved transitively through the group), or is a member of workspace W. You don't choose a strategy per resource — mix them freely on the same object.

Revoking access

Revoke is the inverse of share: delete the exact tuple. A DELETE op removes a single grant; access from the other two sources is unaffected.

curl -X POST http://localhost:8080/workspace.v1.AuthzService/WriteRelationTuples \
-H "Authorization: Bearer $WS_SERVICE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"updates":[
{"op":"DELETE","tuple":{
"namespace":"resource","object_id":"task","relation":"viewer",
"subject":{"user_id":"dad"}}}
]}'

Insert and delete can be batched in one call — the updates array is applied together — so re-sharing (revoke from one user, grant to another) is a single atomic request.

Seeing what is shared

To list the raw grants directly stored on a resource — for a share-management UI — use ReadRelationTuples. It returns the stored tuples matching an exact filter, with no rewrite evaluation: it shows the direct viewers and any group/parent subjects you wrote, but not the users those usersets expand to.

curl -X POST http://localhost:8080/workspace.v1.AuthzService/ReadRelationTuples \
-H "Authorization: Bearer $WS_SERVICE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"namespace":"resource","object_id":"task","relation":"viewer"}'
Read shows storage; Check shows access. ReadRelationTuples is a raw store read — use it to render "who is this shared with" exactly as written. To decide whether a specific user may act, always use Check, which expands groups and inheritance. To see the fully-expanded set of who has access, use Expand (see Check & Expand).

Where to next