Skip to content

Grant access through a Group

In Build in your first Domain you created a Group and added a member to it — but you never used the Group to grant anything. This lesson does. You will grant a Group a relation on a Project, watch every member inherit that access, and then watch it disappear the moment you leave the Group. By the end you will understand how plexsphere delegates access to roles rather than to individuals.

This lesson takes about fifteen minutes.

Before you start

You need a running stack and a logged-in plexctl from Set up your local plexsphere, and you should have finished Build in your first Domain — this lesson assumes you recognise a Project, a Group, a member, and a ReBAC relation. You also need jq on your $PATH.

Recreate the shell environment and capture the Domain and your own user id:

bash
export PATH="$PWD/bin:$PATH"
export PLEXSPHERE_URL=http://localhost:8080

DOMAIN_ID=$(plexctl domain get acme-corp --output json | jq -r '.id')
USER_ID=$(plexctl whoami --output json | jq -r '.subject')

Step 1 — Create a Project to share

Make a fresh Project — the resource whose access you will delegate:

bash
PROJECT=$(plexctl project create \
  --domain "$DOMAIN_ID" \
  --slug group-demo \
  --display-name "Group Demo" \
  --output json)
PROJECT_ID=$(echo "$PROJECT" | jq -r '.id')
echo "$PROJECT" | jq '{id, domain_id, slug, name}'
json
{
  "id": "019ecc67-2b3c-7d4e-8f50-1a2b3c4d5e6f",
  "domain_id": "019ecc65-8622-7a7f-bfe1-0344c7a22dbd",
  "slug": "group-demo",
  "name": "Group Demo"
}

Step 2 — Confirm you do not hold the access yet

You administer the Domain — but that does not make you a maintainer of every Project in it. maintainer is a relation that has to be granted; administering the Domain gives you management permissions through the tenancy hierarchy, not the maintainer role itself. Ask the authorization engine whether you hold it on the new Project:

bash
plexctl rebac check \
  --subject "user:$USER_ID" \
  --relation maintainer \
  --resource "project:$PROJECT_ID"
text
plexctl: rebac check: denied (reason=insufficient_relation, correlation_id=9fdbc8dd-957d-47c3-86eb-c47d2f32a83b)

denied, with a non-zero exit. Good — that is the baseline. Now you will grant the relation, but to a Group rather than to yourself.

Step 3 — Build a Group and join it

Create a Group, and capture its id so you can add a member to it:

bash
GROUP=$(plexctl group create \
  --domain "$DOMAIN_ID" \
  --slug group-demo-operators \
  --display-name "Group Demo Operators" \
  --source manual \
  --output json)
GROUP_ID=$(echo "$GROUP" | jq -r '.id')
echo "$GROUP" | jq '{id, domain_id, slug, display_name, source}'
json
{
  "id": "019ecc67-3c4d-7e5f-9061-2b3c4d5e6f70",
  "domain_id": "019ecc65-8622-7a7f-bfe1-0344c7a22dbd",
  "slug": "group-demo-operators",
  "display_name": "Group Demo Operators",
  "source": "manual"
}

Now add yourself — the principal you authenticated as — as a member:

bash
plexctl group member add \
  --group "$GROUP_ID" \
  --principal "$USER_ID" \
  --kind user \
  --source manual
text
GROUP                                 PRINCIPAL                             KIND  SOURCE
019ecc67-3c4d-7e5f-9061-2b3c4d5e6f70  019ecc65-9f3a-7c14-8d2b-5f3e0a7b1c40  user  manual

Step 4 — Grant the Group the access

Now the key move. Grant the maintainer relation on the Project not to a user, but to the Group's member set — written group:<id>#member:

bash
plexctl rebac tuple add \
  --project "$PROJECT_ID" \
  --resource "project:$PROJECT_ID" \
  --relation maintainer \
  --subject "group:$GROUP_ID#member"
text
ID                                    SUBJECT                                            RELATION    RESOURCE                                      CREATED_AT
019ecc67-4d5e-7f60-8172-3c4d5e6f7081  group:019ecc67-3c4d-7e5f-9061-2b3c4d5e6f70#member  maintainer  project:019ecc67-2b3c-7d4e-8f50-1a2b3c4d5e6f  2026-06-15T18:02:14Z

If the grant returns Permission Denied on the first try, wait a couple of seconds and run it again — the Project you just created takes a moment to propagate to the authorization backend.

The #member suffix is what makes this a role grant: it points at "every member of this Group", not at the Group object itself.

Step 5 — Membership grants the access

Run the exact same check as Step 2 — but now you are a member of a Group that holds maintainer:

bash
plexctl rebac check \
  --subject "user:$USER_ID" \
  --relation maintainer \
  --resource "project:$PROJECT_ID"
text
DECISION  REASON  CORRELATION_ID
allowed           788330f3-d36e-4577-b187-eab964129c6e

allowed. You did not grant yourself anything — your membership in the Group did. Every other member of group-demo-operators has exactly the same access, and onboarding a new joiner is one group member add, not a tuple per Project.

If it still says denied, the grant from Step 4 is propagating — wait a moment and run the check again.

Step 6 — Ask the other two questions

rebac check answers one subject-and-resource question at a time. Two companion queries turn it around — what can a subject reach, and who can reach a resource.

First, what can you reach? List the Projects on which you hold maintainer:

bash
plexctl rebac lookup-resources \
  --subject "user:$USER_ID" \
  --relation maintainer \
  --resource-type project
text
OBJECT
019ecc67-2b3c-7d4e-8f50-1a2b3c4d5e6f

That is the Group Demo Project — the access you inherited through the Group.

Now the reverse, who can reach it? List the users who hold maintainer on the Project:

bash
plexctl rebac lookup-subjects \
  --resource "project:$PROJECT_ID" \
  --relation maintainer \
  --subject-type user
text
OBJECT
019ecc65-9f3a-7c14-8d2b-5f3e0a7b1c40

That lists you, the member who holds it. Notice that neither query names the Group: the engine has already expanded membership into concrete resources and subjects, which is exactly what an access review wants to see. Add a second member and they appear in the lookup-subjects answer; remove yourself — as you do next — and you drop out of it.

Step 7 — Leave the Group, lose the access

Group membership is live, not a snapshot. Remove yourself from the Group:

bash
plexctl group member remove \
  --group "$GROUP_ID" \
  --principal "$USER_ID" \
  --kind user

remove succeeds silently — it returns no body. Now run the exact same check as Step 5:

bash
plexctl rebac check \
  --subject "user:$USER_ID" \
  --relation maintainer \
  --resource "project:$PROJECT_ID"
text
plexctl: rebac check: denied (reason=insufficient_relation, correlation_id=f8da0886-3ddc-430e-9f89-9a1778037a63)

denied again. The Project's grant never changed — you simply stopped being a member of the Group it was granted to. Revoking a person's access across every Project a Group governs is a single group member remove.

The re-check can lag a second or two behind the removal, just as the grant did; if it still says allowed, wait and run it again.

Do this in the Console

Every step above has a browser equivalent in the Console. Open a Project's row on the Projects page and click Manage to reveal its Access grants panel: Grant access picks a Group and grants it a relation on the Project's member set (the group:<id>#member form), and each row's Revoke removes a grant. To confirm a member inherited the access, the panel links to the ReBAC check tool under Tools — the same subject / relation / resource question you ran with plexctl rebac check.

What you learned

  • You grant access to roles, not just people. A relation granted to group:<id>#member is held by every member of the Group.
  • Membership is the control point. Adding or removing one member grants or revokes that access across every resource the Group governs — no per-resource tuple churn.
  • Administering a Domain is not the same as holding a Project relation. Broad tenancy permissions and specific role relations are separate — administering the Domain did not make you a maintainer; you granted that relation explicitly.
  • Authorization is live. ReBAC re-evaluates membership on every check, so access follows the Group in real time.
  • You can ask in both directions. rebac check answers one subject-and-resource pairing; rebac lookup-resources and rebac lookup-subjects answer "what can this principal reach" and "who can reach this" — the queries an access review runs, with Group membership already expanded into concrete resources and subjects.

Where to go next

  • Keep learning by doingLabel your first Project defines a Label, grants the two relations that authorize assigning it, attaches it to a Project, and shows how Labels drive selection.

Or pick the quadrant that matches what you need now: