Appearance
🛡️ adminadmin@example.com · Domain admin in Acme Corp
Chapter 6 — Labels
A Project to classify
bash
PROJECT=$(plexctl project create \
--domain "$DOMAIN_ID" \
--slug label-demo \
--display-name "Label Demo" \
--output json)
PROJECT_ID=$(echo "$PROJECT" | jq -r '.id')
echo "$PROJECT" | jq '{id, domain_id, slug, name}'Labels are typed, not free-form strings
bash
DEF=$(plexctl label define create \
--scope domain --scope-id "$DOMAIN_ID" \
--key env --type enum --enum-values production,staging \
--applicable-kinds project --on-delete cascade \
--output json)
DEF_ID=$(echo "$DEF" | jq -r '.id')
echo "$DEF" | jq '{id, scope, qualified_key, applicable_kinds, on_delete}'A definition first, then assignments against it.
--type enum with two allowed values. --applicable-kinds project — this Label may not be attached to a Node. --on-delete cascade decides what happens to assignments when the definition goes away.
The twist
Assigning a Label is a privileged act, and being Domain admin does not authorize it.
Two explicit relations are needed: maintainer on the target Project, and assigner on the Label definition. The admin relation you hold grants neither.
bash
plexctl rebac tuple add \
--project "$PROJECT_ID" \
--resource "project:$PROJECT_ID" \
--relation maintainer \
--subject "user:$USER_ID"And the second relation
bash
plexctl rebac tuple add \
--project "$PROJECT_ID" \
--resource "labeldefinition:$DEF_ID" \
--relation assigner \
--subject "user:$USER_ID"Why this matters: a Label often drives something — network policy targeting, which credential a Project may use, who gets paged.
If administering a Domain silently granted the right to attach labels, every one of those downstream decisions would inherit that authority.
Assign it
bash
plexctl label object set "project:$PROJECT_ID" \
--definition-id "$DEF_ID" \
--value productiontext
OBJECT_KIND OBJECT_ID QUALIFIED_KEY VALUE ASSIGNED_BY
project 019ecc6a-1a2b-7c3d-8e4f-0a1b2c3d4e5f acme-corp/env production userproduction is one of the declared enum values, so it is accepted. --value bogus is rejected — the schema is enforced at assignment time, not merely documented.
Read it back
bash
plexctl label object list "project:$PROJECT_ID"text
OBJECT_KIND OBJECT_ID QUALIFIED_KEY VALUE ASSIGNED_BY
project 019ecc6a-1a2b-7c3d-8e4f-0a1b2c3d4e5f acme-corp/env production userNote the qualified key: acme-corp/env. Labels are namespaced by the scope that defined them, so two Domains can both have an env and never collide.
Labels exist to be selected
bash
plexctl label selector preview --selector "acme-corp/env=production"text
OP KEY VALUE VALUES
eq acme-corp/env productionpreview only parses — it touches no object. The grammar has equality, inequality, set membership, and existence, joined with commas for AND.
A Label you can only read back would be a sticky note. This is the bridge from "I tagged one thing" to "I can act on everything tagged this way."
Two axes in one query
bash
plexctl label object search \
--selector "acme-corp/env=production" --relation maintainertext
KIND ID
project 019ecc6a-1a2b-7c3d-8e4f-0a1b2c3d4e5fThe Label axis (the selector) and the access axis (the relation), resolved together.
Objects you cannot reach through the relation are silently omitted. The result is exactly the set you are allowed to act on — not a tag list you then have to filter by hand.
This is the shape that network policy targeting and bulk actions consume.