Appearance
plexctl group
Synopsis
plexctl group is the operator surface for the per-Domain ReBAC Group aggregate and its GroupMembership child collection. It wraps the typed /v1/admin/groups and /v1/admin/groups/{id}/members endpoints so an operator can manage Groups (create, list, get, update, delete) and their memberships (add, list, remove) without hand-rolling curl calls.
The command is split into two cohesive blocks: the CRUD block on the Group aggregate itself, and the member subcommand block that operates on the membership child collection. The wire-shape decisions (source enum, principal-kind discriminator, the immutable slug contract on update) are documented next to each subcommand below; the canonical Group context lives in ../../contexts/identity/groups.md.
Invocation
text
plexctl group <subcommand> [flags]
plexctl group member <subcommand> [flags]The eight subcommands are enumerated below. Every subcommand is a private cobra constructor; flags are validated at parse time so an invalid --source or --kind never reaches the wire.
plexctl group create
Creates a Group inside a Domain. The handler enforces the manual / idp XOR on the aggregate; the CLI rejects a --source value that is not in the OpenAPI enum at flag-parse time so a typo surfaces as exit 2 instead of a 400 round-trip.
plexctl group list
Lists Groups in a Domain. Supports keyset pagination via --cursor and an opt-in --all flag that walks every page until the next_cursor field is empty. The --all accumulator is capped at 100 000 items as defence-in-depth against a runaway server.
plexctl group get
Returns a single Group by UUID.
plexctl group update
Updates a Group's mutable fields. Only display_name is settable — the slug is frozen after creation per the OpenAPI contract; an attempt to rename through update is rejected by the schema before reaching the aggregate.
plexctl group delete
Deletes a Group. Requires the persistent --yes flag — a destructive op behind a single typo of the group id is unacceptable, so the RunE refuses to call the API without explicit confirmation .
plexctl group member add
Adds a principal (user, service_identity, or group) to a Group. The --source of the membership MUST match the parent Group's source (both manual or both idp); the server enforces the parity but the CLI surface keeps the contract visible.
plexctl group member list
Lists every membership of a Group.
plexctl group member remove
Removes a principal from a Group. The --kind discriminator is required because the group_memberships table uses an XOR on the principal columns — the server needs the kind to target the correct column, and reading the OpenAPI param annotation guarantees the CLI mirrors the wire contract.
Flags
plexctl group create
| Flag | Type | Required | Default | Description |
|---|---|---|---|---|
--domain | UUID | yes | — | Owning Domain UUID. |
--slug | string | yes | — | URL-safe identifier unique within the Domain. Frozen after creation. |
--display-name | string | yes | — | Human-friendly Group name. |
--source | enum | yes | — | Provenance: manual | idp. |
--idp-binding | UUID | conditional | — | IdP binding UUID. Required when --source=idp (the aggregate enforces the XOR). |
--idp-claim-value | string | conditional | — | Verbatim IdP claim value. Required when --source=idp. |
plexctl group list
| Flag | Type | Required | Default | Description |
|---|---|---|---|---|
--domain | UUID | yes | — | Owning Domain UUID. |
--limit | int | no | server default | Maximum items per page. 0 lets the server pick. |
--cursor | string | no | — | Continuation token from a previous call's next_cursor. Mutually exclusive with --all. |
--all | bool | no | false | Follow next_cursor until exhausted (capped at 100 000 items). |
plexctl group get
| Flag | Type | Required | Default | Description |
|---|---|---|---|---|
--id | UUID | yes | — | Group UUID. |
plexctl group update
| Flag | Type | Required | Default | Description |
|---|---|---|---|---|
--id | UUID | yes | — | Group UUID. |
--display-name | string | yes | — | New human-friendly Group name. |
plexctl group delete
| Flag | Type | Required | Default | Description |
|---|---|---|---|---|
--id | UUID | yes | — | Group UUID. |
--yes (persistent) | bool | yes | false | Required confirmation for the destructive operation. |
plexctl group member add
| Flag | Type | Required | Default | Description |
|---|---|---|---|---|
--group | UUID | yes | — | Group UUID. |
--principal | UUID | yes | — | Principal UUID (user / service identity / nested group). |
--kind | enum | yes | — | Principal kind: user | service_identity | group. |
--source | enum | yes | — | Membership provenance: manual | idp (must match the parent Group). |
plexctl group member list
| Flag | Type | Required | Default | Description |
|---|---|---|---|---|
--group | UUID | yes | — | Group UUID. |
plexctl group member remove
| Flag | Type | Required | Default | Description |
|---|---|---|---|---|
--group | UUID | yes | — | Group UUID. |
--principal | UUID | yes | — | Principal UUID. |
--kind | enum | yes | — | Principal kind: user | service_identity | group. The repo targets the correct XOR column on group_memberships via this discriminator. |
Persistent flags inherited from root
--server, --profile, --token-file, --output, --yes, --reveal-secrets. The --yes flag is consumed by delete; the others apply unchanged. See plexctl.md for the canonical list.
Exit codes
plexctl collapses every failure into the taxonomy below; the single source of truth is exitCodeFor in ../../../cmd/plexctl/app.go.
| Code | Reachable | Meaning |
|---|---|---|
0 | yes | The API returned the expected status (201 for create / member add, 200 for list / get / update, 204 for delete / member remove). |
1 | yes | Runtime / API error: transport failure, unexpected status code, or a malformed response body. |
2 | yes | Flag-parse / misconfiguration: missing required flag, malformed UUID, unknown --source / --kind value, or delete without --yes. |
3 | yes | Missing or insecure credentials, or 401 Unauthorized from the API. |
4 | yes | Permission denied: 403 Forbidden from the API (e.g. the operator lacks domain:manage). |
64 | no | Not reachable — group is fully implemented and never returns *NotImplementedError. |
Examples
Create an idp-sourced Group
shell
export PLEXSPHERE_URL="${PLEXSPHERE_URL:-https://localhost:8080}"
plexctl group create \
--server "${PLEXSPHERE_URL}" \
--domain 0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a0 \
--slug sre-oncall \
--display-name "SRE On-Call" \
--source idp \
--idp-binding 0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0b0 \
--idp-claim-value "groups:sre-oncall"Page through every Group in a Domain
shell
plexctl group list \
--server "${PLEXSPHERE_URL}" \
--domain 0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a0 \
--all \
--output jsonAdd a service identity to a manual Group
shell
plexctl group member add \
--server "${PLEXSPHERE_URL}" \
--group 0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0c0 \
--principal 0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0d0 \
--kind service_identity \
--source manualPermission denied (exit 4)
shell
plexctl group delete \
--server "${PLEXSPHERE_URL}" \
--id 0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0c0 \
--yes
# stderr: plexctl: 403 Forbidden: insufficient_relation (need domain:manage)
echo $? # 4The 403 is decoded by output.DecodeProblem and routed to exit 4 through the typed *output.APIError path in exitCodeFor.
Cross-references
../../../api/openapi/plexsphere-v1.yaml—PostAdminGroup,GetAdminGroupList,GetAdminGroupByID,PatchAdminGroup,DeleteAdminGroupByID,PostAdminGroupMember,GetAdminGroupMembers,DeleteAdminGroupMemberoperation definitions and theGroupResponse,GroupListResponse,GroupMembershipResponse,GroupMembershipListResponseschemas.../../contexts/identity/groups.md— bounded-context reference for the Group aggregate, themanual/idpXOR, the membership state machine, and the audit contract.../../../cmd/plexctl/commands/group.go— source of truth for the cobra command, flag wiring, the--allcap, and the table projection.