Summary / 25. Permissions and least privilege / Document
Spec 2026-09-06
Shared copy: the operator's name and machine names are replaced by roles. Nothing else is changed.
Rendering…
# Build Spec 25 — Permissions / Least Privilege (making authority explicit, task-scoped, capability-based, and monotonically confined under a deterministic control plane with zero learned entries) > Derived from `papers/25-permissions-least-privilege.md` — "Making Authority Explicit, Task-Scoped, Capability-Based, and Monotonically Confined Under a Deterministic Control Plane with Zero Learned Entries." > This document drives the build of component 25: the **permission and least-privilege system** — the law that Component 24's > fence enforces. Component 24 drew the fence: an execution sandbox that contains arbitrary generated code under a deterministic > control plane with zero learned entries. Component 25 is the thing that decides, **before any worker ever reaches the fence**, > exactly which files, commands, services, arguments, credentials, and data flows that worker may touch. Its central claim — > made structural here — is that authority must be **derived, not declared**: the smallest set of actions and resources sufficient > for the current workflow state is computed by deterministic software on CPU [D] and enforced at the tool boundary; ambient > authority is removed so a worker holds **explicit capabilities** rather than broad access; credentials are exposed only through > **scoped, short-lived proxies** rather than raw secrets; specialist agents are prevented from **laundering authority through one > another**; permissions **narrow automatically and monotonically** as work progresses; and every expansion is a **first-class > security transition** demanding evidence that lower-privilege alternatives are genuinely insufficient. The one-sentence human > form of the whole design, and the sentence that makes this component non-negotiable, is: **Never give a worker everything Piper > can access. Give it exactly the capability needed for the next state — and make it structurally impossible for that worker to > acquire anything else.** Throughout the harness the model is a *trusted collaborator* held to account by deterministic gates; > permissions cannot afford that trust even *before* execution begins, because the request itself — "let me read this file, call > this tool, hit this network endpoint" — is the precise place where an injected instruction, a laundered request, or a simple > over-reach becomes action. The structural invariant that governs the entire design is **the L column contains at most one > entry** — and at this component, as at the sandbox, the correct value is **zero**: the permission control plane is deterministic > software on CPU [D], and the model is the *requestor* the control plane mediates, not a *decider* within it. **An empty L column > is a permitted, correct state** — and it is the **only** correct state for the enforcement path: a permission system with any > learned entry in the grant/deny decision has reintroduced the very entity — a model whose tool-choice distribution is measurably > over-privileged — into the one place whose correctness must not be probabilistic. The paper assembles the evidence along **five > dimensions of scope** (resource, action, tool, temporal, data-flow) and **three enforcement pillars** (capability derivation, > policy enforcement, complete mediation) plus one discipline (monotonic confinement), traces the lineage from Saltzer & > Schroeder's design principles through Hardy's Confused Deputy to Capsicum's file-descriptor capabilities, and closes with the > honest-measurement machinery — the **over-privileged tool-use rate (OPUR)**, the false-positive and overhead audits, and the > human-factors question of whether user-authored policies actually help — that turns least privilege from a policy intention into > a *measured, legible property* of tool choice. This spec pins the paper's design to exact primitives: the exact `Capability` > record, the exact `PermissionDecision` record, the exact `PermissionExpansionRequest` record, the exact D/L invariant at its > zero-learned extreme, the exact five dimensions with their enforcement points, the exact three pillars plus monotonic > confinement, the exact credentials-as-capabilities + confused-deputy + laundering defenses, the exact escalation decision space > {deny, allow-once, allow-narrower, require-human} with its transient-failure rule, the exact honest-measurement protocol (OPUR + > false-positive/overhead + legibility), and an exact build sequence. --- ## 1. Purpose & Scope Build a **deterministic permission control plane plus a zero-learned enforcement plane** — a software layer that, given a worker (a repair agent, a research agent, a network agent — the harness's specialists) and the workflow state's declared task, decides *before any action reaches a tool*: which capabilities that worker holds (capability derivation), which requested actions are allowed against that set (policy enforcement), which capabilities may transfer between peers (delegation + laundering check), when a capability is minted and when it expires (temporal scope + revocation), what sensitive data may flow where (data-flow scope), and what happens when a worker asks for *more* authority than it holds (the expansion gate) — all of it deterministic software on CPU, with no learned model anywhere in the grant/deny path. The scope is the **authorization boundary only**. Components 01–23 own *what* each stage computes and *what order* the stages run in; component 24 owns *what the running code may do once a capability is received*; this component owns *which capabilities are ever minted, and to whom, and for how long* — the one question where a wrong answer is not a quality defect but an authority leak. The answer it defends is the most conservative one available: **derive the grant from state, never from intention; enforce it deterministically; make expansion a gated transition; and fail closed.** The paper's discipline compresses into a handful of load-bearing moves that carry the entire design. **Authority is derived, not declared**: the model may *request* authority but never *grants* it — the grant is a computation over the current workflow state and the candidate capability, not a judgment the model is asked to make. **Explicit capability over ambient authority**: the system constructs minimal grants and removes the ambient namespace, so confinement fails closed *by construction* (there is nothing ambient to leak) rather than by the perfection of a filter. **Credentials are capabilities**: a worker never receives a raw secret — it receives a scoped, short-lived proxy, and the proxy, not the worker, holds the credential. **Monotonic confinement**: authority shrinks automatically and only ever grows through an explicit, evidence-gated, first-class event. **Complete mediation**: every action converges on the tool boundary, and the permission system *is* that choke point. **Zero-L**: the permission control plane holds zero learned entries by construction; the model is the *requestor*, never a *decider*. Three principles decide every design choice. **P1 The grant is a computation, not a conversation.** Capability derivation, the policy engine, the grant/deny decision, monotonic narrowing-vs-expansion, and revocation are all deterministic software on CPU [D]; the model never appears in the enforcement path. **P2 The D/L invariant at zero.** The control plane holds zero learned entries by construction; **the L column contains at most one entry**, and here the bound is met at zero — **an empty L column is a permitted, correct state**, and it is the **only** correct state, because a learned entry in the grant/deny decision is the over-privileged model re-admitted to the one place its correctness must not be probabilistic. The only learned elements in the system are **outside** the control plane: the harness's model calls that *request* capability. **P3 Authority is monotonic toward less.** The automatic direction is always toward less authority; the only direction that admits a human or a one-time narrow grant is the expansion direction. A worker can, on its own, end up with *less* than it started; it can never, on its own, end up with *more*. The build lives in `build/25-permissions-least-privilege/`, writes nothing into the live harness, and is kept **only if** the permission system strictly dominates its own absence — measured on the honest instruments of §9: the over-privileged tool-use rate (OPUR) driven toward zero, the false-positive rate held low, the per-request overhead held negligible, and the grant/deny log + expansion history legible enough for an operator to *read* whether the system is working — A/B-tested against the ambient-authority baseline and against the intermediate ablations (no derivation; no policy engine; no tool-boundary mediator; no credential-proxy layer; no provenance/laundering check; no expansion gate; no revocation). --- ## 2. Round 1 vs later rounds (toward the 100% target) The goal is the paper's full **100%** spec. **~80%** is only the expected **first-round landing**; later rounds climb from there. **BUILD-NOW = the Round 1 build** (rows 1–9); **DEFERRED = later rounds** (rows 10–11), **not excluded** but gated by the same §10 A/B protocol. Every later round of this component still builds within the same `build/25-permissions-least-privilege/` tree. | # | Component (paper §2/§3/§4/§5/§6) | Classification | |---|---|---| | 1 | Capability lattice + derivation — MiniScope hierarchy search + optimization, a pure function of the workflow state (§3.1) | **BUILD-NOW** | | 2 | Policy engine — Progent ALLOW/FORBID symbolic rules + Fallback, deterministic and complete (§3.2) | **BUILD-NOW** | | 3 | Tool-boundary mediator — the single choke point through which every action must pass (complete mediation, §3.4) | **BUILD-NOW** | | 4 | Credential-proxy layer — scoped, short-lived proxies; secrets never raw, never in model context (§4.1) | **BUILD-NOW** | | 5 | Provenance / laundering check — authority traceable to a derivation, not a requester; multi-agent laundering blocked (§4.3) | **BUILD-NOW** | | 6 | Expansion-request gate — the `PermissionExpansionRequest` + {deny, allow-once, allow-narrower, require-human} decision space (§5) | **BUILD-NOW** | | 7 | Revocation / ephemeral lifecycle — temporal scope, revocation-by-expiry, automatic narrowing (§2.4, §3.3) | **BUILD-NOW** | | 8 | OPUR instrumentation — the over-privileged tool-use rate measured against a policy-derived oracle (§6.1) | **BUILD-NOW** | | 9 | False-positive + overhead + legibility audits — the grant/deny log and expansion history as readable artifacts (§6.2, §6.4) | **BUILD-NOW** | | 10 | Information-flow graph (SEAgent) — full data-flow scope, chain-of-actions composition denial (§2.5, §4.2) | **DEFERRED** — Round 1 ships the data-flow *scoping* via `data_labels` constraints (§5.1) and the provenance/laundering check; the full SEAgent information-flow graph (composing individually-legitimate actions into prohibited flows) lands when a multi-step data-flow workload demonstrates the need | | 11 | Human-factors policy experiment — whether user-authored policies over-trust or under-trust (§6.3) | **DEFERRED** — the question is unvalidated (single-author, ~11-day-old preprint); it is a *measurement to run*, not a mechanism to ship, and it is gated on the Round 1 legibility audit proving the grant/deny log is actually readable | **The cost rule (explicit).** The L column holds **zero** entries in this component — not one, not "at most one," zero. No learned model is consulted at any point in the control plane: not to derive a capability, not to evaluate a policy rule, not to mediate a tool boundary, not to mint a proxy, not to check provenance, not to evaluate an expansion request, not to revoke. The model's entire presence is **outside** the control plane: it *requests* capability, and the control plane *grants or denies* it. The DEFERRED rows (information-flow graph, human-factors experiment) must clear the same bar as in every other component: strictly dominate the deterministic Round 1 build on §9's measures — specifically, catch a prohibited flow or a policy pathology the Round 1 build demonstrably misses — at a defensible cost, or be rejected. **Design decision: ZERO-L, DETERMINISTIC-ONLY.** Round 1 puts no model anywhere in the permission system, inside or at the edge. The full deterministic control plane (rows 1–7) plus the honest-measurement rig (rows 8–9) is the build. There is no L slot to fill. A permission system that *would* need a learned entry has already failed — because the grant/deny decision is the one place whose correctness must not be probabilistic — and Round 1 is constructed so that need never arises. --- ## 3. The Deterministic Control Plane (D) The control plane is the software that decides, before any action reaches a tool, what authority exists and whether a request is inside it — and it is, by construction, entirely outside the model. It has three pillars — capability derivation, policy enforcement, and complete mediation — plus one discipline — monotonic confinement — and the model's relationship to all of them is a single word: *request*. Recapitulating the invariant at the architecture level, because this is the section it governs: > **the L column contains at most one entry.** The entire permission control plane — capability derivation, the policy engine, the grant/deny decision, monotonic-confinement narrowing versus expansion, and revocation — is deterministic software on CPU [D], holding **zero learned entries by construction**, and here it is exactly **zero** — **an empty L column is a permitted, correct state**, and here it is the **only** correct state. Concretely, the control plane's pipeline is: ``` WORKER + DECLARED WORKFLOW STATE enter → capability-lattice derivation [D] → the Capability set from the state, never the model (§3.1) → policy engine [D] → ALLOW/FORBID + Fallback over every requested action (§3.2) → tool-boundary mediator [D] → the single choke point every action must cross (§3.4) → credential-proxy layer [D] → scoped, short-lived proxies; never raw secrets (§4.1) → provenance / laundering check [D] → authority traced to a derivation, not a requester (§4.3) → monotonic confinement [D] → narrowing automatic, expansion gated (§3.3, §5) → revocation / ephemeral lifecycle [D] → temporal scope; revocation-by-expiry (§2.4) ``` The model never derives a capability, never evaluates a policy rule, never mediates a tool call, never mints a proxy, never checks provenance, never evaluates an expansion request, and never revokes. Every function in this section is deterministic; the L column contributes nothing here. All artifacts land in `build/25-permissions-least-privilege/`. ### 3.1 Capability derivation: computing the grant, not asking for it [D] The control plane's first act is to *derive* — by computation, never by asking the model — the smallest set of capabilities the current workflow state actually needs. **MiniScope** [6] is the reference design: given a task and the current state, it derives the set of tool calls (and their argument ranges) the task can possibly require, and it authorizes **only** those, with **1–6% latency overhead** across ten real applications. Its two mechanisms are directly reused. First, the **hierarchy**: tools and actions are arranged in a **lattice from narrow to broad**, so that "derive the least privilege" is a *search down the lattice* for the narrowest node that covers the task's requirements — a deterministic optimization, not a model judgment. Second, the **optimization**: the derived set is minimized against the task, so a task that needs to read one file does not receive the whole directory. The model contributes the *task*, never the *grant*; the grant is the output of the derivation. This is the point at which the D/L invariant bites hardest. A capability is derived from the **state** — which stage of the workflow is running, what resources that stage demonstrably consumes, what the evidence so far has shown — not from the model's *intentions*. The model may say "I need write access to the whole repository," and the derivation may answer "the current state is a read-only recon step; you receive read on these three files, and the request is recorded but not granted." The model *requests* authority; the derivation *grants* it, or doesn't. The derivation is a **pure function of the workflow state**, so the same state always derives the same capability set, and the set can be audited, diffed, and regression-tested like any other artifact. Its signature, in effect, is `workflow_state → Capability[]`, with no learned term in the map. The exact `Capability` record is pinned in §5.1. ### 3.2 Policy enforcement: symbolic rules, not model discretion [D] The second pillar answers "is this requested action, against the currently derived capability set, allowed?" — and it answers with deterministic policy. **Progent** [7] is the reference design: it expresses security policy as **symbolic rules** over the agent's actions, with two outcomes — **ALLOW** or **FORBID** — plus a **Fallback action** for requests the rules do not cover. The precision of that correction matters and is recorded here scrupulously: Progent's rule outcomes are ALLOW and FORBID with a Fallback action, *not* a naive ALLOW/DENY pair with a model-chosen substitute. The Fallback is itself a policy decision (typically: deny, and route to a safe default), not a model judgment. The significance for the harness is that policy enforcement is **complete and deterministic**: every request lands in ALLOW, FORBID, or a fixed Fallback, and none of the three is a model call. A request that falls through to Fallback is **denied by default** — fail closed, exactly as the program's M3 rule demands of any deterministic gate. The exact `PermissionDecision` record is pinned in §5.2. ### 3.3 Monotonic confinement: narrowing automatic, expansion gated [D] The third pillar is a discipline over *changes* to the derived capability set, and it is where the phrase "monotonically confined" earns its place. Progent's SMT-based classification distinguishes two directions of change, and treats them **asymmetrically**: *narrowing* — reducing a worker's capability set — is applied **automatically**, as the natural, always-safe direction; *expansion* — granting more authority than the current state has — **requires approval** and is treated as a security transition (§5). This asymmetry is the operational definition of monotonic confinement: authority only ever shrinks automatically and only ever grows through an explicit, evidence-gated, first-class event. The model cannot expand its own authority by asking nicely, because expansion is not on the list of things a request can silently do; it is a *transition* the control plane mediates. Lingering Authority [11] supplies the revocation half of the same idea: capabilities are **revocable** and effect-scoped, so the automatic direction — shrinking — has a concrete mechanism to act through. ### 3.4 Complete mediation at the tool boundary [D] The three pillars are worthless unless every request passes through them. This is Saltzer & Schroeder's **complete-mediation** principle [3], applied at the one place in the harness where every action converges: **the tool boundary**. Every file access, command execution, service call, credential use, and network flow is a tool call; the tool boundary is therefore the **single choke point** through which all authority is exercised. The permission system must *be* that choke point — a mediator that sits between every worker and every tool, such that no action can reach a tool without a grant computed by the derivation pillar and checked by the enforcement pillar. This is the same posture as the OS framing advanced by Pirch et al. [15]: the agent is an untrusted user process, and the permission mediator is the kernel that mediates its every reach. Complete mediation is not a feature of the permission system; it is the **existence condition** of the permission system, and it fails the moment any tool is reachable by a path that does not cross the mediator. ### 3.5 Recapitulation of the invariant at the architecture level The architecture of this component is the three pillars plus the mediation choke point, and the whole of it sits inside a single column. The control plane — capability derivation, the policy engine, the grant/deny decision, monotonic-confinement narrowing versus expansion, and revocation — is deterministic software on CPU [D]. There is no learned entry anywhere in that path, and the invariant that governs it is the one already stated and restated: > **the L column contains at most one entry.** And here the correct value is **zero**: **an empty L column is a permitted, correct state**, and it is the state the enforcement path must be in, because the enforcement path is exactly the place where a model — with its measurably over-privileged tool-choice distribution — must have no decision-making role. The model's relationship to the control plane is a single, well-defined edge: it may *request* authority, and the control plane *grants or denies* it. Request, never grant. That one word is the entire security boundary of this component, and everything else in the paper is the machinery that keeps the word true. --- ## 4. The Five Dimensions of Least Privilege (the scoping framework) "Least privilege" is frequently treated as a single scalar — give the agent *less* — but the measurement and the mechanism both decompose it into **five independent dimensions**, each with its own failure mode, its own guarantee, and its own point in the pipeline where it is enforced. Conflating them produces a permission system that is either too coarse to be meaningful or too scattered to be auditable. The five are layered and complementary: no one of them is sufficient, and dropping any one leaves a hole the other four do not cover. | Dimension | The question it answers | The machinery (paper §) | Enforcement point | Fails on | |---|---|---|---|---| | **Resource scope** | *Which objects may be touched?* | Explicit capability; Capsicum capability-mode descriptors [5]; Miller's object-capability composition [18] (§2.1) | Capability derivation — the handle to a *specific* object, never a path in a globally reachable namespace (§3.1) | Ambient authority — a worker holding all of Piper's reach | | **Action scope** | *Which operations may be performed on those objects?* | Saltzer & Schroeder complete mediation + separation of privilege [3]; AgentBound manifest permissions [20] (§2.2) | Policy engine — read vs write, run vs install, query vs mutate (§3.2) | Confusing the whole tool for the specific action-argument pair | | **Tool scope** | *Which tools are even reachable?* | MiniScope per-call authorization [6]; ToolPrivBench's over-privilege measure [1] (§2.3) | Tool-boundary mediator — the reachable-tool list derived from state (§3.4) | Offering a worker tools it should never have been offered | | **Temporal scope** | *When does authority exist?* | Lingering Authority / PORTICO revocable resource-and-effect capabilities [11] (§2.4) | Revocation / ephemeral lifecycle — expiry and mid-session revocation (§7) | Authority that never expires; a credential outliving its call | | **Data-flow scope** | *Where may information travel?* | SEAgent information-flow graph [2]; Goguen & Meseguer noninterference [17]; CaMeL/tacit provenance [8, 9] (§2.5) | Provenance / laundering check + `data_labels` constraints (§5.1, §4.3) | Two legitimate capabilities composing into a prohibited flow | ### 4.1 Resource scope — which objects may be touched The most familiar dimension, and the one least-privilege mechanisms historically started with: *which* files, directories, endpoints, and devices may be the target of an action. The canonical failure is **ambient authority** — a worker that holds all of Piper's own reach and must be *persuaded* not to use it. The canonical fix is the **explicit capability**: a worker holds a handle to a *specific* object, not a path in a globally reachable namespace. Capsicum's model is the clean statement: in capability mode, a process's authority over the filesystem is reduced to the file descriptors it already holds, so "read this file" is authorized by *possessing the descriptor*, not by the ambient right of Piper's account to read any file she owns. Resource scope is the *object* dimension, and its unit of currency is the capability (the descriptor, the token, the handle), never the global name. Its enforcement point is **capability derivation** — the `resource` field of §5.1 is a specific path, subtree, executable, service, or endpoint, never "the whole filesystem." ### 4.2 Action scope — which operations may be performed on those objects Possessing a capability to an object does not yet say *what* may be done to it. A descriptor may permit read without write; a tool may be reachable without all of its verbs being reachable. Saltzer & Schroeder's least-privilege principle is the founding statement, but their *complete mediation* and *separation of privilege* principles are the ones that make action scope enforceable: every access to every object must be checked against the authority of the actor, and no single mechanism should be sufficient to cross a privileged boundary. In the harness, action scope is where the policy distinguishes "read this file" from "write this file," "run this command" from "install this package," "query this service" from "mutate this service" — a distinction that is easy to state and easy to get wrong when the unit of authority is the whole tool rather than the specific action-argument pair. Its enforcement point is the **policy engine** — the closed `action` enum of §5.1 (read, write, execute, connect, invoke), checked per request. ### 4.3 Tool scope — which tools are even reachable Tool scope is coarser than action scope and answers a prior question: *which* tools exist in a worker's universe at all. The ToolPrivBench result is, at bottom, a tool-scope finding: over-privilege is measured as selecting a tool with more authority than the task requires — i.e., a worker whose tool scope includes tools it should never have been offered. MiniScope's contribution is the most direct: it authorizes tool-calling agents at the granularity of **individual tool calls and their arguments** — a mobile-OS-style authorization model (mirroring how a phone asks permission per capability rather than granting an app "the phone"). The point that matters for design is that tool scope is not "which tools the model *wants*"; it is "which tools the *state* makes reachable," and the two are different lists. Its enforcement point is the **tool-boundary mediator** — a tool not in the reachable set is simply not in the worker's universe, so no request can find it. ### 4.4 Temporal scope — when authority exists Authority that never expires is ambient authority in the time dimension. A worker granted read access to a directory for a single step of the workflow must not still hold it three steps later; a credential minted for one API call must not outlive that call. Lingering Authority names the problem directly: its PORTICO system makes capabilities **revocable** and scopes them to resources and effects, so authority is not merely granted but *withdrawn* as the agent's task moves on. This is the dimension that makes monotonic confinement possible in the first place: narrowing requires that authority can be *reduced over time*, and revocation is the mechanism. Its enforcement point is the **revocation / ephemeral lifecycle** — the `expiry` field of §5.1, plus the automatic-narrowing direction of §3.3. ### 4.5 Data-flow scope — where information may travel The first four dimensions govern *actions*; the fifth governs *flow*. Two individually legitimate capabilities — "read the secret" and "write to the network" — compose into a prohibited *flow* when held by the same worker, and a permission system that only checks individual actions cannot see the composition. This is the dimension SEAgent addresses with its information-flow graph, which monitors agent–tool interactions so that a chain of individually-permitted steps cannot move protected data to an unauthorized sink. It is also the lineage of Goguen & Meseguer's noninterference, and of the capability-provenance tracking in CaMeL and tacit. Data-flow scope is the reason the permission system must be able to reason about *sets* of capabilities and their composition, not just about individual grants — a point the multi-agent laundering problem of §6 makes concrete. Its enforcement point is the **provenance / laundering check** plus the `data_labels` constraint of §5.1 — Round 1 scopes the flow via labels on capabilities; the full information-flow graph is the DEFERRED row of §2. ### 4.6 Ambient authority versus explicit capability The five dimensions share a single underlying distinction, and it is the oldest one in the field: **ambient authority** versus **explicit capability**. Under ambient authority — the model POSIX inherited — a process holds everything its user holds, and confinement is attempted by *filtering* a maximal grant. Under explicit capability, a process holds *only* the capabilities it was handed, and authority is *constructed* by *combining* minimal grants. Capsicum is the pragmatic realization: capability mode turns off the ambient namespace and leaves only the descriptors already held, so the ambient authority is *removed* rather than filtered. Miller's dissertation is the foundational treatment of the object-capability composition that underlies it. The harness's permission system must choose the explicit-capability side **unconditionally**: filtering a maximal grant fails closed only if the filter is perfect, whereas constructing a minimal grant fails closed *by construction*, because there is nothing to filter and nothing ambient to leak. --- ## 5. The Core Schemas (the build's JSON contracts) The following schemas are the load-bearing contracts of the build. Their shapes are EXACTLY these fields, no more and no fewer, and the label "EXACTLY these fields" is itself part of the contract — a field added for convenience is a spec violation. ### 5.1 The Capability record (EXACTLY these fields) The paper's capability derivation (§3.1) — "authority over exact files, commands, services, arguments, credentials, and data flows rather than coarse tool access" — compiles to exactly these seven fields. Together they are the **explicit capability**: a handle to a *specific* object, with a *specific* action, under a *specific* scoping envelope, transferable or not, and expiring. ```json Capability { principal: string // the worker/agent holding this capability (the "who" — a specialist, never "anyone") action: enum // {read, write, execute, connect, invoke} — the closed action-verb set (the "what may be done") resource: string // path | subtree | executable | service | endpoint — the exact object the action targets (the "which object") constraints: object // { arguments, workflow_state, task_id, time, count, data_labels } — the scoping envelope (the "under what bounds") delegation: enum // {allowed, forbidden} — whether the holder may transfer this capability to a peer (§6.3 laundering check) expiry: integer // epoch seconds at which the capability lapses (temporal scope, §4.4); authority never outlives the task provenance: string // the derivation record that justified this capability (§3.1) — traced to the derivation, never the requester (§6.3) } ``` The load-bearing decisions, enforced not advisory: - **`principal` names the holder, and the holder is a specialist, never "anyone."** A capability is minted *for* a specific worker's derived set; it is not a bearer token that anyone holding it may exercise. - **`action` is a closed five-valued enum.** read, write, execute, connect, invoke — nothing else. The policy engine (§3.2) checks every request against this enum; an action verb outside it is not "helpfully" mapped, it is refused. - **`resource` is a specific object, not a namespace.** A path, a subtree, an executable, a service, or an endpoint — never "the filesystem," never "the network." Ambient authority is removed (§4.6) by construction: what is not named is absent. - **`constraints` carries the scoping envelope.** `arguments` (the argument ranges the action may take — MiniScope's per-argument authorization), `workflow_state` (which state this capability is valid *in*), `task_id` (which task it was derived *for*), `time` (the temporal bound), `count` (a use-count bound — allow-once is a count of one), and `data_labels` (the data-flow scope, §4.5 — which sensitivity classes may flow through this capability). - **`delegation` is a closed two-valued enum.** A capability is either transferable to a peer (`allowed`) or not (`forbidden`). Default is `forbidden` — the laundering defense of §6.3 is the *absence* of delegation, not a check bolted on after the fact. - **`expiry` is a hard epoch bound.** A capability past its expiry is as absent as a resource that was never minted — revocation-by-expiry is denial-by-absence applied to time (§7). - **`provenance` records the derivation, never the requester.** The field names the workflow-state derivation (§3.1) that justified the grant, so authority is traceable to *why it was derived*, not to *who asked* — the anti-laundering invariant. ### 5.2 The PermissionDecision record (EXACTLY these fields) The policy engine of §3.2 compiles to exactly these five fields. A decision is the deterministic outcome of holding a requested action against the currently granted set — it is a computation, never a model judgment, and it records *enough* that the delta between "what was asked" and "what is held" is explicit rather than implicit. ```json PermissionDecision { requested: object // the candidate capability being requested (the exact file/command/service/argument range) currently_granted: object[] // the worker's present derived capability set, so the delta is explicit rather than implicit reason_required: boolean // whether this decision crosses into expansion (gated) territory — true ⇒ the expansion gate (§7) must run expansion: object? // present iff reason_required is true — the PermissionExpansionRequest (§5.3) this decision triggers decision: enum // {grant, deny} — the deterministic outcome; grant is computed by the derivation (§3.1), deny is the fail-closed default } ``` - **`requested` is a single candidate capability.** The policy engine decides one request at a time, against the full currently granted set — no batching into an interpretive gray zone. - **`currently_granted` makes the delta explicit.** The decision carries the worker's present set *with it*, so a grant that *narrows* (inside the set) and a grant that *expands* (outside the set) are distinguishable by inspection, never by inference. - **`reason_required` is the expansion flag.** A request inside the current set is decided by the policy engine alone; a request outside it flips `reason_required` to true and routes through the expansion gate (§7) — a structural separation, not a policy nuance. - **`expansion` is present iff the request widens authority.** When present, it is the structured `PermissionExpansionRequest` (§5.3) — the evidence-bearing transition, never an unannotated "more, please." - **`decision` is a closed two-valued enum — {grant, deny}.** Progent's ALLOW/FORBID vocabulary maps here: a grant is an ALLOW computed inside the allowed region, a deny is the FORBID or the Fallback default. There is no third "maybe," and no value that means "ask the model." ### 5.3 The PermissionExpansionRequest record (EXACTLY these fields) The expansion gate of §5 is the paper's "first-class security transition," and its structured input compiles to exactly these seven fields — the paper's §5.1 list, one field each, no more and no fewer. The request is *data*, not a *decision*: it is the structured input to a deterministic evaluation, and the evaluation, not the request, produces the outcome. ```json PermissionExpansionRequest { requested_capability: object // the exact file, command, service, argument range, credential proxy, or data-flow edge being asked for current_capabilities: object[] // the worker's present derived set, so the delta is explicit rather than implicit task_requirement: string // what the current workflow state demonstrably needs that the requested capability would satisfy why_insufficient: string // a specific account of why the current set cannot satisfy that requirement — not "I need more," but "this operation requires write on X, and the current set grants only read" evidence: string // the concrete artifact (a failing step, an error, a task specification) that the requirement is real alternatives_attempted: string[] // what lower-privilege approaches were already tried and how they failed risk: string // the marginal risk of granting the expansion, scoped against the workflow } ``` - **`requested_capability` names the exact widening.** Not "more files" but *this* file, *this* command, *this* service, *this* argument range, *this* credential proxy, or *this* data-flow edge — the expansion is a specific edge, not a vague request. - **`current_capabilities` carries the delta's left side.** The worker's present derived set, so the evaluation sees exactly *what is being added* rather than being asked to reconstruct it. - **`task_requirement` is the state's demand, not the model's.** What the workflow state demonstrably needs — the derivation's input, restated as a requirement the expansion must satisfy. - **`why_insufficient` is a specific insufficiency, not a plea.** The paper's exact discipline: "this operation requires write on X, and the current set grants only read" — a concrete delta, not "I need more." - **`evidence` is a concrete artifact.** A failing step, an error, a task specification — something that makes the requirement *real* rather than asserted. - **`alternatives_attempted` is a list of lower-privilege approaches already tried and how they failed.** This is the field that the transient-failure rule (§7.3) polices: "the last attempt errored" does *not* populate it, because a transient failure is evidence about the attempt, not about the privilege. - **`risk` is the marginal risk of granting, scoped against the workflow.** The expansion is costed, not free — the evaluation weighs `risk` against the demonstrated requirement. --- ## 6. Credentials as Capabilities, and the Confused-Deputy Defense Authority over *files and tools* is only half the problem. The other half is authority over *secrets* — and secrets are where least privilege has historically collapsed, because a secret that enters the model's context is no longer scoped at all. This section treats credentials as capabilities, and treats the classic confused-deputy failure as the case the permission system must structurally prevent. ### 6.1 Credentials via scoped proxies, never raw secrets The rule is simple and absolute: **a worker never receives a raw secret.** It receives a *capability* — a scoped, short-lived proxy that authorizes one operation, against one service, for one bounded interval — and the proxy, not the worker, holds the credential. This is the capability model applied to secrets: the credential is the ambient authority (a key that opens everything it touches), and the proxy is the explicit capability (a handle that opens exactly what the task needs). The program's own M1 rule — secrets never enter model context, the tool layer injects credentials at call time, and the model holds only opaque handles — is the same principle stated as a standing rule; this component is its coding-harness instantiation. The lineage that makes it rigorous is the capability-tracking line. tacit tracks capabilities through the agent's execution and reports **100% security across 131 trials** in its classified mode; Verifiably Safe Tool Use applies STPA hazard analysis to a capability-enhanced MCP to make tool use verifiably safe; CaMeL defeats prompt injections *by design* with capability and provenance tracking. The common thread is that the secret is never the thing the model holds — the model holds a tracked capability, and the tracking is what makes the proxy's scope enforceable rather than advisory. Concretely, in the harness: a worker requesting a credential receives a `Capability` (§5.1) whose `resource` is the proxy handle, whose `action` is the one operation the proxy authorizes, whose `constraints.time` is the bounded interval, and whose `constraints.count` is the use-count — and the raw credential lives in the proxy, which the tool layer injects at call time, never in the worker's context. ### 6.2 SEAgent: the mandatory-access-control reference The reference design for this section is SEAgent [2] — the framework whose paper is titled "Taming Various Privilege Escalation in LLM-Based Agent Systems: A Mandatory Access Control Framework" (SEAgent is the framework name, not part of the title). Its three components map cleanly onto the harness's needs. First, it is a **mandatory access control** (MAC) framework built on **attribute-based access control** (ABAC): the grant decision is not left to the agent's discretion (discretionary access control) but is enforced by policy over the attributes of the actor, the object, and the operation — which is exactly the "deterministic grant/deny, not model discretion" posture of §3. Second, it monitors agent–tool interactions via an **information-flow graph**: the data-flow scope of §4.5, made concrete, so that a chain of individually-legitimate actions cannot compose into a prohibited flow. Third, it explicitly identifies and blocks a **variant akin to the classic confused-deputy problem** — the agent induced to exercise its own authority on behalf of a hostile instruction — and it does all of this while maintaining a **low false-positive rate and negligible system overhead**. The overhead claim matters doubly for the harness: a permission system that adds a model call per request is not only insecure, it is unusable, and SEAgent demonstrates that mandatory control need not be either. ### 6.3 The multi-agent laundering problem The paper names a failure mode no single-agent permission system sees: **specialist agents laundering authority through one another.** In the harness, workers are *specialists* — a repair agent, a research agent, a network agent — and their capabilities are derived per-specialty. The laundering attack is a *chain*: the repair agent, which has no network capability, asks the research agent to "fetch this reference," and the research agent — which has no network capability either — asks the network agent to fetch it, and the network agent, which *does* have network, complies, because the request came from a *peer* it trusts. Each individual grant is legitimate; the *composition* is a capability the repair agent was never supposed to hold, acquired not by asking for it but by *borrowing* it through a chain of peers. This is data-flow scope applied to the *social* structure of the harness rather than to a single agent's actions. The defense is the same in both places: **authority must be traceable to a *derivation*, not a *requester*.** A request arriving at the network agent must carry the provenance of the original capability derivation that justifies it, and the network agent's mediator must check that provenance against the *originating* worker's derived set — not against the immediately-preceding peer's. If the repair agent's derived set lacks network authority, then no chain of peer requests can synthesize it, because the provenance check fails at the first hop. This is the mechanism behind "prevent specialist agents from laundering authority through one another," and it is the multi-agent generalization of the same capability-tracking discipline that tacit and CaMeL apply within a single agent. Concretely, in the harness: the `provenance` field of §5.1 carries the derivation that minted a capability, `delegation: forbidden` is the default, and the provenance check (§3 pipeline) refuses any request whose provenance does not resolve to the originating worker's own derived set. ### 6.4 Hardy's Confused Deputy: the founding story The entire section is a footnote to the founding story, which Norm Hardy told in 1988 and which is worth retelling because every element of it recurs in the LLM-agent setting. A FORT compiler at Tymshare, installed in the protected directory SYSX, was instrumented to write usage statistics to a hardcoded file, (SYSX)STAT. The compiler — the *deputy* — held elevated authority, because it alone could write into SYSX. A user discovered that the compiler's debug-output option accepted a filename, and that by supplying an arbitrary target file the user could redirect the compiler's privileged write to *any* file — exploiting the deputy's authority to do something the user could not do directly. The deputy was *confused*: it exercised its own authority on behalf of a request it did not understand, and the attacker rode that authority to a target the attacker could never have reached. The story is the template for everything in this section. The confused deputy is the agent with a broad tool; the debug-output filename is the injected instruction or laundered request; the protected SYSX authority is the raw credential or ambient capability the agent holds; and the fix — why capabilities "might have been invented" — is the explicit capability: give the deputy a *handle to the specific file it may write*, not the ambient right to write to the whole protected directory. SEAgent's confused-deputy variant is the same failure in the agent setting, and the permission system's answer — credentials as scoped proxies, provenance-tracked requests, and deterministic grant/deny — is the same fix, applied forty years later, to a deputy that is no longer a compiler but a learned model. --- ## 7. Monotonic Confinement, and Escalation as a First-Class Event §3 established that narrowing is automatic and expansion is gated. This section specifies the gate: the `PermissionExpansionRequest` (§5.3), a first-class security transition that makes every privilege increase visible, evidence-gated, and auditable — and it closes with the discipline that a transient failure is not a justification. ### 7.1 The PermissionExpansionRequest When the capability derivation produces a grant that is *larger* than the current state's set — or when a worker explicitly requests more — the control plane does not silently widen. It opens a security transition carrying, at minimum, the seven fields of §5.3: the requested capability, the current capabilities, the task requirement, the why-insufficient, the evidence, the alternatives-attempted, and the risk. The request is *data*, not a *decision* — it is the structured input to a deterministic evaluation, and the evaluation, not the request, produces the outcome. This is the mechanism by which "treat every privilege expansion as a first-class security transition" becomes something the system *does* rather than something the thesis *says*. ### 7.2 The decision space: deny, allow-once, allow-narrower, require-human The expansion evaluation returns one of a fixed set of outcomes, and **none of them is a model call**: - **Deny**: the requirement is not demonstrated, the evidence is insufficient, or the requested capability exceeds what the task can justify; the request is refused and the worker continues (or fails) with its current set. - **Allow-once**: the expansion is granted for exactly one action, then auto-revoked — the temporal-scope discipline of §4.4 applied to the grant itself (a `constraints.count` of one). - **Allow-narrower**: the requirement is valid but the requested capability is broader than it justifies; the control plane grants the *narrowest* capability that satisfies the demonstrated requirement, not the one requested. - **Require-human**: the expansion crosses a boundary the deterministic plane is not authorized to cross — an irreversible action, a new credential, a new service — and is routed to the operator for out-of-band confirmation, per the program's M3 token-gating. The key property is the **asymmetry preserved throughout**: the automatic direction is always toward less authority, and the only direction that admits a human or a one-time narrow grant is the expansion direction. A worker can, on its own, end up with *less* than it started; it can never, on its own, end up with *more*. ### 7.3 Transient failure is not justification for escalation The discipline that closes this section is the direct correction of the failure mode the harness's own loop would otherwise teach. ToolPrivBench found that over-privilege is amplified by transient tool failures — a tool call fails, and the model reaches for a broader tool. In a harness built on iterative repair, that is not a corner case; it is the *steady state*. The permission system must therefore encode the rule explicitly: **a transient failure is evidence about *the attempt*, not about *the privilege*.** "The call failed" says nothing about whether a higher-privilege call would have succeeded, and it is specifically *not* the `why_insufficient` and `evidence` fields of a `PermissionExpansionRequest`. The request's `evidence` field must demonstrate that a *lower-privilege alternative was genuinely insufficient* — and "the last attempt errored" does not meet that bar. This one rule is what keeps the harness's repair loop from being a privilege-escalation treadmill: the model may retry, may re-derive, may narrow, but it may not convert its own failures into authority. --- ## 8. The L Column — zero learned entries (the requestor is not a decider) Every element of this component is classified into one of two columns: **[D]** — deterministic software running on CPU — or **[L]** — a learned model. The structural invariant that governs the entire design is: > **the L column contains at most one entry.** The permission control plane — capability derivation, the policy engine, the grant/deny decision, monotonic-confinement narrowing versus expansion, and revocation — is deterministic software on CPU [D], with **zero learned entries by construction**. The model may *request* authority but never *grants* it; the grant decision is a computation over the current workflow state and the candidate capability, not a judgment the model is asked to make. **An empty L column is a permitted, correct state** — a deterministic phase of the harness that needs no model call at all — and here, as in Component 24, it is the *only* correct state for the enforcement path: a permission system with any learned entry in the grant/deny decision has reintroduced the very entity — a model whose tool-choice distribution is measurably over-privileged — into the one place whose correctness must not be probabilistic. The only learned elements in the system are *outside* the control plane: the harness's model calls that *request* capability. Inside the control plane there is nothing to learn, and nothing that learns. This is the same invariant that governs Component 24, held at the same strength but applied to a different moment. The sandbox's invariant holds *during execution*; the permission system's invariant holds *before execution*, at the request boundary. The two compose: the permission system decides what a worker may *ask for and receive*, and the sandbox decides what the received capability may *do*. Neither has a learned entry in its enforcement path, and both are correct to have none. ### 8.1 Why the model's tool-choice distribution is the reason The claim that the grant/deny decision must have zero learned entries is a reading of the measurement, not paranoia. The most decisive evidence is ToolPrivBench [1]: across eight domains and five recurring risk patterns, **six of the eleven evaluated models exceed a 30% over-privileged tool-use rate (OPUR)**, with the worst offenders being the small open-weight models the harness itself most plausibly runs locally — **Qwen3-8B at 64.9%** and **LLaMA-3.1-8B at 55.9%**. These are not adversarial settings; they are ordinary tool-selection tasks where the model reaches for a higher-privilege tool than the task actually requires. The inversion sharpens when one looks at what *fixes* the problem: general safety alignment — the very intervention that makes models refuse harmful requests — does not reliably transfer to least-privilege tool choice; safety alignment *increased* over-privilege for Qwen2.5-7B (from 50.4% to 60.7%) and reduced it only modestly for Ministral (68.8% to 62.5%). A model made *more* safe in one sense became *less* safe in the sense that matters here. The lesson is structural: least privilege is not a behavior the model acquires from alignment; it is a property the *system* imposes, because the model's distribution over tool choices does not honor it and alignment does not push it there. Hence zero, not one: the model is the *requestor* the control plane mediates, never a *decider* within it. ### 8.2 Zero, not one — recapitulated at the architecture level Recapitulating the invariant at the architecture level, because this is the section it governs: > **the L column contains at most one entry.** The entire control plane — capability derivation, the policy engine, the grant/deny decision, monotonic-confinement narrowing versus expansion, and revocation — is deterministic software on CPU [D], holding **zero learned entries by construction**. The model is **not a contributor** to this plane; it is the **requestor** the plane mediates. **An empty L column is a permitted, correct state** — and here it is the **only** correct state for the enforcement path: a control plane with any learned entry in the grant/deny decision has put a model with a measurably over-privileged tool-choice distribution in charge of deciding what authority is granted. The only learned elements in the system are **outside** the control plane — the harness's model calls that *request* capability — and **inside** the control plane there is nothing to learn, and nothing that learns. ### 8.3 The kinship with the program's own M3 and M5 rules This component is not a new idea entering the program from outside; it is the coding-harness instance of two rules the program already ratifies. The prompt-injection ruleset's **M3** makes irreversible and high-blast actions token-gated by a *deterministic* (non-LLM) classifier that fails closed regardless of who the request claims to be from; the permission control plane is the same principle applied to *authority* rather than *bluntness* — a deterministic grant/deny gate that fails closed. The ruleset's **M5** routes state-changing tool calls through deterministic gates; the permission system is precisely such a gate, placed at the tool boundary where every state-changing call must pass. The relationship to Component 24 is the closest of all: where the sandbox is the *fence*, this component is the *law* — the sandbox enforces that received capabilities cannot escape their containment, and the permission system decides which capabilities are ever minted. A fence without a law contains everything and authorizes nothing; a law without a fence authorizes precisely and enforces nothing. The harness needs both, and they share one invariant. --- ## 9. Honest Measurement — least privilege as a measured, legible property A permission system that cannot be measured cannot be trusted, and a permission system that cannot be *read* cannot be audited. The measurement machinery has three parts — the over-privilege rate, the false-positive and overhead audits, and the human-factors question — all deterministic software on CPU [D], and all converging on the claim that least privilege must be a *legible* property, not merely an enforceable one. ### 9.1 The over-privileged tool-use rate (OPUR) The headline metric is the over-privileged tool-use rate (OPUR) from ToolPrivBench [1], and it is the number the harness must drive toward zero. Six of eleven evaluated models exceed 30% OPUR, with Qwen3-8B at 64.9% and LLaMA-3.1-8B at 55.9%; across eight domains and five recurring risk patterns, the benchmark measures both initial selection and escalation after transient failures. The metric's value is twofold. It is a *detection* instrument: it tells the harness whether its own model, running its own tool-choice loop, is over-privileged even before any policy exists. And it is a *regression* instrument: the OPUR of the harness's tool-choice loop, measured against a **policy-derived oracle** of "what the task actually needed," is the number that must fall when the permission system is introduced, and that must not creep back up as the harness evolves. The benchmark's own mitigation finding — privilege-aware post-training, not general safety alignment — is a hint, not a substitute: the harness should not expect alignment to fix this, because §8.1's numbers show alignment can make it worse. ### 9.2 False positives and overhead A least-privilege system that over-blocks is one the operator will disable, and one that adds a model call per request is one the harness cannot afford. SEAgent is the reference for the *cost* side of the ledger: its mandatory control framework blocks various privilege escalation while maintaining a **low false-positive rate and negligible system overhead**. The false-positive claim matters because a MAC gate that denies legitimate requests forces a choice between a broken harness and a disabled gate; the negligible-overhead claim matters because the permission mediator sits on the **hot path** of every tool call in the harness. Both are honest-measurement requirements, not design flourishes: the harness must measure the permission system's false-positive rate against its own workloads and its per-request overhead, and treat regressions in either as permission-system bugs. Because the control plane is deterministic and zero-L (§8), the false-positive and overhead audits are themselves deterministic — a per-request cost counter and a deny-reversal ledger, neither involving a model. ### 9.3 The human-factors question The least-settled measurement is the human one: does letting *users* author permission policies actually improve protection against agent overreach? The single work directly on the question is "Do User-Authored Permission Policies Improve Protection Against AI Agent Overreach?" — a single-author preprint by Ting Yan, roughly eleven days old at the time of verification, and unrefereed. It is flagged as such here scrupulously: the finding is interesting and unvalidated, and it must not be cited as settled. The question it poses is nonetheless the right one for the harness, because the harness's policies are ultimately authored by the operator and by Piper, not by the model — and whether human-authored policies over-trust or under-trust is an empirical matter the harness should *measure*, not assume. The mention-level systems reinforce the same theme from the mechanism side: Conleash's consent-driven MCP authorization makes the *grant* itself a user-facing, legible decision ("options, not clicks"); MCP-Secure adds a runtime access-control layer for privilege-aware tooling; CapChain binds capability tokens to a verifiable provenance log; SkillScope pushes least-privilege enforcement down to the granularity of individual skills. ### 9.4 Legibility as a property, not a courtesy The thread that unites the measurement machinery is that least privilege must be **legible**: the derived capability set, the policy rules, the grant/deny log, and every `PermissionExpansionRequest` must be readable artifacts, not buried state. Legibility is what makes §7's escalation discipline auditable — a history of "denied, allow-once, allow-narrower, require-human" outcomes is a record the operator can *read* to see whether the permission system is working or silently over-granting. It is also what makes the honest measurement of §9.1 and §9.2 possible, because a rate can only be computed against a policy oracle that is itself inspectable. Legibility is not a courtesy appended to the permission system; it is the property that lets the operator — and Piper — verify the law the fence enforces, rather than merely trusting it. --- ## 10. A/B and Ablation — the component earns its place The permission system, like every component, is **kept only if it strictly dominates its own absence** — measured, not asserted. **The baseline is the ambient-authority path** — the worker holding all of Piper's reach and having to be *persuaded* not to use it, which is what the harness does *without* this component. The full permission system is the **arm**: the derivation → policy → mediation → proxy → provenance → expansion-gate → revocation pipeline of §3–§7. Because the permission system's value is *authority confinement* (not a quality-of-output improvement), the instruments are all **authority counters**, never a self-report: 1. **OPUR driven down** — the over-privileged tool-use rate (§9.1) measured against the policy-derived oracle; the baseline is the harness's model over-privilege rate (six-of-eleven-over-30% territory, §8.1), and the arm must drive it toward zero. 2. **Expansion requests gated** — every privilege increase becomes a `PermissionExpansionRequest` (§5.3) with a decision in {deny, allow-once, allow-narrower, require-human}; the baseline widens silently, the arm refuses to widen at all without the gate. 3. **Laundering blocked** — a multi-agent chain attempting to synthesize authority through peers (§6.3) fails at the first hop; the baseline lets the composition succeed, the arm's provenance check refuses it. 4. **Raw secrets never held** — no worker context ever contains a raw credential (§6.1); the baseline holds ambient secrets, the arm mints only scoped proxies. 5. **Legible grant/deny log** — every grant, deny, and expansion is a readable artifact (§9.4); the baseline has no log to read, the arm's ledger is the audit trail. **The A/B decision rule (explicit).** The permission system is **kept iff** the arm strictly dominates the ambient-authority baseline on the authority instruments — OPUR driven down, expansions gated, laundering blocked, raw secrets never held — at a defensible false-positive rate and per-request overhead (§9.2). A mechanism whose *addition* degrades a measure is a demonstrated harm and is removed outright. **Never keep a mechanism because it sounds rigorous.** If the arm is indistinguishable from the baseline on the measures that matter, the invariant's escape hatch fires: **an empty L column is a permitted, correct state**, and the machinery is pared back until the evidence changes. **The ablation rule (mechanical).** One mechanism removed at a time, everything else held fixed, the delta measured on the instruments above: 1. **Ablate capability derivation** (§3.1) — grant the ambient set instead of deriving from state. The delta must show OPUR *rising* (the worker reaches broader tools than the task requires) — the direct test of the "derived, not declared" claim. 2. **Ablate the policy engine** (§3.2) — let requests pass unchecked. The delta must show an off-manifest action succeeding, i.e. the boundary moving from deterministic to nonexistent. 3. **Ablate the tool-boundary mediator** (§3.4) — give one tool a path that skips the choke point. The delta must show an action reaching that tool without a grant — complete mediation is the existence condition, and bypassing it fails it. 4. **Ablate the credential-proxy layer** (§6.1) — hand raw secrets to the worker. The delta must show a raw secret in the worker's context, i.e. a credential that is no longer scoped at all. 5. **Ablate the provenance/laundering check** (§6.3) — check authority against the immediately-preceding peer instead of the originating derivation. The delta must show a repair agent acquiring network authority through a peer chain — the laundering attack succeeding. 6. **Ablate the expansion gate** (§7) — allow silent widening. The delta must show a privilege increase that produced no `PermissionExpansionRequest` and no decision — the escalation becoming invisible. 7. **Ablate the revocation lifecycle** (§2.4, §7) — let capabilities linger. The delta must show a stale capability replayed after its task — lingering authority the system no longer revokes. 8. **Ablate the zero-L contract** (§8) — introduce a learned "authority judge" into the grant/deny path. The delta must show the model's own over-privileged tool choices *succeeding where the deterministic engine refuses them*, proving the learned entry is the over-privilege re-admitted, not a defense. A mechanism is **DELETED** iff its removal does **not** degrade the honest measures beyond noise. The L column is never granted a single entry to "fix" a problem a deterministic mechanism can solve — that would be re-importing the over-privileged requestor through the back door. --- ## 11. Implementation Order (build sequence) Nine phases, each independently testable and reversible, landing in `build/25-permissions-least-privilege/` as standalone importable modules. The order is deliberate — it builds the control plane from the capability lattice outward, then the mediator and proxy, then the provenance and expansion machinery, then the revocation and measurement rig — in the exact sequence the paper's mechanics require: **capability-lattice + derivation first, then the policy engine (ALLOW/FORBID + Fallback), then the tool-boundary mediator, then the credential-proxy layer, then the provenance/laundering check, then the expansion-request gate, then the revocation/ephemeral lifecycle, then the OPUR + false-positive/overhead instrumentation.** Rollback = delete or flag-off that phase's module; nothing earlier breaks because phases only add modules and never mutate live harness code. | Phase | Delivers | Isolated test | Rollback | |---|---|---|---| | **1. Capability lattice + derivation** | `capability.py` (§3.1 the `Capability` schema + the pure `workflow_state → Capability[]` derivation; the MiniScope lattice hierarchy + optimization) | Derive a capability set from a fixture state; assert it names exactly the state's paths/actions/tools and the scoping envelope; assert the derivation is a pure function (same state → same set); assert the lattice search returns the *narrowest* node covering the task's requirements; assert a read-only recon state derives read on exactly the files it consumes, not the whole directory; assert **no LLM consulted**. | Delete `capability.py`. | | **2. Policy engine** | `policy.py` (§3.2 the `PermissionDecision` schema + the ALLOW/FORBID evaluator with Fallback) | Evaluate an in-set request → `grant`; an out-of-set request → `deny` (fail closed); an undecided request → the fixed Fallback (deny-to-safe-default); assert the allowed region is computed (symbolic), never model-resolved; assert `reason_required` flips true exactly when a request is outside the current set; assert **no LLM consulted**. | Delete `policy.py`. | | **3. Tool-boundary mediator** | `mediator.py` (§3.4 the single choke point every action must cross) | Route a file read / command exec / service call / network flow through the mediator; assert no action reaches a tool without a grant computed by §3.1 and checked by §3.2; assert a tool outside the reachable set is simply absent from the worker's universe; assert the mediator is the *only* path (a bypass fixture is refused); assert **no LLM consulted**. | Delete `mediator.py`. | | **4. Credential-proxy layer** | `proxies.py` (§6.1 scoped, short-lived proxies; secrets never raw, never in model context) | Mint a proxy for one operation against one service for one bounded interval; assert the worker receives the proxy handle, not the raw secret; assert the raw credential lives in the proxy and is injected at call time (M1); assert the proxy is scope-bound (`constraints.time` + `constraints.count`); assert a proxy past its scope is refused; assert **no LLM consulted**. | Delete `proxies.py`. | | **5. Provenance / laundering check** | `provenance.py` (§6.3 authority traceable to a derivation, not a requester; multi-agent laundering blocked) | Simulate the three-hop chain (repair → research → network); assert the network agent's mediator checks provenance against the *originating* worker's derived set, not the immediately-preceding peer's; assert the chain fails at the first hop; assert `delegation: forbidden` is the default and a forbidden transfer is refused; assert **no LLM consulted**. | Delete `provenance.py`. | | **6. Expansion-request gate** | `expansion.py` (§7 the `PermissionExpansionRequest` schema + the {deny, allow-once, allow-narrower, require-human} decision space) | Evaluate a widening request → assert it opens a `PermissionExpansionRequest` (never a silent widen); assert each of the four outcomes is reachable and none is a model call; assert allow-once grants exactly one action then auto-revokes; assert allow-narrower grants the narrowest satisfying capability, not the one requested; assert a transient-failure "evidence" is refused as insufficient; assert **no LLM consulted**. | Delete `expansion.py`. | | **7. Revocation / ephemeral lifecycle** | `lifecycle.py` (§2.4 temporal scope; revocation-by-expiry; automatic narrowing) | Mint capabilities with `expiry`; assert a past-expiry capability is as absent as an unminted one (revocation-by-expiry); assert narrowing is applied automatically and expansion only via §6's gate; assert a stale handle replayed after its task is rejected, not honored; assert the lifecycle is a pure function of the episode; assert **no LLM consulted**. | Delete `lifecycle.py`. | | **8. OPUR instrumentation** | `opur.py` (§9.1 the over-privileged tool-use rate against a policy-derived oracle) | Run a fixture of tool-choice tasks; assert the OPUR of the harness's tool-choice loop is computed against the policy-derived oracle of "what the task actually needed"; assert the rate falls when the permission system is introduced and does not creep back on re-run; assert the oracle is itself inspectable (legible, §9.4); assert **no LLM consulted** in the measurement. | Delete `opur.py`. | | **9. False-positive + overhead + legibility audits** | `audit.py` (§9.2 the FP/overhead counters + §9.4 the readable grant/deny log and expansion history) | Run a workload; assert the false-positive rate (deny-reversal ledger) and per-request overhead are measured deterministically; assert the grant/deny log and every `PermissionExpansionRequest` are readable artifacts; assert a regression in FP or overhead is flagged as a permission-system bug; assert **no LLM consulted**. | Delete `audit.py`. | The DEFERRED rows (information-flow graph, human-factors experiment — rows 10–11 of §2) enter the sequence at the same position as Phase 5 and Phase 9 respectively, only when a measured gap justifies them and the 72h-age + scrutiny gate clears. The measurement rig (Phases 8–9) is the only phase that ever *invokes* the permission system end-to-end — only to measure it, never to modify the live harness. The permission system invents no new model, no new execution substrate of its own, and no new diagnosis path; it reuses the harness's own workers as the requestors and adds only the deterministic authorization boundary around them. --- ## 12. Dependencies & Supply-Chain Per Piper's rule: nothing is installed unless >72h old AND (≥10 stars OR deep-scrutinized). Round 1 is **zero-install (Python-only, stdlib + in-repo)**: | Dependency | Why | Status / age-stars | Install needed? | |---|---|---|---| | stdlib `enum` | §5 the `action` / `delegation` / `decision` enums and the schema type hints | CPython stdlib. | **No** | | stdlib `typing` / `dataclasses` | §5 the frozen schemas (immutable, rebuilt not mutated) | CPython stdlib. | **No** | | stdlib `json` | §5 the Capability / PermissionDecision / PermissionExpansionRequest serialization | CPython stdlib. | **No** | | stdlib `time` / `datetime` | §5.1 the `expiry` epoch bound; §7 the temporal scope | CPython stdlib. | **No** | | stdlib `hashlib` | §9.4 the legibility ledger's content hashing (audit trail integrity) | CPython stdlib. | **No** | | Python toolchain (`python3`, pinned) | §3–§9 all deterministic services | In-repo, pinned interpreter. | **No** | | Harness components 01–24 | §1 the workers (specialists) that request authority; §24's sandbox is the fence this law enforces; §23's state schema feeds §3.1 | The harness's own stages, already vetted. | **No** | **Preferred path: zero-install.** Round 1's deterministic control plane is Python-only and stdlib; there is no third-party Python package, no runtime substrate, and no new model. The DEFERRED rows (§2 rows 10–11) are the SEAgent information-flow graph and the human-factors policy experiment — the former is a *design pattern* (a deterministic graph over capabilities, implementable in-repo, no external install) and the latter is a *measurement* (an experiment run against the operator, no dependency at all). No model, no learned-judgment dependency, and no third-party package is introduced in Round 1. --- ## 13. Guardrails & Reversibility **Revocability.** - The build lives entirely in `build/25-permissions-least-privilege/`; it never writes into the live harness's source. - A **feature flag** (env var `PERMISSIONS=25|ambient`, default `ambient` during Round 1 A/B) routes authorization; the flag alone switches between the permission system and the ambient-authority baseline — no code deletion required to revert. The default stays `ambient` until §10's A/B shows strict domination, because **an empty L column is a permitted, correct state** and an unproven mechanism must not be the default. - The measurement rig (`opur.py` / `audit.py`) *measures* the permission system and never modifies it. - The permission ledger (every derived capability, every policy decision, every grant, every deny, every expansion request, every decision outcome, every proxy mint, every revocation) is append-only and versioned; rolling the build back never loses it. This ledger *is* the legibility the paper claims (§9.4): every grant, every denial, and every expansion has a name and an address. **Invariants (must hold, checked at the end of every phase).** 1. **the L column contains at most one entry** — and at this component it is exactly **zero**: the permission control plane holds zero learned entries by construction, and the model is the requestor the plane mediates, never a decider. **an empty L column is a permitted, correct state** — and here it is the **only** correct state for the enforcement path. 2. **Request, never grant** — the model may request authority and never grants it; no learned model appears in capability derivation, policy enforcement, tool-boundary mediation, proxy minting, provenance checking, expansion evaluation, or revocation. 3. **Derived, not declared** — authority is a pure function of the workflow state (MiniScope hierarchy search + optimization); the same state always derives the same capability set; ambient authority is removed, not filtered. 4. **Explicit capability over ambient authority** — a worker holds handles to specific objects with specific actions and bounds, never a path in a globally reachable namespace; credentials are scoped, short-lived proxies, never raw secrets. 5. **Complete mediation** — the tool boundary is the single choke point through which every action passes; no tool is reachable by a path that does not cross the mediator. 6. **Monotonic confinement** — authority shrinks automatically and only grows through an explicit, evidence-gated, first-class event; a transient failure is evidence about the attempt, never justification for escalation. 7. **Laundering blocked** — authority is traceable to a derivation, not a requester; delegation defaults to forbidden; a multi-agent chain cannot synthesize authority the originating worker was never derived to hold. 8. **Honest measurement** — the permission system is kept only if it strictly dominates the ambient-authority baseline on OPUR, gated expansions, blocked laundering, and never-held raw secrets, at a defensible false-positive rate and per-request overhead, all reported on held-out workloads; the grant/deny log and expansion history are legible artifacts. --- *End of spec. This document is the build contract; the next document records the A/B outcome.*