Appearance
Sign in as a Platform Operator
A Platform Operator's authority is platform-wide, not tenant-scoped. The platform sign-in lets you authenticate against a platform-shared IdP binding without naming any tenant Domain: the resulting session is platform-scoped (principal_scope: platform, no domain_id), and your reach comes from the Domain-independent platform:plexsphere#admin grant.
This differs from tenant sign-in, where you enter a Domain ID and the session is scoped to that Domain. It also differs from the reserved platform-system Domain, which is non-authenticating by contract and never a login home.
Prerequisites
- An active platform-shared IdP binding. Either
- an existing platform admin registered one via
plexctl platform-idp create/POST /v1/admin/platform-idp(see Manage IdP bindings), or - the deployment seeds one from configuration — the bootstrap path for the very first operator (next section).
- an existing platform admin registered one via
- The
platform:plexsphere#adminReBAC grant on your user. The platform session itself confers no permissions; the grant is what the platform surfaces authorize against.
Seed the first platform binding from configuration
On a fresh deployment no admin session exists yet to register a binding — a chicken-and-egg the config-driven seed closes. Set the four load-bearing keys on the plexsphere binary and restart it:
shell
PLEXSPHERE_PLATFORM_IDP_ISSUER=https://idp.example.com
PLEXSPHERE_PLATFORM_IDP_CLIENT_ID=plexsphere-operator
PLEXSPHERE_PLATFORM_IDP_CLIENT_SECRET_REF=secret://prod/operator-idp
PLEXSPHERE_PLATFORM_IDP_DISCOVERY_URL=https://idp.example.com/.well-known/openid-configuration
# Optional:
PLEXSPHERE_PLATFORM_IDP_ALIAS=corp-sso
PLEXSPHERE_PLATFORM_IDP_JIT_POLICY=allowThe seed is idempotent (one binding under a fixed reserved id, re-asserted on every readiness tick) and fail-safe:
- All four keys unset — the seed is a no-op. Deployments that manage platform bindings purely through the admin surface are unaffected.
- A partial set — the binary refuses to boot, so a typo'd manifest never ships with the first operator silently locked out.
CLIENT_SECRET_REFstores a symbolic secret-store reference (for examplesecret://prod/operator-idp), never a raw secret.
Changing the keys after the first boot does not rewrite the existing binding — post-creation lifecycle belongs to the admin surface (plexctl platform-idp).
The local dev stack pre-seeds a platform binding against its Dex fixture, so make dev supports the flow out of the box.
Sign in from the Console
- Open the Console sign-in page and choose Sign in as a Platform Operator below the Domain-ID form.
- The Console lists the platform identity providers (
GET /v1/auth/idp-bindings?scope=platform). A sole provider proceeds immediately; two or more render a chooser. - Complete the sign-in at your IdP. You land back in the Console with a platform-scoped session: the header shows a neutral Platform pill instead of the Domain and Project switchers, and your profile's Domain row reads "Platform (no Domain)".
The equivalent API call is a POST /v1/auth/sign-in whose body names only the binding:
shell
curl -sS -H 'Content-Type: application/json' \
--data '{"idp_binding_id": "<platform-binding-uuid>"}' \
"${PLEXSPHERE_URL}/v1/auth/sign-in"GET /v1/auth/whoami on the minted session reports "principal_scope": "platform" and omits domain_id.
Sign in from the CLI
plexctl login --platform runs the same platform sign-in through the device-code flow — no tenant Domain is ever named:
shell
plexctl login --server "${PLEXSPHERE_URL}" --platformDevice-code initiation is anonymous, so the server accepts the platform mode only on deployments that explicitly opted in with PLEXSPHERE_AUTH_PLATFORM_DEVICE_LOGIN=true (the local dev stack does). On an un-opted deployment the command fails with 400 Bad Request at the device-code step; the browser sign-in from the previous section works either way.
- The CLI resolves the platform identity provider. With a single active platform binding it proceeds immediately; with several it lists them and asks you to pin one via
--idp-binding-id <uuid>or--idp-binding <alias>(the same chooser surface the Console uses,GET /v1/auth/idp-bindings?scope=platform). - Open the printed verification URL in a browser. The approval page states that a platform-scoped authorization is being approved and walks you through the platform sign-in if you are not already signed in. The approving browser session must itself be platform-scoped — a tenant session is refused with
403 device-scope_mismatch; sign in as a Platform Operator and approve again. - Back in the terminal, the CLI persists the minted token to your plexctl profile. Verify the scope:
shell
plexctl whoamiThe SCOPE column reads platform and the DOMAIN column stays - — the token belongs to no tenant Domain. With the platform-admin grant in place (next section), platform surfaces such as plexctl platform-idp list work without ever selecting a tenant Domain.
See plexctl login and plexctl whoami for the full flag and output contracts.
Grant the platform-admin permission
Your first sign-in provisions your user just in time, but the platform session confers no permissions by itself. An existing platform admin (or the config-driven seed below) grants the Domain-independent admin relation:
text
platform:plexsphere#admin@user:<your-user-uuid>Tooling that looks the user UUID up by subject finds the row under the reserved platform-operators Domain with external_subject set to <platform-binding-uuid>/<your-idp-sub> — the subject is namespaced by the resolving binding, so two platform identity providers that emit the same upstream sub always map to two distinct users.
With the grant in place you can reach every platform surface — for example IdP binding administration (plexctl platform-idp list, GET /v1/admin/platform-idp), Cloud creation, and the catalog-global blueprint-catalog lifecycle (register, import, deregister) — without ever selecting a tenant Domain.
Seed the first operator's grants from configuration
On a fresh deployment no platform admin exists yet to write that tuple — the authorisation half of the same chicken-and-egg the binding seed closes. Name the first operator on the plexsphere binary and restart it:
shell
PLEXSPHERE_PLATFORM_OPERATOR_SUBJECT=<the operator's IdP sub claim>
# Optional:
PLEXSPHERE_PLATFORM_OPERATOR_EMAIL=operator@example.com
PLEXSPHERE_PLATFORM_OPERATOR_USER_ID=<uuid pin for the seeded row>The seed pre-creates the operator's user row under the reserved platform-operators Domain (keyed on the binding-namespaced subject, so your first real sign-in adopts exactly this row) and asserts two grants for it on every readiness tick:
platform:plexsphere#admin— the platformmanageauthority behind Cloud creation, catalog-global blueprint catalogs, platform IdP bindings, platform Labels, and the backup catalog.managementfleet:fleet#admin— the Management Fleetmanageauthority behind cluster registration and Project-assignment termination.
The seed is idempotent and fail-safe: SUBJECT unset is a clean no-op (deployments that grant operators through existing tooling are unaffected), a refinement without the subject refuses to boot, and a configured seed re-asserts the grants each tick, so a wiped SpiceDB heals itself. The subject is namespaced with the config-seeded binding's reserved id — the seed pairs with the PLEXSPHERE_PLATFORM_IDP_* binding above; operators signing in through a binding created later via the admin surface receive their grants from an existing platform admin instead.
The local dev stack seeds operator@example.com this way, so plexctl login --platform lands with the grants in place out of the box. Later operators are granted by an existing platform admin.
Known limitations
- Per-Domain surfaces stay per-Domain: listing a tenant's audit events or identities still addresses that Domain explicitly, and a per-Domain command run against a platform-scoped profile fails closed server-side (
403 Forbidden, exit4in plexctl) unless your user also holds relations on that Domain. Your own sign-in audit lives under the reservedplatform-operatorsDomain.