Skip to main content
Private Preview·Early access by invitation.Request access →
Kirimana.
Docs · Governance

RBAC roles + capabilities

Kirimana’s access model is deliberately small: four roles, a handful of capabilities, and one rule for scoping them to an environment and a domain. The whole thing is machine-readable in one place, so the CLI, the web app, and the MCP identity passthrough all decide access the same way. Identity comes from your OIDC provider; the mapping from identity to role lives in kiri_rbac.yml.

Four roles

The roles are additive going left to right — each strictly contains the rights of the one before it:

RoleValueWhat it can do
ViewerviewerRead-only, in every env.
AuthorauthorApply contract changes in dev/test (scoped by domain). Loses apply in prod.
ApproverapproverEverything an author can, plus approve/promote/release in every env.
Platform-adminplatform-adminEverything, including editing platform config and redacting audit logs.

When a caller’s identity groups map them to more than one role in the same env × domain cell, the strictest applicable role wins — the resolver picks the one with the most rights, not the least, so a narrowly-scoped elevated binding is honoured rather than diluted.

The capability matrix

Roles are checked against capabilities, not against individual commands. The capabilities are kept small on purpose — if a new operation cannot be classified as one of these, the answer is usually to add a domain, not a capability.

CapabilityWhoNotes
readviewer+Read-only access, every env.
applyauthor+ in dev/test; approver+ in prodAuthor loses apply in prod — production cuts are an intentional, elevated act.
release_applyapprover+ on test/prod; platform-admin everywhereThe production-cut operation (kiri release apply).
approveapprover+PR approval / contract promotion gate.
promote_goldapprover+ in the owning domainMedallion promotion to gold.
edit_platform_configplatform-admin onlyEdit kiri.yml auth/domains, kiri_rbac.yml, vault or adapter config.
redact_auditplatform-admin onlyRedact an audit-log entry. Separate cohort from config edits — different blast radius, different PR gate.
dispatch_incidentviewer+ in dev/test; author+ in prodManually fire an ITSM ticket; the bar lifts in prod because a real on-call gets paged.

The one rule worth memorising: author loses apply in prod. In dev and test an author lands their own changes; the moment the target is prod, applying becomes an approver-or-above act. That single tightening is what makes a production cut a deliberate handoff rather than something an author can do alone.

Scoping: env × domain

A role is never granted globally by accident. Bindings in kiri_rbac.yml map an identity group to a role, optionally narrowed to specific environments and/or domains:

version: 1
auth:
  provider: github
  group_claim: teams
role_bindings:
  - group: acme/platform-team
    role: platform-admin            # every env, every domain
  - group: acme/sales-eng
    role: author
    domains: [sales]                # sales domain only
  - group: acme/sales-leads
    role: approver
    environments: [prod]
    domains: [sales]                # approver, but only sales in prod

Omitting environments means “every env, including ones added later”; omitting domains means “every domain”. An empty list is rejected at load time as ambiguous — use omission or null to mean “all”. Two bindings with an identical (group, role, environments, domains) tuple are also rejected, as a copy-paste smell. The resolver picks the strictest role for the exact env × domain cell an operation touches, then the capability matrix decides the rest.

OIDC-delegated identity

Kirimana never stores its own user database. Caller identity is delegated to your OIDC provider, named by the auth.provider discriminator (github, azure_ad, oidc_generic are supported; okta and google are declared but provider plugins are deferred). The group_claim names the token claim that carries group membership — GitHub emits teams, Azure AD emits groups — and those group values are exactly what your role_bindings match on. The auth: block here is kept in sync with the one in kiri.yml; the loader validates the two do not silently desync.

The dev bypass, and why prod ignores it

For local development you can act as a role without an OIDC login, using --as-role (or the KIRIMANA_AS_ROLE env var):

kiri apply --target dev --as-role approver

This is honoured only when two conditions both hold: dev_bypass: true is set on the kiri_rbac.yml auth block, and the target env is not prod. A --as-role override against prod is rejected unconditionally — the safety rail is fixed in production, which always requires real identity. Every bypass invocation is audit-logged with acting_as_override=true, so a forensic answer to “who was actually at the keyboard” always exists.

What a denial looks like

Privileged CLI commands check access near the top and, on denial, print a clear reason and exit with code 3 — a code reserved for RBAC denials specifically, so a shell script can tell a permission problem from a bug. The message names the missing right; the author-in-prod case even points you at the fix:

✗ Access denied. role=author cannot perform apply in env=prod.
  Author loses apply rights in prod. Ask an approver to run
  `kiri release apply`, or open a PR.

If no RBAC is configured at all, Kirimana degrades to a single-user mode where everything is allowed — but the audit trace still records the OS-level subject, so even an unconfigured project leaves a trail.

How this composes with the rest of governance

RBAC answers “may this identity act here?”; the surrounding machinery answers the other governance questions. Domain owners + CODEOWNERS routes contract PRs to the right reviewers. PR-time governance gates enforces classification, ownership, and lineage at merge. And the capabilities above are exactly the gates the contract-approval review leans on when a change needs a human decision recorded.

Updated 5 July 2026 · v1.0.0-beta.1