ADR 0016: Operator RBAC and delegated control-plane permissions
- Status: Accepted (partially implemented)
- Date: 2026-05-29
- Decider(s): @davesade (David Kubec)
- Relates to: ADR 0003, ADR 0013, ADR 0015
Context
Today the control plane starts from a single bootstrap operator token. This is good for bring-up, but not enough for real production operations where we need:
- multiple operator identities
- least-privilege access by function (support, SRE, billing, platform admin)
- auditable permission boundaries
- safe delegation to customer-facing control workflows
Without explicit roles and permissions, every elevated token is effectively super-admin, which increases blast radius and weakens audit quality.
Decision
Kilolock will add RBAC for operator/control-plane actions with scoped permissions and role bindings. Bootstrap token remains for initial setup, then operators should create least-privilege principals.
D1. Principals, roles, permissions
Add explicit control-plane auth model:
- Principal: human, service account, automation client
- Role: named permission bundle
- Permission: atomic action (for example
tenant.create,token.rotate) - Binding: principal -> role with optional scope
D2. Scoped authorization
Permissions are evaluated with scope:
- global (all tenants)
- tenant-scoped
- environment-scoped
This allows support/billing/operator personas without global superuser access.
D3. Bootstrap remains one-time break-glass
klc init still issues the first high-privilege token. After initialization:
- bootstrap token is intended for emergency use only
- day-to-day operations should use role-bound principals
- bootstrap rotation/revocation is supported operationally
D4. Default role set
Ship opinionated defaults:
platform_admin(full control-plane permissions)tenant_admin(manage one tenant and its environments/tokens)support_readonly(read metadata/state inventory only)provisioner(run provisioning workflows, no tenant billing/admin changes)
D5. Enforcement point
Authorization is enforced in klc API/CLI handlers (policy check before store mutation). Data-plane backend auth remains environment token based.
Consequences
Positive
- reduced blast radius
- clear separation of duties
- stronger enterprise posture for paid service
- cleaner external control-plane integration story
Negative
- added schema and policy complexity
- migration/bootstrapping path must be clear to avoid lockout
- more test surface (authz matrix)
Implementation path (phased)
- Schema + policy primitives
- tables for principals, roles, permissions, bindings, auth tokens
- seed default roles/permissions
- Read-only enforcement first
- require authn principal resolution for control-plane API
- enforce permissions for list/get endpoints
- Write-path enforcement
- enforce create/update/delete/suspend/archive/provision actions
- deny by default when permission missing
- Scoped bindings
- tenant/environment-scoped role bindings
- scope-aware evaluator
- Operator UX
- CLI/API for principal create, role binding, token rotation/revocation
- web UI role management screen
- Audit hardening
- log principal ID, action, scope, request ID, decision (allow/deny)
- include role source in decision trace
Compatibility and migration
- Existing bootstrap token flow remains functional.
- Initial rollout can map bootstrap token to
platform_admin. - If RBAC config is absent, startup behavior should fail closed in production
mode and fail open only in explicit development mode.
Non-goals
- Full enterprise IdP/SSO in first RBAC iteration.
- Resource-level Terraform object authorization in backend runtime.
- Replacing customer environment tokens for Terraform/OpenTofu backend access.