Last updated 2026-06-29
Groups & Nesting
This guide models reusable membership sets — teams, distribution lists,
"family", "all-engineering" — with the built-in group namespace,
and shows how groups nest and how granting a group access to
anything works.
What you'll learn: how to add users and other groups to a
group, how to grant a group a role or a resource permission with a single
userset tuple, and how a Check resolves transitively through
nested groups.
The group namespace
A group has exactly one relation, and it is a plain leaf — just the directly stored tuples:
member := this()
There is no role hierarchy inside a group; it is a flat set. The power comes
from what a member can be: a subject is either a concrete
user or a userset — and a userset may itself point at another
group's member relation. That is how groups nest.
Adding members
Use GroupService.AddGroupMember. Its member field is
a oneof: pass user_id to add a person, or
group_id to nest a whole group. Like all management RPCs it
requires acting_user_id.
# add a user to the backend teamcurl -X POST http://localhost:8080/workspace.v1.GroupService/AddGroupMember \ -H "Authorization: Bearer $WS_SERVICE_TOKEN" \ -H "Content-Type: application/json" \ -d '{"group_id":"backend","member":{"user_id":"alice"},"acting_user_id":"alice"}'That stores a tuple with a concrete-user subject:
group:backend#member@aliceNesting a group inside a group
To make every member of backend also a member of the broader
all-eng group, add backend as a
group member of all-eng:
curl -X POST http://localhost:8080/workspace.v1.GroupService/AddGroupMember \ -H "Authorization: Bearer $WS_SERVICE_TOKEN" \ -H "Content-Type: application/json" \ -d '{"group_id":"all-eng","member":{"group_id":"backend"},"acting_user_id":"alice"}'The stored tuple uses a userset subject:
group:all-eng#member@group:backend#member
Read that as "everyone who is a member of backend is
a member of all-eng." Nesting is unbounded —
all-eng can in turn be nested inside everyone.
Traversal is cycle-safe, so an accidental loop cannot hang a check.
Granting a group access to anything
Because any subject may be a userset, you grant a whole group access to an
object by writing one tuple whose subject is the group's
member set. The same shape works across every namespace:
# the family group can view a documentresource:doc#viewer@group:family#member
# the all-eng group are members of the acme workspaceworkspace:acme#member@group:all-eng#member
These are written through AuthzService.WriteRelationTuples (one
or more INSERT/DELETE ops in a single call):
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":"workspace","object_id":"acme","relation":"member", "subject":{"set":{"namespace":"group","object_id":"all-eng","relation":"member"}}}} ]}'
Now every member of all-eng — including the nested
backend members — is a member of acme, with no
per-user tuples. Add or remove a person from backend once and
their access everywhere those groups are granted updates automatically.
A transitive Check
Given the tuples above:
group:backend#member@alicegroup:all-eng#member@group:backend#memberworkspace:acme#member@group:all-eng#memberAsk whether alice is a member of the acme workspace:
curl -X POST http://localhost:8080/workspace.v1.AuthzService/Check \ -H "Authorization: Bearer $WS_SERVICE_TOKEN" \ -H "Content-Type: application/json" \ -d '{"namespace":"workspace","object_id":"acme","relation":"member","subject_user_id":"alice"}'{ "allowed": true }Check walks the chain:
-
workspace:acme#memberhas a userset subjectgroup:all-eng#member— recurse intoCheck(group, all-eng, member, alice). -
group:all-eng#memberhas a userset subjectgroup:backend#member— recurse intoCheck(group, backend, member, alice). -
group:backend#member@aliceis a stored tuple with a concrete subject — the leaf matches. Allowed.
Where to next
- Sharing Resources — combine group grants with direct shares and inheritance
- Check & Expand — use
Expandto see who a group resolves to - Workspaces & Groups — the product surface