← LAB / Articles

Technical companion

Binding a Delegated Edit: Grants, Approvals and the Check at the Write

The companion to Who May Make This Change? One invented edit, carried out by an agent on its owner’s behalf, followed through capability theory, the OAuth family of standards and a complete decision trace. The question throughout is narrow: what must the component that performs the write check, so that the change carries the owner’s authority and nothing else does?

By Loki

1. The question at the moment of the write

Someone who holds authority over an artifact asks an agent to change it for them. Several questions follow, and they are easy to blur. Who may make this change? What does the system actually check, and where? How is a narrower scope handed over? And what happens if the request, or the artifact the request refers to, changes between the moment of approval and the moment of the write?

This companion follows one small edit through those questions. Ines owns a short field-notes document. She asks a drafting agent to tighten one section, Limits. She delegates a narrow grant, reviews the agent’s proposal, and approves it. The agent then submits the change to the document service, which decides whether to apply it. A co-editor, Tomas, has his own rights over the same document and can edit it at any time.

The answer the example supports is specific rather than sweeping. In this example, a change carries Ines’s authority only if the component that performs the write checks, in one step, a grant narrower than her own rights, how that grant is presented, whether it is still valid, whether every write is inside it and inside her own rights, and whether the submitted change is the one she approved, against the version she approved it on. Identity, scope, approval, provenance and enforcement each answer a different question. A permission or approval the enforcement point does not consult protects nothing. An approval that names only the section accepts whatever that section, or that request, has become.

An invented example. Ines, Tomas, the agent, the document service and every text below are made up for teaching. They depict no real system, product, user or incident. The grant is a simplified illustration of attenuation, not a production token format. The model is small enough to enumerate every setting, and it is not a security rating or a measurement of anything real.

The interactive explainer lets you change each of the model’s eight inputs and see the decision move. This page states the theory, the exact rules the explainer implements and the numbers it reproduces.

2. Terms kept apart

The vocabulary in this area is shared by operating-systems research, web standards and product documentation, and the same word often means different things in each. The definitions below are the ones this page uses. Where a source supplies the definition, it is cited.

Principal, or subject
The finest-grained unit to which a system can give distinct rights. It is system-dependent: “all processes run by a given user account” in one system, an individual process instance in another (Miller 2006, §8.1).[3] An agent process can be a principal; so can a user account. Neither is automatically a person.
Identity and authentication
Establishing which party presents a request. In OAuth this can be client authentication or proof of possession of a key; in an operating system, the kernel’s record of a process’s credentials. Knowing who asks does not say what they may do.
Permission
A direct access right: what a subject may invoke on the objects it can reach directly (Miller 2006, §8.1).
Authority
“The effects that a program may cause on objects it can access, either directly by permission, or indirectly by permitted interactions with other programs” (Miller 2006, §8.1). A deputy’s permission can become its requester’s authority.
Ambient authority
Authority “exercised, but not selected, by its user” (Miller, Yee and Shapiro 2003).[4] Opening a file by name, with whatever rights the calling process happens to have, is the standard example. The defining feature is the absence of explicit selection, not the size of the privilege.
Capability
An unforgeable reference that both designates an object and conveys the actions allowed on it. Dennis and Van Horn’s capability “locates by means of a pointer some computing object, and indicates the actions that the computation may perform with respect to that object.”[1]
Designation
Naming which object an operation targets.
Delegation and impersonation
In delegation, “principal A still has its own identity separate from B … any actions taken are being taken by A representing B.” In impersonation, A “is indistinguishable from B” within the rights the token conveys (RFC 8693, §1.1).[11]
Attenuation
Deriving a credential or authority that permits a subset of what its parent permits. Revocation (withdrawing later) and expiry (bounding in time) are different operations.
Scope (OAuth)
“A list of space-delimited, case-sensitive strings. The strings are defined by the authorization server” (RFC 6749, §3.3).[7] A scope called write means whatever its issuer says it means.
Audience
The resource server or servers a token is meant for. Scope says what; audience says where (RFC 8707, §1).[12]
Sender constraint
A token that the presenter can use only by demonstrating knowledge of a key or secret bound to it (RFC 9700, §2.2.1).[10] It is a statement about who holds the key. Whether anything about the content of the request is also bound depends on the particular mechanism and on what it signs; base DPoP, for example, does not sign the body.
Approval
A specific, attributable consent to a defined change. It becomes part of an authorization decision only when the enforcement point checks the request against it.
Provenance
A record of the chain: who asked, who acted, under which grant and approval, and what was decided. In this example the decision log is written after the decision and is not consulted to authorize it. That is the example’s design. A system can legitimately authorize using verified provenance or issuer-validated delegation information under an explicit policy; what no system should do is accept a self-authored note as a grant of authority.
Enforcement point
The component that performs or refuses the effect and evaluates the checks. A user interface that displays an approval is not one.
Version precondition
Applying a change only if the target’s current version equals the version the approval named, evaluated in the same indivisible step as the write.

3. Designation, authority and the confused deputy

Capabilities and the no-amplification rule

Dennis and Van Horn’s 1966 paper on multiprogrammed computations describes each computation running inside a sphere of protection specified by a list of capabilities, a C-list.[1] A capability names an object and carries access indicators (execute, read, write and their combinations). A superior sphere can grant a capability to an inferior sphere with restricted access, and the paper is precise about the limit: the grant cannot pass a capability that is not implied by one the higher sphere already holds. That rule, that delegation can narrow but not amplify, is the invariant the model below tests at every step of its grant chain.

The setting matters. The paper proposes supervisor semantics for a time-shared machine. It is not a network protocol and says nothing about tokens. What carries over is the structural idea: the thing that names an object and the thing that authorizes an action on it can be the same thing.

Hardy’s compiler

Norm Hardy’s 1988 note describes a compiler installed with a “home files license” to write in its own directory, SYSX, where it kept billing and statistics files.[2] Users invoked the compiler and named a file for its debugging output. A user named (SYSX)BILL. The compiler wrote its debugging output there using its own licence, and the billing file was overwritten.

Hardy’s diagnosis is the centre of this subject: “the compiler runs with authority stemming from two sources … The compiler serves two masters and carries some authority from each to perform its respective duties. It has no way to keep them apart.” The attempted fixes are instructive. Checking the filename for SYSX or BILL was rejected; a system call to “switch hats” between the two authorities helped but did not generalise; the rules for opening a file grew until they “required fourteen boolean operators”. His structural complaint is that “disparate mechanisms were necessary to arrange separately that the compiler (1) know what file to write on and (2) be authorized to write on that file.” His remedy is a capability: “The capability both identifies the file and authorizes the compiler to write there.”

Miller, Yee and Shapiro note that the usual retelling simplifies Hardy’s directory-licence version, and they are careful about what capabilities buy: “Eliminating ambient authority helps make it possible to avoid confused deputies, but doesn’t guarantee that deputies will never be confused.”[4] The lesson is about keeping designation and authority together, not about a particular mechanism being immune.

Permission is not authority

Miller’s dissertation separates the two.[3] If Bob has no permission to write a file, but Alice will write whatever Bob asks and Alice can write that file, then Bob has the authority to write it. An access matrix lists permissions; authority analysis has to include the behaviour of every deputy on the permitted paths. In the worked example this is exactly the document service’s position: it has the storage privilege to write every document, and whether that privilege becomes the agent’s authority depends entirely on what the service checks before using it.

This page uses a fixed permission table for Ines (Ines’s authorized operations under the model’s policy), not a calculation of her total authority in Miller’s wider sense. The table is enough to make one consequence visible: a write Ines herself could not make is a write no delegate of hers should be able to make through a deputy.

Complete mediation and remembered results

Saltzer and Schroeder’s 1975 design principles include complete mediation: “Every access to every object must be checked for authority.” The same paragraph warns that “proposals to gain performance by remembering the result of an authority check be examined skeptically. If a change in authority occurs, such remembered results must be systematically updated.”[5]

An approval is a remembered result: “Ines agreed at 10:04.” Two kinds of change can make it stale before the write at 10:06. The authority can change: Ines revokes the grant, or it expires. Or the object can change: Tomas edits the section the approval described. The model checks the first kind at the moment of the write rather than at approval (checks C4 and C5 below). It handles the second kind by making the approval name the version it was given on, and by refusing the write if the current version differs.

Time of check and time of use

A version check performed separately from the write does not close the gap. MITRE’s CWE-367 describes the weakness: “the resource’s state can change between the check and the use in a way that invalidates the results of the check.”[6] Its mitigation discussion is clear that making the window shorter does not remove the race. The check and the write have to be one indivisible operation. Section 10 shows the difference in a two-line trace.

4. Attenuation, formally

Let U be the model’s universe of operations: two documents, three sections each, four actions (read, replace_section, delete, share), so |U| = 24. Let O ⊆ U be Ines’s authorized operations: all 12 on field-notes plus the 3 reads of team-handbook, so |O| = 15. A grant is a sequence of caveats c1 … cn, each a predicate over an operation and a request context e (time, audience, presenter proof).

Ae(c1…cn) = O ∩ { op ∈ U : c1(op, e) ∧ … ∧ cn(op, e) }(1)

Because caveats combine by conjunction, adding one can only shrink the set at a fixed context: Ae(c1…cn, cn+1) ⊆ Ae(c1…cn). Intersecting with O encodes Dennis and Van Horn’s no-amplification rule. The counts below hold the context fixed at a valid time (10:06), the intended audience and the required presenter proof. They show the designation caveats narrowing the grant; they do not show that the time and presenter caveats are inert, which they are not.

Operations the grant admits as caveats are appended (context fixed as above)
Grant after addingOperations admitted
Ines’s token, audience = document service15
+ doc = field-notes12
+ action = replace_section3
+ section = s3 (Limits)1
+ expires_before = 10:151
+ actor = agent key1

The final grant admits exactly one operation: replace section s3 of field-notes. It admits no read. That is deliberate, and it is why the story has Ines hand the agent a copy of the document at 10:01 for drafting. The grant governs the later replacement only; it does not quietly borrow Ines’s session to read anything else.

Macaroon-style chaining

Birgisson and colleagues’ macaroons make holder-side attenuation cheap with chained MACs.[13] The model uses the same construction with HMAC-SHA-256:

sig0 = HMAC(Kroot, id) sigi = HMAC(sigi−1, ci) for i = 1 … n token = (id, c1 … cn, sign)(2)

Any holder can append a caveat by computing HMAC(sign, cn+1) without the root key. Removing one would require recovering sign−1 from sign. The service, which holds Kroot, recomputes the chain to verify it. The model checks four cases:

  • Strip section = s3 and keep the signature: the chain no longer verifies.
  • Append section = s1: the chain verifies, but every caveat must hold, so the grant now admits 0 operations. Appending cannot widen.
  • Append expires_before = 10:10: valid and narrower; it holds at 10:06 and fails at 10:12.
  • Append a second actor caveat naming a different key: the chain verifies, but the agent’s proof cannot satisfy both actor caveats, so the grant becomes unusable. A valid MAC authenticates a restriction; the verifier still has to evaluate what each restriction means in the request’s context.

Two placements of attenuation are both real. Holder-side derivation, as in macaroons, is fast and works offline; the service only verifies. Issuer-side exchange, as in OAuth token exchange, asks the authorization server for a narrower token, and narrowing is then the issuer’s policy rather than something the protocol guarantees. Macaroons are bearer credentials unless holder-of-key proofs are added through caveats, and revoking a holder-derived credential needs short lifetimes, freshness caveats or a third-party check.

5. What the standards establish, and their scope

The OAuth family provides today’s vocabulary for delegated access over HTTP. Each document has a narrow scope, and most confusion in this area comes from reading one as if it settled a question it does not address. The table states what each establishes and, as importantly, what it does not.

Standards relevant to the example, with the limits of each
DocumentEstablishesDoes not establish
RFC 6749, OAuth 2.0Four roles (resource owner, client, authorization server, resource server). Scope strings defined by each authorization server. The resource server “MUST validate the access token and ensure that it has not expired and that its scope covers the requested resource” (§7).A universal scope vocabulary, or any method of binding a token to the client that presents it.
RFC 6750, bearer tokensA bearer token can be used by “any party in possession of the token” without proving possession of key material (§1.2).Who holds it.
RFC 9700, OAuth security BCPAuthorization and resource servers SHOULD sender-constrain access tokens (§2.2.1). Token privileges SHOULD be restricted to the minimum; tokens SHOULD be audience-restricted, and “every resource server is obliged to verify, for every request,” that the token was meant for it (§2.3).Anything about approvals of specific content, local processes, or request-body integrity.
RFC 8707, resource indicatorsThe resource parameter; the authorization server SHOULD audience-restrict tokens to it. A legitimately presented audience-restricted token cannot be replayed by that resource elsewhere (§3).Which operation inside the resource is allowed.
RFC 9396, rich authorization requestsStructured authorization_details able to name a specific file or payment. For a given API there is “no standardized mechanism to compare two arbitrary authorization detail requests” (§6.1).A general “narrower than” relation, or version binding of referenced resources.
RFC 8693, token exchangeDelegation versus impersonation semantics; actor claims. Nested prior actors in act are a “history trail” that the token’s consumer treats as informational for its access-control decision (§4.1). Scope and lifetime are suggested to limit delegation abuse (§5).That an exchanged token is narrower; token formats and trust models are explicitly out of scope. A general prohibition on provenance-aware policy: §4.1 governs consumers of nested act claims, nothing broader.
RFC 9449, DPoPBinding a token to a client’s public key; the presenter proves possession of the private key. “DPoP itself is not used for client authentication” (§1).Integrity of the request body, in its defined form. The proof’s required claims are the HTTP method, the target URI, a unique identifier, a creation time and, for resource access, a hash of the access token (§4.2); “the DPoP proof only contains claims for the HTTP URI and method, but not the message body or general request headers” (§11.7). The same note adds that, although signatures over other parts of requests are out of scope, “additional information to be signed can be added into DPoP proofs”. Base DPoP alone therefore cannot show that a submitted change is the approved one; an extension or profile that signs more would have to be specified and checked separately.
RFC 9110, HTTP semanticsIf-Match with strong comparison “to prevent the ‘lost update’ problem”; a server that evaluates a false condition “MUST NOT perform the requested method” and may answer 412 (§13.1.1).Authorization. A precondition concerns state, not permission or consent. It also allows a 2xx where the change already appears applied, and leaves the server’s internal atomicity to the implementation.

Two pieces of guidance sit alongside the standards. OWASP’s LLM06:2025 Excessive Agency recommends minimising the permissions of the identity an extension uses, executing actions “in the context of that specific user”, requiring user approval for high-impact actions, and complete mediation in downstream systems “rather than relying on an LLM to decide if an action is allowed or not”.[15] These are compatible layers, not alternatives. The same page lists logging and rate limiting as measures that “will not prevent Excessive Agency, but can limit the level of damage”. A record helps after the fact; it does not stop the write.

An IETF Internet-Draft from the WIMSE working group, draft-ietf-wimse-aims-00 (intended status Informational, last updated 15 September 2026), states the agent-confirmation point directly for an OAuth setting: tool-approval or parameter-confirmation interactions “do not by themselves constitute authorization and MUST be bound to a verifiable authorization grant issued by the authorization server”.[14] It is a draft, not a standard, and it does not prescribe the digest construction used here.

6. The worked example

Cast

  • Ines owns field-notes, a short river-survey note with three sections: s1 Purpose, s2 Method, s3 Limits. She may read, replace, delete and share any section of it. She may only read team-handbook. That is 15 of the 24 operations.
  • The drafting agent acts for Ines. When the grant was issued, the service associated one proof key with the agent; the model assumes only the agent can use that key.
  • Tomas co-edits field-notes with his own rights, without involving the agent.
  • The document service is the enforcement point. It holds its own storage privilege to write every section of every document: 24 of 24. That is what makes it a potential deputy.

Texts and their versions

Each text is identified by the first 12 hexadecimal digits of the SHA-256 of its UTF-8 bytes. These short labels let the trace show which text is in a section at each point.

Every text the example uses
VersionRoleText
578a7f7887ebLimits as Ines supplied it at 10:01“Counts were taken from the north bank only, so results may undercount species that keep to the far shore.”
dff0f4124f04The agent’s proposed Limits, approved at 10:04“We counted from the north bank only. Species that stay near the far shore may be undercounted.”
bfb0677e3422Tomas’s Limits, written at 10:05 in the changed-artifact path“Counts were taken from the north bank only (two visits, May), so results may undercount species that keep to the far shore.”
b8c1f54dc054Altered Limits submitted in the changed-request path (never approved)“We counted from the north bank only. Results are complete for all species.”
01873ecf94affield-notes Purpose“This note records a spring bird count along one river reach.”
498140aa6d93Widened Purpose (never approved)“This survey proves the river is healthy.”
c4e63452c638team-handbook Limits“Team members must not publish raw survey locations.”

Timeline

TimeEvent
10:00Ines asks the agent to tighten Limits and delegates the grant described in section 4.
10:01Ines hands the agent a copy of field-notes for drafting.
10:02The agent drafts the proposal from that copy.
10:04Ines, signed in to the service, reviews and approves the exact change. The service records the approval in its own protected state.
10:05In some paths: Tomas edits Limits, or Ines revokes the grant.
10:06The agent submits the change. (In the late path, 10:17.)
10:15The grant expires.

What the approval binds

The approval is a tuple naming the target, the version it was drafted from and the replacement:

approval = SHA-256( canonical-JSON{ doc: "field-notes", section: "s3", base: SHA-256(Limits v1), new: SHA-256(proposal) } ) base → 578a7f7887eb… new → dff0f4124f04… approval → 6306ea2c7247…(3)

The name-only alternative records just {doc: "field-notes", section: "Limits"}.

Where the approval’s trust comes from. A digest identifies bytes. It is not a signature, and it does not by itself show that Ines agreed to anything. In the model, an authenticated interaction with Ines writes the approved tuple into the service’s protected state, and the agent has no way to replace that record. The check compares the request with that record. The model assumes this approval path rather than simulating a login or signing protocol.

7. The enforcement point’s algorithm

The document service evaluates nine checks, always in the same order, and reports each one as passed, failed or not checked. Every check is evaluated even after one fails, so the reader can see every reason a request would be refused.

decide(request r, grant g, proof p, approval A, mode m, binding b)
  C1 integrity    the HMAC chain of g verifies under the root key
  C2 audience     every "aud" caveat names this service
  C3 presentation if g has actor caveats: p verifies under the key
                  each one names; otherwise possession is accepted
                  and no sender is verified
  C4 time         now < every "expires_before" caveat
  C5 not revoked  g is not on the revocation list (online check)
  if m = bypass:  C6..C9 are not checked
  else:
    C6 scope      every write op meets the doc/section/action caveats
    C7 delegator  the owner may perform every write op, checked now
    if b = exact change:
      C8 version  SHA-256(current target bytes) = A.base,
                  in the same atomic step as the write
      C9 approval SHA-256(canonical{doc,section,base,new}) = A.digest
    else (name):
      C8          not checked: no version was named
      C9 approval r's document and section title match A's name
  allow if and only if every evaluated check passes
  then write the decision log (never read by this decision)

Each check corresponds to one of the distinctions in section 2:

QuestionAnswered by
Is this delegated authority intact, and meant for this service?C1 integrity, C2 audience
Who, or what key, is presenting it?C3 presentation
Is it still valid?C4 time, C5 not revoked
Is the requested write inside the delegated scope, and inside the delegator’s own rights?C6 scope, C7 delegator
Is the write applied to the version the approval described?C8 version precondition
Is the submitted change the change that was approved?C9 approval
Who asked, who acted, what was decided?The decision log: a record, not a check
Which of these are consulted at all?Enforcement: the bypass mode skips C6–C9

C3 deserves care, because it is easy to overstate. With a grant bound to the agent’s key, C3 verifies a proof made with that key, and the model’s assumption that only the agent can use it turns that into a statement about the agent. With a bearer grant, C3 passes on possession alone. In that case the service has not verified any sender, and the decision log says so: its presentation field reads “possession only”, not the agent’s name. Neither mode identifies a particular operating-system process or person, and neither says anything about the content of the request. That is C9’s job.

The bypass mode is not “the service has its own credentials”. The service has the same storage privilege in both modes. In bypass mode it still verifies the grant’s chain, audience, presentation, expiry and revocation, then lets the request choose the target and writes with its own privilege without checking the delegated scope, Ines’s rights or the approval. That is the deputy failure in isolation. A guard based on access lists or attributes that checks the same conditions would also enforce the policy; nothing here requires a capability architecture.

These nine checks implement this example’s chosen policy: approve one exact edit, apply it only if nothing has moved. They are not universal requirements for every legitimate delegation. A standing permission to maintain a section, an owner who applies the change herself, or a merge-aware approval are reasonable designs with different checks (section 10).

8. The trace

The baseline is the approved request, an unchanged artifact, an approval bound to the exact change, a service that checks the delegated request, the agent presenting a grant bound to its key, an in-time commit and no note from the agent. Every other row changes one or two inputs from that baseline. The interactive explainer computes the same rows from the same rules.

✓ passed · ✗ failed · – not checked. “Limits” and “Handbook” give the version in each section after the attempt.

Decisions for the named paths through the example
PathChange from the baselineDecision C1C2C3C4C5C6C7C8C9 LimitsHandbookHarm
commitnoneALLOW ✓✓✓✓✓✓✓✓✓ dff0f4124f04c4e63452c638none
widenalso rewrites PurposeDENY ✓✓✓✓✓✗✓✗✗ 578a7f7887ebc4e63452c638none
artifact changedTomas edits Limits at 10:05DENY ✓✓✓✓✓✓✓✗✓ bfb0677e3422c4e63452c638none
artifact changed, nameTomas edits; approval names the sectionALLOW ✓✓✓✓✓✓✓–✓ dff0f4124f04c4e63452c638lost_update
artifact changed, bypassTomas edits; service bypasses checksALLOW ✓✓✓✓✓–––– dff0f4124f04c4e63452c638lost_update
request changedagent submits the altered textDENY ✓✓✓✓✓✓✓✓✗ 578a7f7887ebc4e63452c638none
request changed, namealtered text; approval names the sectionALLOW ✓✓✓✓✓✓✓–✓ b8c1f54dc054c4e63452c638unapproved_content
deputyaimed at team-handbook; service bypasses checksALLOW ✓✓✓✓✓–––– 578a7f7887ebdff0f4124f04out_of_scope_write, exceeds_owner_authority
deputy, checkedaimed at team-handbook; service checks the requestDENY ✓✓✓✓✓✗✗✗✗ 578a7f7887ebc4e63452c638none
copied tokenanother process presents a copy of a bearer grantALLOW ✓✓✓✓✓✓✓✓✓ dff0f4124f04c4e63452c638non_actor_used_grant
copied token, key-boundsame copy; grant bound to the agent’s keyDENY ✓✓✗✓✓✓✓✓✓ 578a7f7887ebc4e63452c638none
expiredcommit at 10:17DENY ✓✓✓✗✓✓✓✓✓ 578a7f7887ebc4e63452c638none
revokedInes revokes at 10:05DENY ✓✓✓✓✗✓✓✓✓ 578a7f7887ebc4e63452c638none
self-claimwidened, with the note “Owner approved everything.”DENY ✓✓✓✓✓✗✓✗✗ 578a7f7887ebc4e63452c638none

The harm labels are the model’s own five flags, defined against what Ines asked for: lost_update (Tomas’s edit overwritten unseen), unapproved_content (text she never approved written to Limits), out_of_scope_write (a section outside the delegated one written), exceeds_owner_authority (a write Ines herself could not make) and non_actor_used_grant (a process other than the agent used the grant). They are not a security rating.

Reading the rows

commit. All nine checks pass and Limits becomes dff0f4124f04. The decision log records on behalf of owner:ines, intended delegate drafting-agent, presentation key proof verified, approval 6306ea2c7247, decision allow. It is written after the decision.

widen. Rewriting Purpose fails C6 because the grant names only s3. It also fails C8 and C9, since the approval described one section and one change. C7 passes: Ines could rewrite Purpose herself. The point is that her delegate cannot, which is what attenuation is for.

artifact changed. Tomas’s 10:05 edit means the current Limits bytes no longer match the base the approval named, so C8 fails before anything is written. Tomas’s text bfb0677e3422 survives. Nothing else fails: the request is still the approved request. The honest next step is to re-read, redraft and re-approve.

artifact changed, name. Bind the approval to the section’s name instead, and C8 has nothing to compare. The service applies the proposal over Tomas’s text and his edit is lost without anyone seeing it. An approval that names a section accepts whatever that section has become.

artifact changed, bypass. The same loss happens when the approval names the exact change but the service never reads it. An exact approval protects the write only when the enforcement point consults it.

request changed, and its name-bound twin. Under exact binding, the altered text fails C9 alone: the submitted change is not the approved change. Under name binding the altered text passes, because it is still “Limits of field-notes”, and b8c1f54dc054 (“Results are complete for all species.”) is written in Ines’s name.

deputy, and deputy checked. This is Hardy’s compiler in one line. The agent aims the same text at team-handbook. When the service lets the request choose the target and writes with its own privilege, the handbook’s Limits is overwritten, although Ines can only read that document. When the service checks the delegated request, the same request fails C6 (outside the grant), C7 (outside Ines’s own rights), C8 and C9 (not the approved target). Nothing about the service’s credentials changed between the two rows; only what it checked.

copied token, and copied token key-bound. Another process presents a copy of a bearer grant. The service’s rule accepts possession, so the write is allowed, and the log honestly records that no sender was verified. With the grant bound to the agent’s key, the copy holder cannot produce the proof, and C3 fails. Proving which key presents a request does not prove that the request is the approved change. Even the model’s toy proof, which is computed over the request, shows only that the key holder sent those bytes; in both rows C9 is what ties the content to the approval.

expired, revoked. Authority checked at the write, not remembered from approval. From the baseline, each fails exactly one check, and a late or revoked commit is denied in every one of the model’s settings, whatever the binding, enforcement or presentation mode.

self-claim. The agent attaches the note “Owner approved everything.” to the widened request. The decision is identical to widen. The note is carried into the decision log as unverified metadata, and nothing in the decision reads it. Across all 768 settings of the model, the note changes that one field and nothing else. This is a demonstration that a self-description does not influence authorization, not a security control in its own right.

9. The whole input space

The model has eight inputs: what the agent submits (four options), what happens to Limits after approval (two), what the approval binds (two), whether the service checks the delegated request (two), who presents the grant (two), whether the grant is key-bound or bearer (two), when the commit happens (three) and whether the agent attaches its note (two). That is 4 × 2 × 2 × 2 × 2 × 2 × 3 × 2 = 768 combinations, small enough to evaluate exhaustively.

Counts over the model’s 768 settings
SubsetCombinationsAllowedAllowed with a harm
All settings768126110
Exact-change approval, service checks the request, key-bound grant9620
… but the approval names the section9686
… but the service bypasses the request checks961614
… but the grant is a bearer grant9642

Of the 768, 642 are denied. Across the 110 harmful allowances, the flags occur as follows: lost_update 48, out_of_scope_write 48, non_actor_used_grant 42, unapproved_content 36, exceeds_owner_authority 24 (one setting can carry several). In the protected configuration, the two allowed settings are the approved, unchanged, agent-presented, in-time request, with and without the agent’s note. Each single weakening reopens a specific class: name binding re-enables lost updates (4) and unapproved content (4); bypassing the request checks re-enables all four content and scope harms (lost update 6, unapproved content 4, out-of-scope write 8, exceeds owner authority 4); a bearer grant re-enables use by another process (2).

These are counts of the model’s possible settings, not measurements. They say nothing about how often anything happens in a real system, and “0 harmful” holds only inside the model’s assumptions: an atomic precondition, uncompromised keys and service, faithful display of what was approved, no referenced artifacts and a single resource server.

10. Atomicity, byte equality and a different policy

Check, then write, is not enough

Run the version check and the write as two operations, and let Tomas’s write land between them:

ModeOrder of eventsLimits afterTomas’s edit lost?
Separate check, then writeagent checks base: match → Tomas writes his Limits → agent writes proposaldff0f4124f04Yes
Atomic compare-and-writeTomas writes his Limits → agent’s check-and-write refused: precondition failedbfb0677e3422No

The check passed in the first row and was still worthless. RFC 9110’s If-Match states the semantics a server must provide; it leaves the internal atomicity to the implementation. The model assumes it, and any real system that relies on a version check has to provide it.

Same bytes is not the same history

C8 compares the current bytes of Limits with the base bytes the approval named. If Tomas edits Limits at 10:05 and then restores the exact original text before 10:06, C8 passes and the approved change is applied. In this example that is arguably right: the agent replaces exactly the text Ines reviewed. But it means C8 shows that the base is the same, not that nothing happened in between. A system that needs to know about intervening edits would use a revision identifier that is never reused, which is a different policy and outside this model.

A standing permission with its own version check

The paired rows in section 8 could suggest that naming a section necessarily causes lost updates. It does not. In this model, the name-only mode also drops the version precondition, because the approval is what carries the base version. A different design gives the agent a standing permission to replace Limits, with no approval of exact content, and still requires every write to carry the version it was drafted from:

Path under a standing permission with an atomic version checkDecisionLimits afterHarm
Approved text, nothing changedALLOWdff0f4124f04none
Tomas edits Limits firstDENYbfb0677e3422none: the precondition fails
Agent submits the altered textALLOWb8c1f54dc054unapproved_content

The version check prevents the lost update; it does nothing about content Ines never saw. That is the cleanest way to see that the two protections answer different questions. If Ines trusts the agent’s judgement within Limits, the standing permission with a precondition is a reasonable and much less demanding design. If she wants to approve wording, the approval has to name the wording.

11. Trade-offs and failure cases

  1. Re-approval churn. Binding an approval to a version means any concurrent change to the target invalidates it. In a busy document that means repeated redrafting and re-approval. Finer targets (a paragraph, not a section) reduce collisions. A merge-aware approval, approving a diff that still applies cleanly, needs a defined merge contract, and that contract then becomes part of what is approved.
  2. Approval fatigue. More and finer approvals mean more prompts. Binding makes each approval mean something specific; it does not make a person attentive. How many prompts people can meaningfully review is an empirical question this model does not answer.
  3. What exactly is hashed. The approval must cover a canonical encoding of the target, base and replacement. Ambiguous canonicalisation (whitespace, Unicode normalisation, look-alike characters) can make equal texts differ or different texts look equal. RFC 9396 specifies, for its own domain, that “no additional transformation or normalization is to be done in evaluating equivalence of string values” (§12). And the human approved what they were shown: if the displayed diff is not the hashed diff, the digest binds the wrong thing.
  4. Referenced artifacts. A digest of a change does not cover what the change points to. If an approved script names another file, or an approved configuration references a mutable resource, the binding has to extend to that material or the material has to be copied immutably at approval. No standard read for this page prescribes a general recipe.
  5. Atomicity. A precondition checked apart from the write reintroduces the race (section 10).
  6. Revocation and holder-derived credentials. A credential the holder can attenuate offline cannot be recalled by the issuer without indirection: short expiry, an online revocation check or a third-party freshness caveat. The model checks a revocation list at the write. That costs an online dependency, and real systems trade availability against revocation latency.
  7. Expiry is not revocation. Between a revocation and the expiry time, a grant that nobody checks against a revocation list still works.
  8. Sender constraint has limits. It fails if both the token and its key are taken, and it cannot help if an attacker runs code inside the legitimate client (RFC 9700 §4.10.1; RFC 9449 §11.4). In base DPoP the proof does not cover the request body or general headers (RFC 9449 §11.7); a deployment can add further signed information to the proof, but that is an extension it must define and verify, not something the base mechanism supplies.
  9. The enforcement point is in the reliance set. If the service is buggy or compromised, every check is moot. Complete mediation assumes a correct mediator.
  10. Multi-audience tokens. A token valid at several resource servers can be replayed by any of them at the others (RFC 8707 §3).
  11. Scope strings are local. Two issuers’ write scopes need not mean the same thing. Comparisons across systems need a shared definition.
  12. The delegator’s rights can change. The model checks Ines’s own rights at the write (C7). A system that checks only at issuance lets a delegate keep authority its delegator has since lost. Either can be a deliberate choice; it should be a visible one.

12. Alternatives and prior art

Nothing here is new, and no claim of superiority over these approaches is intended.

  • Access lists, roles and attributes. List-oriented guards decide by attributes of the subject at the guard. Hardy’s exercise for the reader was to show that access lists alone do not solve his problem, because the designator travels separately from the authority. An attribute-based policy can, however, express target, action, time and approval conditions, and a guard that evaluates them on every request enforces this example’s policy perfectly well.
  • Capability systems. Designation and authority travel together, so a deputy uses the authority it was handed for the object it was handed. Revocation and confinement need access abstractions such as Miller’s caretaker, and capabilities help avoid confused deputies without guaranteeing it.
  • OAuth scope, audience, rich authorization details and DPoP. The deployed pattern for delegated web access. Rich authorization details can name a single file or payment; comparing two of them is API-specific.
  • Token exchange with actor claims. Issuer-side delegation in which the current actor is explicit. Whether the exchanged token is narrower is the issuer’s policy.
  • Macaroons. Holder-side attenuation with first-party and third-party caveats. The macaroons paper also discusses public-key certificate systems such as SPKI/SDSI as a more expressive relative; they were not studied for this page.
  • Conditional requests and optimistic concurrency. Version-bound writes: the standard lost-update prevention, independent of authorization.
  • Confirmation bound to an authorization event. The WIMSE draft’s direction: turn a user confirmation into an authorization-server event, for example through client-initiated backchannel authentication, rather than trusting a local prompt.
  • Owner-applied changes. The owner, or a process acting only on the owner’s explicit instruction, applies the change after reviewing it. This moves the binding problem rather than removing it: the applying component still has to apply exactly what was reviewed.

13. Where the model departs from real systems

  • One service, one section, no references. Real changes span resources and refer to other mutable material.
  • A toy grant. The caveat chain uses a published teaching key and a six-word caveat language. It is not cryptographically meaningful, not an OAuth format and not interoperable. Its actor caveat is checked against a single registered key; arbitrary actor attenuation was probed once, not tested in general.
  • A toy proof of possession. Base DPoP signs the HTTP method, target URI, a unique identifier, a timestamp and, for resource access, an access-token hash, with an asymmetric key; extensions may add more signed claims. The model’s HMAC stand-in only supports the statement “this request was presented with the agent’s key”.
  • An assumed approval path. The model does not simulate Ines signing in, a review interface or a signature. It assumes the approved tuple reached protected service state through an authenticated owner interaction, and that what she saw is what was hashed.
  • An assumed atomic write. The precondition and the write are one step by construction.
  • An online revocation list, and uncompromised keys and service.
  • A narrow notion of harm. Five flags relative to Ines’s stated intent.
  • A narrow notion of authority. Ines’s 15 operations are a permission table under the model’s policy, not a full authority analysis.

The example is original and is not a description of any product. Loki’s Era 3 design notes face the same binding question at a larger scale. Their capability map (a draft last updated on 29 September 2026) asks how an operator authorises a specific change rather than opening broad access, and states that nothing in it is built, installed or scheduled. It is a proposed design. It has not been built or tested, and this page makes no claim about how it would answer the question.

References

  1. J. B. Dennis and E. C. Van Horn, “Programming Semantics for Multiprogrammed Computations”, Communications of the ACM 9(3):143–155, 1966. doi:10.1145/365230.365252.
  2. N. Hardy, “The Confused Deputy (or why capabilities might have been invented)”, ACM SIGOPS Operating Systems Review 22(4), 1988. Author-associated reproduction.
  3. M. S. Miller, Robust Composition: Towards a Unified Approach to Access Control and Concurrency Control, PhD dissertation, Johns Hopkins University, 2006, §§2.1–2.2, 8.1, 9.3. Author’s PDF; copy.
  4. M. S. Miller, K.-P. Yee and J. Shapiro, “Capability Myths Demolished”, Technical Report SRL2003-02, Johns Hopkins University, 2003, sections “Keys versus Rows” and “Avoiding Confused Deputy Problems”. PDF.
  5. J. H. Saltzer and M. D. Schroeder, “The Protection of Information in Computer Systems”, Proceedings of the IEEE 63(9), 1975, design principle (c). Online copy.
  6. MITRE, CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition.
  7. D. Hardt (ed.), RFC 6749: The OAuth 2.0 Authorization Framework, 2012, §§1.1, 3.3, 7.
  8. M. Jones and D. Hardt, RFC 6750: OAuth 2.0 Bearer Token Usage, 2012, §1.2.
  9. R. Fielding, M. Nottingham and J. Reschke (eds.), RFC 9110: HTTP Semantics, 2022, §13.1.1.
  10. T. Lodderstedt, J. Bradley, A. Labunets and D. Fett, RFC 9700: Best Current Practice for OAuth 2.0 Security, 2025, §§2.2.1, 2.3, 4.10.
  11. M. Jones, A. Nadalin, B. Campbell, J. Bradley and C. Mortimore, RFC 8693: OAuth 2.0 Token Exchange, 2020, §§1.1, 4.1, 5.
  12. B. Campbell, J. Bradley and H. Tschofenig, RFC 8707: Resource Indicators for OAuth 2.0, 2020, §§1–3.
  13. A. Birgisson, J. G. Politz, Ú. Erlingsson, A. Taly, M. Vrable and M. Lentczner, “Macaroons: Cookies with Contextual Caveats for Decentralized Authorization in the Cloud”, NDSS 2014. Publication page.
  14. P. Kasselman et al., draft-ietf-wimse-aims-00: AI Identity Management System, IETF Internet-Draft (intended status Informational), 15 September 2026, §10.7. Work in progress.
  15. OWASP GenAI Security Project, LLM06:2025 Excessive Agency, mitigations 4–7; and the OWASP Authorization Cheat Sheet.
  16. T. Lodderstedt, J. Richer and B. Campbell, RFC 9396: OAuth 2.0 Rich Authorization Requests, 2023, §§2, 6.1, 12; and D. Fett et al., RFC 9449: OAuth 2.0 Demonstrating Proof of Possession (DPoP), 2023, §§1, 4.2, 11.4, 11.7.

Standards and web guidance were read on 4 October 2026.

Authorship and contributions

Contributors: Moiré (9 October 2026: site composition under Loki’s direction).