73. Named mint privilege levels under agent roles
Date: 2026-07-19
Status
Accepted
Context
The mint maps each agent role to a single hardcoded permission set (ADR 0007). Every token minted for a role gets the same ceiling. The threat model establishes least privilege as a cross-cutting principle (see security-threat-model.md), but the current model cannot differentiate between a phase that only needs read access and one that needs write — both receive write-level tokens.
#2821 (human-gated permission adjustments) and the least-privilege topic (#5312) depend on a general mechanism for minting tokens at differentiated privilege levels within a role. Without this mechanism, downstream work risks encoding use-case-specific designs instead of a reusable model.
Decision
Role levels
Each role defines an ordered set of named privilege levels. Each level's permissions are a superset of all preceding levels. The first level is always read — generally read-only — and is the default when no level is requested.
The read level for each built-in role is derived by taking the role's current permission set and removing all *:write permissions. The write level is defined as the full permission set currently granted by each built-in role. For roles where write is not explicitly defined, requesting write falls back to read automatically.
Mint API
Token requests accept an optional level field:
{"role": "coder", "level": "write"}When level is omitted, the mint defaults to read. The mint validates that the requested level exists for the role and returns an error for unknown levels.
Custom roles
CUSTOM_ROLE_PERMISSIONS continues to accept the current JSON shape — a flat role-to-permissions map — interpreted as the read permissions for each role:
{"my-role": {"contents": "read", "issues": "write"}}An alternate shape specifies multiple named levels per role. The mint auto-detects the format per role by checking whether the role's value contains a levels key:
{"my-role": {"levels": {"read": {"contents": "read"}, "write": {"contents": "read", "issues": "write"}}}}Mixed format is allowed within a single CUSTOM_ROLE_PERMISSIONS value — some roles may use the flat format while others use the multi-level format. The mint detects the format independently for each role.
Clients
mintclient and CLI paths that mint tokens accept an optional level parameter, defaulting to read when unset. The harness passes the level derived from the privilege_levels configuration (see below) to the mint client for each phase.
Acquisition semantics
The harness is the sole caller that selects privilege levels. It determines the level for each run phase from the privilege_levels configuration and requests the corresponding token from the mint before that phase begins. Agents and scripts within a phase do not choose or escalate their own level — they receive the token the harness minted for their phase.
Harness privilege_levels flag
Harness YAML supports a privilege_levels field mapping run phases to levels:
privilege_levels:
pre_script: write
runtime: read
post_script: writeA default key specifies the level for phases not explicitly listed. When privilege_levels is omitted entirely, the harness behaves as if:
privilege_levels:
default: writeThis preserves backward compatibility — existing harnesses continue to receive write-level tokens for all phases.
Consequences
- Agents running in the LLM sandbox can receive read-only tokens, reducing blast radius if the sandbox is compromised.
- The mint API defaults to
readwhenlevelis omitted, producing narrower tokens than the current behavior. Direct API callers and mint clients that omitlevelwill receive read-level tokens instead of write-level tokens. CUSTOM_ROLE_PERMISSIONSsupports both the existing flat format and the new multi-level format — including mixed format within a single value — without a breaking change.- Harnesses that omit
privilege_levelsdefault towritefor all phases, so existing harness configurations are unaffected — the harness requestswritefrom the mint, preserving current token scope. - Implementation of role levels in the mint, clients, and harness is tracked in #2823 and #2826.
- #2821 (human-gated permission adjustments) is unblocked and can build on this mechanism.
