Documentation for v2.9.0. Read the latest, v2.12.0.

Operator

Roles and permissions

Authorisation is expressed in permissions, the unit every check tests. Roles are fixed bundles of permissions defined in the service, not composed by an operator, so what a role confers cannot drift from what the code enforces. This chapter is the complete table of both, and the two things that sit beside them: root and maintainers.

What changed in 2.6.0

Maintainers also manage the webhooks of their Producers and of the Consumers of the same name.

Permissions#

Permission What it allows Held by
manage_users List, approve and delete user accounts. admin, user_manager
manage_roles Grant and revoke roles, manage groups and their roles and members, assign maintainers. admin, user_manager
manage_producers Administer Producers and their version lines: delete a version (the escape hatch from GA immutability), delete a Producer, edit its metadata, manage its webhooks, rename and delete sanshain-branches. admin; maintainer within its Producers
manage_consumers Delete a Consumer and its recorded dependencies. admin
view_audit Read the audit log and timeline, export it as CSV. admin, viewer
manage_settings Cleanup ages, the cache, auto-approve, the webhook allow-list and global webhooks, the debug flags. admin
manage_auth_config Read and change the authentication mode and the LDAP and OIDC configuration. admin
view_observability Read logs, stats and the debug configuration. Defined and reported in a user's effective permissions; in 2.6.0 no route checks it, the observability routes admit every signed-in user. admin, viewer
run_destructive_operations The bulk deletions of the Danger Zone and the whole-database snapshot download. admin
release_ga Publish with stability: ga (a fresh GA, a promotion, an idempotent GA re-provide), cut a sanshain-branch. admin, releaser

Root holds all ten and any permission a later release adds. GET /admin/roles returns this catalogue; GET /auth/me returns the caller's effective permissions, which is what the web UI gates on.

Roles#

Role Confers Granted how
admin Every permission. Instance-wide; admin-guarded.
user_manager manage_users, manage_roles. Instance-wide.
viewer view_audit, view_observability. Instance-wide.
maintainer manage_producers, within a set of Producers. Never instance-wide: assigned to a Producer. Granting it as a role answers 400 Role 'maintainer' is scoped to specific Producers and cannot be granted instance-wide.
releaser release_ga. Instance-wide; admin-guarded.

admin and releaser are admin-guarded: only an admin or root may grant or revoke them, or put them on or take them off a group; a user_manager who tries gets a bare 403. For admin that closes self-escalation; for releaser it keeps manage_roles from being a side door to release rights.

Publishing GA without the permission answers 403 with the remedy (publishing GA for 'orders' requires the 'releaser' role — publish as a snapshot, or ask an administrator to grant the role) and writes a VERSION_REJECTED audit entry with reason ga_requires_releaser. Grant releaser to whatever performs releases, typically the CI user whose token sets the ga switch. Holders also get a Promote to GA button on snapshot entries; the promoting account is named in the VERSION_PROMOTED entry while the original provider stays credited.

How routes are guarded#

Every /admin/* route declares one of three guards, and a build-time test keeps the router and maintenance.yaml in step (the generated Maintenance API shows the guard of every route):

Admin routes take a session token only. An API token answers 401, a read_only one on the backup route (the only admin route that admits API tokens) answers 403 with this API token has scope 'read_only' and may not call administrative routes; use a token with scope 'full' of an account holding the permission.

Maintainers#

A maintainer is responsible for a set of Producers. It is an assignment, not a role, because it means nothing without the Producers it is over. A user or a group can be assigned; group assignment is what scales, so responsibility follows membership. A Producer-scoped action admits either the global permission or maintainership of that Producer, so an administrator acts on any Producer and a maintainer on theirs, and is refused on everybody else's.

What maintaining a Producer covers in 2.6.0:

Maintainers do not release: a GA provide stays the pipeline's job.

Route Guard Effect
GET /admin/maintainers manage_roles Every Producer with its maintainers.
GET /admin/producers/{name}/maintainers manage_producers, Producer-scoped Users and groups maintaining it; a maintainer may read their own.
POST /admin/producers/{name}/maintainers manage_roles Body {"user_id": 3} or {"group_id": 1}, exactly one.
DELETE /admin/producers/{name}/maintainers/users/{user_id}, …/groups/{group_id} manage_roles Unassign.
GET /admin/users/{id}/maintains manage_roles The Producers a user is responsible for.

Groups#

A group is a set of users that roles attach to. Groups carry a source:

A native and a directory group may share a name; the UI shows the origin. The LDAP form's Bootstrap Admin Group is the usual first directory group: saving it creates the group with admin attached. OIDC groups are not mapped here; only the OIDC admin_group grants a role, see Authentication.

Route Effect
GET /admin/roles The role catalogue and what each confers.
GET/POST /admin/users/{id}/roles, DELETE …/roles/{role} Roles granted directly to a user; grant with {"role": "user_manager"}.
GET/POST /admin/groups All groups with origin, roles and members; create a native one with {"name": "…"}.
PUT/DELETE /admin/groups/{id} Rename, replace its roles, or both; delete a group and its grants.
POST /admin/groups/{id}/members, DELETE …/members/{user_id} Membership of a native group; body {"user_id": 3}.

All of them need manage_roles and are audited (GRANT_ROLE, REVOKE_ROLE, CREATE_GROUP, UPDATE_GROUP, DELETE_GROUP, ASSIGN_MAINTAINER, UNASSIGN_MAINTAINER).

Token scopes, from the operator's side#

Users mint their own API tokens on /account.html; an operator cannot mint one for someone else. A token's scope is fixed at creation: full does everything its owner may, read_only reads reports, versions, the update streams and the MCP endpoint and is refused on every route that changes the catalog with 403 this API token has scope 'read_only' and may not change the catalog; create a token with scope 'full' for provide, require and catalog pushes. A token never exceeds its owner: a full token of a user without releaser cannot publish GA. Expired tokens stop validating at once and are removed by the cleanup pass. Deleting a user revokes their tokens. The 2.6.0 UI has no administrative view of other users' tokens.

Checked against v2.6.0.