Appearance
🛡️ adminadmin@example.com · Domain admin in Acme Corp
Chapter 3 — Read a tenant
What can I see
bash
plexctl domain listtext
ID SLUG DISPLAY_NAME CREATED_AT
019ecc65-8622-7a7f-bfe1-0344c7a22dbd acme-corp Acme Corp 2026-06-15T17:47:46ZFour Domains exist. You see one.
This is not a filter someone remembered to add to a query. The list surface asks the authorization engine which Domains this principal holds a relation in, and returns those. Chapter 5 shows the engine directly.
Look inside
bash
plexctl project list --domain "$DOMAIN_ID"Projects are where work lives — each one owns Resources and gets a slice of the Domain's mesh range.
bash
plexctl identity list --domain "$DOMAIN_ID"Identities are per-Domain. This list is Acme Corp's, and there is no list that spans Domains.
The audit log is not an add-on
bash
plexctl audit entries list --domain "$DOMAIN_ID"Every state change in this Domain, hash-chained.
You have written nothing yet, so what you are looking at is the seed's own trail — the bootstrap Job creating Domains and binding identity providers, recorded the same way your commands will be.
Chapter 4 writes into this and reads its own writes back.
Now walk into the wall
Beta LLC is not discoverable through the API — the Domain list already showed you that. So take its id straight from the database and point a call at it while still signed in as your Acme Corp principal:
bash
DOMAIN_ID_BETA=$(kubectl exec statefulset/postgres -- \
env PGPASSWORD=plexsphere psql -U plexsphere -d plexsphere -tAc \
"SELECT id FROM plexsphere.domains WHERE slug='beta-llc'")
plexctl identity list --domain "$DOMAIN_ID_BETA"text
plexctl: Permission Denied: identities ListIdentities: caller lacks domain:read on the addressed DomainChanging --domain changed the target. It did not change who you are.
Two surfaces, two refusals
bash
plexctl project list --domain "$DOMAIN_ID_BETA"text
ID DOMAIN SLUG DISPLAY_NAME CREATED_ATA header and nothing under it. Exit code 0.
The identity surface denied. The Projects surface filtered — row by row, showing nothing it may show, without confirming the Domain exists at all.
Different signal, same guarantee. A script that automates plexsphere has to treat "empty" and "denied" as the same wall.
Ask the engine directly
bash
USER_ID=$(plexctl whoami --output json | jq -r '.subject')
plexctl rebac check \
--subject "user:$USER_ID" \
--relation read \
--resource "domain:$DOMAIN_ID"text
DECISION REASON CORRELATION_ID
allowed c9663519-57c6-4e68-92f5-cad6e81f9b2aREASON stays empty — the engine only fills it in to explain a denial.
The same question, the other Domain
bash
plexctl rebac check \
--subject "user:$USER_ID" \
--relation read \
--resource "domain:$DOMAIN_ID_BETA"text
plexctl: rebac check: denied (reason=insufficient_relation, correlation_id=e64fa71c-870e-4fac-b868-a41e5af60ea8)insufficient_relation is the reason behind both earlier refusals — the 403 and the empty page.
The tenancy boundary is not a convention layered on top of the API. It is this decision, and every read on the last five slides went through it.
🏢 betaadmin@example.com · a different principal, in Beta LLC
Optional — so where is Beta LLC
Exactly where Acme Corp is. You just have to sign in to it:
bash
plexctl login --domain-id "$DOMAIN_ID_BETA" --profile-name betabash
plexctl domain list --profile betatext
ID SLUG DISPLAY_NAME CREATED_AT
019ecc65-9012-7b3c-a1d4-7e2f5a8b0c11 beta-llc Beta LLC 2026-06-15T17:47:46ZBeta LLC appears. Acme Corp does not.
The same human, the same Dex account — two principals, two subject UUIDs. One login is one tenant. There is deliberately no cross-tenant super-admin.
Presenter note: this costs a browser sign-in, and it must be a private window — the earlier approval left a session cookie that the approval page will happily reuse, minting an Acme Corp token under the
betaprofile and making the slide read backwards. Skip when short on time.
Optional — drop the second session
bash
plexctl logout --profile betatext
Logged out. Cleared profile "beta".Revokes the Beta token server-side and drops the profile. The default Acme Corp profile is untouched.
🛡️ adminadmin@example.com · Domain admin in Acme Corp
Everything has a machine shape
bash
plexctl domain list --output jsonAlways { "items": [ … ] }.
And it resolves the ambiguity from four slides ago: when a list is empty because rows were hidden from you, the JSON carries empty_reason: no_rebac_membership. When it is empty because nothing exists, the field is absent.
Its presence, not its value, is the signal.