Internet-Draft Agent Delegation Profile September 2026
Hassan Expires 6 March 2027 [Page]
Workgroup:
Web Authorization Protocol
Internet-Draft:
draft-hamr-oauth-agent-delegation-01
Published:
Intended Status:
Standards Track
Expires:
Author:
A. Hassan
Independent

An Attenuated Delegation Profile for Automated Agents

Abstract

This document specifies a profile for delegating authorization to automated agents across administrative domains. It defines an HTTP header field, Agent-Delegation, that carries a chain of attenuated delegation links. Each link narrows the scope, tightens or holds a set of floor conditions, and shortens or holds the expiry of its parent. A verifier checks every link in the chain, not only the last, and rejects the chain if any link violates attenuation. The profile is deliberately agnostic to the credential format and to the nature of the entity that issues floor attestations; it specifies required properties, not a specific encoding or a specific kind of issuer. It composes with, and does not replace, existing work on agent credential provisioning and posture.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 6 March 2027.

Table of Contents

1. Introduction

A person asks an automated agent to arrange a car rental. That agent does not do the work itself: it engages a second, specialized agent operated by a different company, and that second agent calls the rental service's API. The rental service now receives a request from software that no human has ever spoken to, two hops removed from the person who authorized anything, and operated by an organization the person has no relationship with.

The rental service needs three things it cannot currently get: proof that the authority behind the request actually came from that person; assurance that nothing along the way granted itself more than it was given; and the ability to check both without calling back to any of the parties involved.

Automated agents increasingly act as principals against network and application APIs on behalf of a human or an organization. Where such an agent's authorization is delegated from a human principal, and potentially re-delegated agent-to-agent across administrative domains, three properties are needed that existing OAuth mechanisms do not by themselves guarantee end to end: that each re-delegation can only narrow what came before it, that a verifier can check the entire chain rather than trusting the last hop, and that the conditions under which a delegation was granted travel with it in a form a verifier can check offline.

This document specifies a profile that supplies those three properties. It does not define a new credential format, a new signature scheme, or a new agent identity system. It defines:

an HTTP header field for carrying a delegation chain (Section 5); a closed set of attenuation rules that every link in the chain must satisfy relative to its parent (Section 8); the required properties of a floor attestation, independent of its encoding (Section 12); an ordered verification procedure a verifier MUST follow (Section 14); and a profile of the RFC 9421 signature parameters (Section 6).

The profile is a composition layer. It assumes an underlying attestation issuer exists somewhere in the deployment and that a trust source can resolve that issuer's key; it says nothing about who or what that issuer is. Appendix A gives one concrete, non-normative instantiation to make the abstract properties concrete for a reader evaluating deployability; nothing in that appendix is required to implement this profile.

2. Conventions and Terminology

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

This document defines the following terms:

Attestation Issuer:
An entity that can produce a signed, boolean, expiring statement about whether a subject satisfies a floor condition, without disclosing the underlying value that produced the answer. This document treats the Attestation Issuer abstractly; it may be any party a deployment trusts to make such a statement.
Delegator:
The principal that holds a delegation and grants a narrower one to the next link in the chain. The first Delegator in a chain is the human or organizational principal that originates the delegation.
Delegate:
The principal that receives a delegation link, whether it is an automated agent or a sub-agent.
Relying Service:
The service that receives a request bearing a delegation chain and must decide whether to honor it.
Resource Owner:
The party that owns the resource named by a request's target origin and that publishes and signs the declared menu (Section 10.4) governing that origin's classSource declared value. A deployment commonly has the Resource Owner and the Relying Service be the same operator, but this document does not require it; the two are distinct roles, and only the Resource Owner's signing key and the menu's origin binding are constrained by this document, not the roles' organizational identity.
Scope:
The set of capabilities a link grants, expressed as a set of opaque strings. See Section 7.
Floor:
A named set of conditions a subject must satisfy, expressed as axis/value pairs, that a Relying Service or an intermediate Delegate requires before it will act. See Section 9. Each axis/value pair within that set is a constraint; a floor is the aggregate of the constraints it carries, at most one per axis.
Link:
One element of a delegation chain: a scope, a floor, an expiry, and the signature that binds them to their issuer and to their parent link.

3. Motivation

A delegation chain that is only checked at its endpoint is not a chain; it is a single trust decision with extra steps. If an intermediate Delegate can widen scope, loosen a floor, or extend an expiry beyond what it was itself granted, then the guarantees made at the root of the chain do not survive to the Relying Service, regardless of how carefully the root delegation was constructed. This document's primary technical contribution is to make that failure structurally difficult: attenuation is defined precisely enough that a verifier can check it mechanically at every link, and Section 19 states plainly that a verifier which checks only the final link defeats the entire mechanism.

A second, related problem is that authorization conditions are often expressed as raw attribute values (a date of birth, a tenure in days, an account balance) which a Relying Service does not need and should not receive. This document requires that floor conditions be evaluated by the Attestation Issuer and exposed to the chain only as a signed boolean result bound to a verifier's nonce and to an expiry, never as the underlying value. This is a disclosure-minimization requirement independent of the chaining requirement, and both apply together.

4. Position Among Delegation Layers

This section is informational. It introduces no new requirement beyond those stated elsewhere in this document; it exists to place this document among neighboring work so a reader does not mistake the boundary this document draws for an oversight.

Delegated agent authorization, read across the drafts active in this space, separates into three distinct layers:

(a) Who may act on whose behalf:
the delegation chain and the floor conditions that bound it, as it narrows from an originating principal down to the agent making a request.
(b) What exactly may be done, per invocation:
binding a specific action, tool name, or argument set to a grant, at the moment the action is about to occur.
(c) What happened, once:
the receipts and audit evidence that record that a given action was authorized, presented, and consumed.

This document occupies layer (a), plus floors. The chain model of Section 8 and the floor axes of Section 9, including the extensible registry of Section 9.1, bound the space of what an agent could invoke coarsely: an axis such as actionClass or writeBudget constrains a category of action or a cumulative budget across an entire chain, not the arguments of any single call. A floor is evaluated once, when a link is issued or verified, and it does not change per invocation.

This document does not perform per-call argument binding, and it does not produce a receipt. [RFC9396] defines Rich Authorization Requests, which bind authorization detail at grant time. [I-D.das-agentic-tool-binding] binds a tool name and its arguments per call, at the moment a request is dispatched, after a model has already chosen what to invoke. [I-D.schrock-ep-authorization-receipts] addresses layer (c), the recorded evidence that an action occurred. Read together, the ordering runs: authorization detail is bound at grant time; floors of the kind this document specifies bound the space in between as a chain is attenuated hop to hop; and a per-call digest is bound after a specific choice has been made, with a receipt recording what happened afterward.

One structural problem is shared across this layering and is not solved by any one layer alone: a parent credential remains valid after it has been attenuated into a narrower child, so a holder of the parent retains the broader authority the child was only ever a narrowing of. This problem sits in the composition boundary between layers, not inside any single token profile. [I-D.asor-wimse-agent-delegation-chain] Section 9.1 lists mitigations for it (a short token lifetime, holder binding, and status lists). This document shares the same problem and offers no token-profile fix for it either; a deployment that needs to close it needs a mechanism this document does not supply.

Implementation experience that informed where this document draws its boundary is summarized, non-normatively, in Appendix A.

6. Profile of RFC 9421 Signature Parameters

RFC 9421 [RFC9421] Section 2.3 defines a set of signature parameters, including created, expires, nonce, alg, keyid, and tag. This section profiles the use of those parameters for a signature applied over a request carrying an Agent-Delegation field; it does not define a new signature mechanism.

A sender applying this profile MUST include the keyid, alg, created, expires, and nonce signature parameters, and MUST include the Agent-Delegation field among the signature's covered components.

A sender MUST set the tag signature parameter to the string "agent-delegation", so a verifier can identify signatures made under this profile. RFC 9421 defines tag as "an application-specific tag for the signature as a String value" used "to help identify signatures relevant for specific applications or protocols," and tag values are application-specific strings requiring no registry. RFC 9421 Section 7.2.7 discusses the risk of tag collision between unrelated applications; this profile does not add any mechanism beyond that discussion.

RFC 9421 defines the expires parameter, "expiration time as a UNIX timestamp value of type Integer," but leaves its enforcement to the application. This profile therefore requires it: a verifier MUST reject a request whose signature expires value is earlier than the time of verification. This is a requirement this profile adds; RFC 9421 itself does not impose it.

RFC 9421 Section 7.2.2 describes the nonce parameter, "a random unique value generated for this signature as a String value," as allowing a verifier to detect replay of the signature itself, and describes a signer providing a creation timestamp and an expiration time to limit the utility of a captured signature value. It further states that a verifier wanting to trigger a new signature from a signer can send the Accept-Signature header field with a new nonce parameter, and that an attacker simply replaying a signature would not be able to generate a new signature with the chosen nonce value. A verifier under this profile that needs a fresh signature bound to a value of its own choosing SHOULD use the Accept-Signature header field carrying a nonce parameter, as described in RFC 9421 Section 7.2.2. This profile does not define a separate nonce-delivery mechanism, because RFC 9421 already provides one.

RFC 9421's nonce signature parameter binds one HTTP message. The nonce carried inside a floor attestation (Section 12) binds one attestation to one verifier's question. These are two distinct nonces at two distinct layers, and implementers MUST NOT conflate them.

RFC 9421 defines message-level signing and provides no notion of delegated authority, sub-principals, or attenuation of scope. The chain model of Section 8 therefore extends RFC 9421 rather than restating it.

7. Scope

A scope is a set of zero or more capability strings. A capability string is OPAQUE to this profile: this document defines no syntax, no internal structure, and no namespace for it. Any structure a deployment gives it, for example a resource:action convention, is a deployment convention and MUST NOT be interpreted by a verifier implementing this profile.

Comparison is by exact set containment over case-sensitive, octet-for-octet string equality. A scope S1 is contained in a scope S2 if and only if every member of S1 is octet-for-octet identical to some member of S2.

A verifier MUST NOT apply any of the following when comparing scopes, because none is defined by this profile:

The presented bytes are compared as presented. A verifier that applies a matching rule this profile does not define can accept a chain another conforming verifier rejects, which makes attenuation unverifiable in exactly the way this profile exists to prevent.

The empty set is a valid scope and is contained in every scope; a link granting the empty scope grants no capability and is not, by itself, an error.

Wildcard, hierarchical, and pattern-based scope semantics are OUT OF SCOPE for this version of the profile and are future work; they are not defined here.

8. Attenuation Rules

A delegation chain is an ordered sequence of links L(0), L(1), ..., L(n), where L(0) is the root delegation and L(n) is the link presented to the Relying Service. For every link L(i) with i > 0, relative to its parent L(i-1), all of the following MUST hold:

  1. Scope containment. The scope granted by L(i) MUST be a subset of the scope granted by L(i-1), per Section 7. L(i) MUST NOT name a capability that L(i-1) did not itself hold.

  2. Floor non-relaxation. The floor carried by L(i) MUST be at least as tight as the floor carried by L(i-1) on every axis defined in Section 9. L(i) MUST NOT be looser than L(i-1) on any axis, whether by omitting an axis L(i-1) constrained, by widening an ordered axis's value, or by substituting a different value on an equality axis that L(i-1) did not itself permit.

  3. Expiry non-extension. The expiry carried by L(i) MUST NOT be later than the expiry carried by L(i-1).

These three rules apply to every link in the chain, not only to the leaf link. A verifier MUST validate rules 1 through 3 for every adjacent pair (L(i-1), L(i)) for i = 1 to n, in addition to validating L(0) against the root Attestation Issuer's published floor and the Delegator's own authority. A verifier MUST reject the entire chain if any single link violates any single rule; a partial acceptance of a chain up to the point of violation is not conforming.

A verifier MUST support a configurable maximum chain depth and MUST reject a chain whose length exceeds that configured maximum, prior to evaluating any link's content. This bounds the verification cost of a chain and limits the depth to which authorization can be re-delegated across administrative domains in a single deployment.

Widening a scope, a floor, or an expiry beyond what a chain currently carries is out of scope for this profile. Where a deployment needs to widen an existing grant, that MUST be performed as a distinct, explicit, consent-visible operation outside this delegation mechanism, producing a new root delegation rather than a modification of an existing chain.

9. Floor Axes

A floor is a set of axis/value pairs. This document defines a closed set of axes for this version of the profile. Extending the axis set beyond this closed set is no longer future work as of this revision: Section 9.1 defines a registry mechanism for it. The closed set defined directly by this document is:

Table 1
Axis Type Comparison
subjectClass enum one_of (singleton)
tenureMin duration min
credentialAgeMin duration min
accountClass enum one_of (singleton)
partialPolicy enum one_of (singleton)

An axis name that does not appear in this table MUST cause a verifier to reject the floor, and therefore the link that carries it, outright. An unknown or misspelled axis name MUST be treated as a hard rejection; it MUST NOT be silently ignored or dropped from the comparison. This applies equally to a published floor and to a requested floor presented in a delegation link.

For an enum-typed axis (subjectClass, accountClass, partialPolicy), the comparison is equality only; these axes are unordered and a value either matches the required value or it does not. For a duration-typed axis (tenureMin, credentialAgeMin), a larger value is strictly tighter than a smaller one.

9.1. Floor Axis Registry

This document establishes a registry of floor axis names, the "HAMR Floor Axis Registry" (Section 20). What is registered is the axis NAME; a registration does not define new comparator semantics of its own. Each registered name is bound to exactly one comparator type, drawn from the following closed set of four comparators:

min:
an ordered value. A min constraint present on the parent MUST be present on the child, with the child's value greater than or equal to the parent's value: child.min >= parent.min. This is the comparator already used, without this name, by every duration-typed axis in this section's closed set, per Section 9.3.
max:
an ordered value. A max constraint present on the parent MUST be present on the child, with the child's value less than or equal to the parent's value: child.max <= parent.max.
rank:
a position in a registered, totally ordered value list. A rank constraint present on the parent MUST be present on the child, with the child's rank less than or equal to the parent's rank: child.rank <= parent.rank. Tightening on a rank axis therefore means lowering the rank, consistently with how this document's proof-of-concept treats an ordered axis whose values are r, w, and x with r < w < x, where a child link may only lower the value, never raise it.
one_of:
a set of values. A one_of constraint present on the parent MUST be present on the child, with the child's set a non-empty subset of the parent's set.

For every comparator in the set above, the constraint is non-droppable in the child: where the parent carries a constraint on a registered axis, the child MUST also carry a constraint on that axis, per the same non-relaxation-by- omission rule Section 8 already states for rule 2. An axis name that is not registered, or a registration that names a comparator outside the closed set of four above, MUST be rejected by a verifier. This extends, to comparators, the same hard-rejection rule this section already states for an unregistered axis name: neither an unknown axis name nor an unknown comparator is ever silently ignored or dropped from the comparison.

A registration in the HAMR Floor Axis Registry carries the following five fields:

  1. Name. The axis name, an opaque registered string.

  2. Comparator. Exactly one of min, max, rank, or one_of, as defined above.

  3. Value domain. The set or type of values the axis may carry (for example, the duration grammar of Section 9.2, a non-negative integer, or a registered ordered value list).

  4. Tightening direction. Which change to a value on this axis counts as tightening, per the comparator's definition above.

  5. Omitted-axis rule. Two distinct cases apply, and this field states both rather than one. First, link-to-link within a chain: where the parent link L(i-1) carries a constraint on a registered axis and the child link L(i) omits it, that omission is non-relaxation-by-omission and the chain MUST be rejected, per rule 2 of Section 8. Second, link-to-published-floor: where no link in the chain up to and including the link under evaluation carries a constraint on a registered axis, the issuer's published floor for that axis applies unchanged. effective names the verifier's evaluated outcome for an axis under this second case; how, or whether, a verifier conveys that outcome to a caller is out of scope for this document. The second case is inheritance, not relaxation, and it is not a restatement of the first.

The initial contents of the registry, established by this document, are:

Table 2
Name Comparator Value domain Notes
subjectClass one_of exactly one value from the closed set Section 9 already defines for it, encoded as the bare value (a singleton set in one_of terms) tightening from a singleton is identity only; behaviour identical to -00's equality-only rule
tenureMin min duration, Section 9.2 larger is tighter
credentialAgeMin min duration, Section 9.2 larger is tighter
accountClass one_of same singleton rule as subjectClass same note
partialPolicy one_of same singleton rule as subjectClass same note
actionClass rank ordered set r < w < x semantics specified in Section 10
classSource rank ordered set method < declared semantics specified in Section 10
writeBudget max non-negative integer, cumulative per chain semantics specified in Section 11

For the three enum-typed axes (subjectClass, accountClass, partialPolicy), a multi-valued (set) encoding is out of scope for this revision; registering them as one_of records the comparator their equality-only rule already is, and changes nothing on the wire.

The semantics of writeBudget, including the distinction between the limit carried in the chain and the count a verifier holds, and the derivation of the chain identifier that count is keyed by, are specified in Section 11. The semantics of actionClass and classSource, and the method by which each is assigned to a request, are specified in Section 10.

9.2. Duration Grammar

A duration-typed axis value MUST match exactly one of the two forms P<n>D or P<n>Y, where <n> is a non-negative integer. The integer <n> MUST be in the range 0 to 2^53 - 1 inclusive, and a value outside that range MUST be rejected. No other ISO 8601 duration form is accepted. In particular, a duration expressed in months MUST be rejected, because a month is a calendar-dependent unit and its length in days is ambiguous; accepting it would make the tightening comparison in Section 9.3 ill-defined. For the purpose of comparison, 1Y is defined as exactly 365D. A value that falls outside the permitted range, or that does not match either form, MUST be rejected.

9.3. Monotone Tightening

On every ordered (duration-typed) axis, a requested floor value MUST be greater than or equal to the corresponding published or parent floor value. If it is not, the request MUST be refused. Widening a floor value on an ordered axis MUST NOT occur implicitly as a side effect of any operation defined by this profile. As stated in Section 8, any widening is a distinct, explicit, consent-visible operation outside this profile's scope.

10. Action Class Floors

This section specifies the semantics of the actionClass and classSource axes registered in Section 9.1. It answers two separate questions: what is the maximum class of operation a delegation link permits (actionClass), and where a verifier is permitted to obtain a given request's class from (classSource).

10.1. actionClass

actionClass is an ordered enumeration with three values, r, w, and x, ranked r < w < x per the rank comparator of Section 9.1. A link's actionClass value states the maximum class of operation the agent presenting that link MAY perform; it is not a statement about any single operation, but a ceiling on the whole link. The three values are:

r:
a read-only operation: one that does not modify state.
w:
an idempotent write: an operation that modifies state but whose repetition has the same effect as performing it once.
x:
a consequential, non-idempotent action: an operation whose repetition is not guaranteed to have the same effect as performing it once.

Per the rank comparator, a child link's actionClass rank MUST be less than or equal to its parent's: a child link MUST NOT carry a higher actionClass value than its parent, and tightening on this axis means lowering the value (x to w, w to r, or x to r).

10.2. classSource

classSource is an ordered enumeration with two values, ranked method < declared per the rank comparator of Section 9.1, naming where a verifier is permitted to obtain a request's actionClass. Where no link in the chain constrains classSource, an omitting link's default value is method, per the link-to-published-floor case of the omitted-axis rule of Section 9.1; method is also the lower-ranked, tighter value of the two, so the default and the tightest value coincide. Where a parent link does constrain classSource, a child link's omission of it is instead the link-to-link case of that same rule, and the chain MUST be rejected rather than defaulted.

method:
the verifier classifies a request from its HTTP method alone, per Section 10.3, and does not consult a menu.
declared:
the verifier additionally consults the Resource Owner's signed menu (Section 10.4) and, where that menu verifies and names the operation, uses the menu's declared value in place of the method default.

method never extends trust to a value asserted by a party other than the verifier itself, while declared extends that trust to whatever the Resource Owner's menu asserts; method is therefore the tighter of the two values, and is ranked lower per Section 9.1's ordered set method < declared. Per the rank comparator, a child link's classSource rank MUST be less than or equal to its parent's: a parent that carries method MUST NOT be followed by a child that carries declared, and the only permitted change on this axis is from declared down to method. This is the same comparator, applied in the same direction, as the actionClass axis of Section 10.1: on both rank axes, tightening lowers the value, with no per-axis exception.

The proof-of-concept accompanying this document names these same two values in the opposite written order internally (declared before method in a source-code table), with identical admission semantics to this section; a reader comparing the code against this text should not read that ordering as a disagreement about which value is tighter.

10.3. Classification

Where a request's classSource is method, its actionClass is the default associated with its HTTP method: GET, HEAD, and OPTIONS default to r; PUT and DELETE default to w; POST and PATCH default to x. This default tracks the safe and idempotent method properties defined by [RFC9110]: GET, HEAD, and OPTIONS are safe methods, and PUT and DELETE are idempotent methods, in the terms [RFC9110] uses; POST and PATCH are neither.

Where a request's classSource is declared, its actionClass is the value the Resource Owner's menu declares for that operation, provided the menu verifies per Section 10.4 and names the operation. On any failure of the menu: the menu is absent, its signature does not verify, the verifier has no key for the claimed owner, or the menu's issuer does not match the request's target as required by Section 10.4; the verifier MUST fall back to the method default given above. A verifier MUST NOT compute the greater of, or otherwise combine, the method default and a declared value; where a valid, matching menu entry exists, the declared value alone governs, replacing the method default rather than being compared against it.

The absence of any menu at all is a defined state, not a fault condition distinct from the failures listed above: it is handled identically to a menu that fails to verify, and the method default applies. A verifier MUST NOT construct or synthesize a menu entry, or a menu, on behalf of a resource owner that has not published one.

10.4. The Declared Menu

A declared menu is a JSON Web Signature (JWS) [RFC7515] whose payload is a JSON object with two members:

iss:
a string carrying the origin of the resource the menu applies to, serialized per [RFC6454].
menu:
a JSON object mapping an operation key to an actionClass value (r, w, or x). An operation key has the form "METHOD path-template", an HTTP method in upper case, one space, and a path using OpenAPI path-template syntax, in which a brace-delimited segment such as {callId} stands for exactly one path segment supplied at request time.

A verifier matches a concrete request against the operation keys of a menu by the following rule, applied deterministically and without exception: the request's method matches the key's method exactly; the request's path and the key's path-template are split into slash-separated segments and are considered a match only where both have the same number of segments and, for every segment position, either the template segment is a literal that equals the request's segment exactly, or the template segment is a brace-delimited parameter that matches any single non-empty request segment. Where more than one operation key matches the same request under this rule, the key with the greater number of literal (non-parameter) segments governs. A menu MUST NOT contain two operation keys under the same method whose templates both match the same concrete request path with an equal number of literal segments; a verifier that encounters such a tie MUST treat the lookup as a miss rather than choosing between the tied keys by any other means, such as the order the keys happen to appear in the menu's JSON serialization. Where no operation key matches, including where the only matches available are tied in this way, the menu is treated, for that request, the same as a menu that does not name the operation, per Section 10.3, and the method default applies.

A menu applies only to the resource that issued it. The menu's iss value is compared, octet-for-octet after serialization per [RFC6454], against the origin of the request target; where the two do not match, the menu fails, exactly as if its signature had not verified, and the verifier MUST fall back to the method default. A verifier MUST NOT accept a menu whose iss value does not match the request target's origin, regardless of whether the menu's signature is otherwise valid.

A menu MUST be verified against the Resource Owner's public key, obtained out of band or from a configured trust source; a verifier MUST NOT obtain that key from the menu itself, following the same rule Section 14 already states for the root attestation's key. The menu's signature algorithm is EdDSA, using the Ed25519 curve; this document defines no algorithm negotiation or agility for the menu, and a menu using any other algorithm does not verify.

10.6. Verifier Placement

Classification and admission under this section are performed by the verifier at the resource or credential boundary described in Section 4, not by any harness or orchestration layer upstream of that boundary. A harness that coordinates an agent's actions does not itself classify a request or admit it against an actionClass floor; an agent that declines to attempt an action it believes will be refused does not, by that restraint, satisfy this section. Admission is an act of the verifier refusing or permitting a request at the boundary, not an act of the harness or the agent abiding by a floor it was merely told about.

10.7. Limits

The method default of Section 10.3 is a reliable floor in one direction and a lossy signal in the other: a GET is never admitted as a write or a consequential action, but a POST is not always a consequential action either. In a survey of the CAMARA API catalogue conducted 2026-09-01 (292 operations across 60 repositories, recorded in this repository's own proof-of-concept data), 57 of 138 POST operations are named as reads (retrieve-, check-, verify-, or status-prefixed operation identifiers) and are classed x under classSource method, notwithstanding their read-shaped names.

The same survey's judgement of GET operations, by contrast, was templated rather than read per operation, so the absence of any GET classed x in that survey rests on a general design convention rather than on an individual reading of each GET operation; and the cost of the 57 read-named POST operations above defaulting to x was not itself reviewed for whether a Resource Owner would actually want to declare them down to r. Neither limitation is corrected by this document; a deployment relying on the survey's numbers should treat them as directional, not as an audited count.

11. Write Budget

This section specifies the semantics of the writeBudget axis registered in Section 9.1. It answers three separate questions: what value a link carries (Section 11.1); what a verifier holds as state separately from that value, and how a request is admitted against the two together (Section 11.2); and how a verifier derives the identifier that state is keyed by (Section 11.3).

11.1. writeBudget

writeBudget is a non-negative integer, compared with the max comparator of Section 9.1: it is the total number of w-class or x-class operations, per the actionClass values of Section 10.1, that the chain carrying it may have admitted in aggregate. A child link MUST NOT carry a writeBudget value greater than its parent's; per the max comparator, tightening on this axis means lowering the value, and a child that raises it violates rule 2 of Section 8.

Where no link in the chain constrains writeBudget, an omitting link inherits the issuer's published floor for writeBudget, which is zero unless the issuer's published floor for the chain states otherwise; this is the link-to-published-floor case of the omitted-axis rule of Section 9.1, and omission is therefore never a widening, only ever an inheritance of whatever floor already applies. This is the same effective-zero outcome the proof-of-concept's own handling of an omitted writeBudget already produces. Where a parent link does constrain writeBudget, a child link's omission of it is instead the link-to-link case of that same rule, and the chain MUST be rejected rather than defaulted, per rule 2 of Section 8.

11.2. Limit and Count

Two distinct quantities are in play, and this document uses "limit" and "count" to keep them apart. The LIMIT is the writeBudget value carried in the chain, per Section 11.1: it rides on the wire, on every link that carries the axis, and a verifier reads it the same way it reads any other floor value. The COUNT is the number already spent against that limit for a given chain; it is state the verifier holds, keyed by the chain identifier of Section 11.3, and it is never carried in a link. A link MUST NOT be interpreted as carrying the count, and a verifier MUST NOT treat any field of a link as the count merely because it varies between requests.

Admission proceeds as follows. A request classed r, per Section 10.3, never spends against writeBudget: it is admitted or refused on the actionClass rule of Section 10.1 alone, and the count is left unchanged. A request classed w or x is admitted only if the remaining count for the chain is at least one, and the count MUST be decremented as part of the same admission decision that admits the request, with no window in which two concurrent requests against the same chain can both observe the same remaining count before either decrement is applied; this is a requirement on the outcome, not on any particular locking or storage mechanism a verifier uses to achieve it. Where the remaining count is zero, a request classed w or x MUST be rejected, with the same uniform rejection outcome Section 14 requires for any other failed check; the verifier does not disclose the remaining count in the rejection. Section 12.2 is why this count can only ever live in verifier-held state: anything carried on the wire can be re-presented, and a count that could be re-presented is not a count.

11.3. Chain Identifier

The chain identifier that keys the count of Section 11.2 MUST be derived by the verifier itself from the chain being verified, and MUST NOT be taken from a value the request supplies. A verifier that accepts a caller-supplied chain identifier does not conform to this section, regardless of what other checks it performs.

The chain identifier is the SHA-256 [RFC6234] digest of the octet string that is the signature value of L(0), the root link of the chain, in whatever encoding that signature value is carried on the wire by the credential format in use, per Section 5 and Section 12. This document defines no signature encoding of its own and needs none for this purpose: the digest is computed over the same exact bytes a verifier already extracts, and has already verified, as L(0)'s signature under item 1 of Section 12. This document does not otherwise use a hash function; SHA-256 is adopted here as the digest for this one purpose.

Every link in a chain, from L(0) to L(n), shares the single chain identifier derived from L(0)'s signature. A re-delegation, which produces a new child link L(i) with its own signature, does not change L(0) and therefore does not change the chain identifier; re-delegation MUST NOT mint a fresh budget, and a verifier MUST NOT derive a chain identifier from any link other than L(0).

This rule is what case 22 of the proof-of-concept accompanying this document, recorded in Section 18, now exercises: the code derives the chain identifier itself and pins that a caller-supplied identifier field has no effect, rather than refilling the budget when one is presented with the same chain.

11.4. Limits

The count of Section 11.2 is held per verifier, not shared across verifiers. Two verifiers holding no shared state do not share a count, so a single chain presented in parallel to N distinct verifiers can spend up to N times its writeBudget limit in aggregate, one full budget at each verifier, before any single verifier's own count reaches zero. A mechanism for verifiers to share a count is out of scope for this document; this limitation is stated here rather than hidden.

The budget is cumulative per chain, counted across every admitted w-class or x-class request regardless of which operation each request invokes, and is deliberately not allocated per action or per operation: a per-action cap is defeated by splitting one action into many smaller ones, each within its own separate cap, so this document defines only one running count per chain.

This document makes no claim about how many real transactions a given writeBudget value can accommodate. Economic scarcity is not measured here, and no writeBudget size is described in this document as practical, sufficient, or adequate for any deployment; a deployment choosing a value does so without guidance from this document beyond the mechanism itself.

12. Attestation Properties

This document does not mandate a specific credential format for a floor attestation. A JSON Web Token, a CBOR Object Signing and Encryption (COSE) structure, or another signed data structure MAY be used, provided the resulting object satisfies every property in this section. A conforming attestation, regardless of its encoding, MUST:

  1. be signed by the Attestation Issuer over the exact bytes presented to the verifier. A verifier MUST verify the signature over those exact bytes and MUST NOT re-serialize the object before or in place of that verification;

  2. carry the verifier's nonce, echoed back exactly as the verifier supplied it;

  3. carry an expiry. A missing expiry, or an expiry that is not a well-formed numeric timestamp, MUST be treated by the verifier as already expired; it MUST NOT be treated as "no deadline";

  4. carry a boolean result of the floor evaluation and MUST NOT carry the underlying value that produced that result. An attestation that discloses a raw date of birth, a raw tenure count, a raw account balance, or any other unrounded attribute in place of, or in addition to, the boolean result does not conform to this profile;

  5. be rejected by the verifier if its serialization contains a duplicate top-level key. A parser that resolves duplicate keys by a first-wins or last-wins rule is exposed to an ambiguity where the issuer and the verifier can disagree about which value was signed; this profile closes that ambiguity by requiring detection and rejection rather than a resolution rule.

12.1. Signed Refusal

Where an Attestation Issuer declines to attest that a subject meets a floor, it MUST return a signed refusal rather than silence, an unsigned error, or a bare protocol-level failure. A signed refusal MUST itself be signed by the Attestation Issuer, MUST carry the same nonce the verifier supplied for the corresponding request, and MUST carry an expiry, using the same rules as an accepted attestation. This prevents a refusal from being forged in the Attestation Issuer's name and prevents a genuine refusal from being stripped from the response without detection.

A verifier MUST verify a signed refusal along a verification path independent of the path used to verify an accepted attestation, so that a defect in one path does not silently admit a forged instance of the other.

12.2. Nonce Binding Is Not Replay Protection

Echoing the verifier's nonce in a signed attestation or refusal binds that response to the specific request that carried the nonce; it does not, by itself, prevent that same response from being presented again. The same response, re-presented against the same expected nonce, verifies again for as long as the response's own expiry has not passed. Preventing such a replay (for example, by tracking nonces already consumed within their validity window) is a separate responsibility that falls to the verifier and is outside the scope of this document. This document does not claim that nonce binding, by itself, provides replay protection, and implementers MUST NOT assume that it does.

13. Agent Identifier

A Delegate that presents a delegation chain is identified to a Relying Service by an identifier whose only required property, in this profile, is: the identifier MUST be unlinkable across distinct Relying Services and MUST remain stable across separate interactions with the same Relying Service. This document does not specify how such an identifier is derived, what inputs contribute to it, its length, or its encoding; those are deployment choices left to the entity that issues or constructs the identifier.

Appendix B records that this unlinkability requirement pulls in a different direction from a stability requirement found in related work, and treats that tension as an open, unresolved item rather than as settled by this document.

14. Verification Procedure

A verifier processing a request that carries an Agent-Delegation field MUST perform the following steps, in the order given. A failure at any step MUST produce one uniform rejection outcome, indistinguishable from the outcome of a failure at any other step, so that the specific reason for rejection does not become available to an unauthenticated party as an oracle (see Section 19).

  1. Parse the Agent-Delegation Structured Field List into its ordered member links.

  2. Compare the number of links against the verifier's configured maximum chain depth; reject if the chain exceeds it.

  3. Verify the RFC 9421 HTTP Message Signature applied by the immediate sender over the request, including the Agent-Delegation field among its covered components, per the profile of Section 6.

  4. Verify each link's own signature, per Section 12, over its exact presented bytes.

  5. Verify the root attestation, L(0), against the Attestation Issuer's public key, where that key is obtained from a configured trust source and MUST NOT be obtained from the message itself.

  6. Verify the three attenuation rules of Section 8 for every adjacent pair of links in the chain.

  7. Verify that every floor attestation carried by the chain echoes, exactly, the nonce the verifier issued for this exchange, and reject if any does not.

  8. Verify that no link in the chain has an expiry that has passed, evaluated at the time of verification.

  9. Classify the request's actionClass per Section 10.3, consulting the declared menu of Section 10.4 where the chain's classSource is declared, and admit or refuse the request against the writeBudget ledger of Section 11; per Section 10.6, this step is performed by the verifier at the resource or credential boundary, not by an upstream harness or orchestration layer.

  10. Only after every preceding step has succeeded, apply the Relying Service's authorization policy to the resulting, fully verified chain.

A verifier that implements only a subset of these steps, or that implements them out of order such that a later step's success can mask an earlier step's failure, does not conform to this profile.

15. A Worked Example

This section is non-normative. It illustrates the rules of the preceding sections using a single chain that is built and then verified; where it appears to conflict with a normative section, the normative section governs.

Recall the scenario from Section 1: a person asks an automated agent to arrange a car rental. The parties in this example are the person, who is the root Delegator; Agent A, a general assistant; Agent B, a specialized booking agent operated by a different company; and the rental service, which is both the Relying Service and the verifier.

Link L(0) is the root delegation, created on the person's device:

Table 3
Field Value
scope booking:read, booking:create, payment:authorize
floor: subjectClass interactive
floor: tenureMin P2Y
floor: credentialAgeMin P90D
expiry 2026-11-01T12:00:00Z

Link L(1) is Agent A's re-delegation to Agent B:

Table 4
Field Value
scope booking:read, booking:create
floor: subjectClass interactive
floor: tenureMin P2Y
floor: credentialAgeMin P180D
expiry 2026-11-01T11:00:00Z

Agent A drops payment:authorize from the scope it grants to Agent B. Dropping a capability is always permitted, because rule 1 of Section 8 requires only that the child's scope be a subset of the parent's.

On the wire, Agent B presents both links to the rental service as an Agent-Delegation Structured Field List:

Agent-Delegation: :BASE64-ENCODED-L0:, :BASE64-ENCODED-L1:

Each member of the list is a Byte Sequence per RFC 8941 [RFC8941]. The base64 content is elided here for readability; the decoded fields are shown in the tables above.

The expiry values in the tables above are written in a human-readable form for readability only. The on-the-wire encoding of a link is determined by the credential format in use, as stated in Section 5, and this document does not mandate one. A link's expiry is distinct from the expiry of a floor attestation carried by the chain (Section 12), and both are distinct from the RFC 9421 expires signature parameter profiled in Section 6, which covers the HTTP message rather than the delegation.

The rental service verifies the chain following Section 14. For the adjacent pair (L(0), L(1)), the three attenuation rules of Section 8 are checked as follows:

Every link in the chain verifies, so the chain is accepted, and the rental service applies its own authorization policy to a scope of booking:read and booking:create only. It will not honor a payment request on this chain, because no link in it carries that capability.

Had L(1) instead carried credentialAgeMin="P30D", that value would be looser than the parent's P90D, and rule 2 would require the verifier to reject the entire chain.

16. Privacy Considerations

This profile is built around boolean-only disclosure: a floor attestation states whether a subject meets a condition and MUST NOT carry the underlying value. This bound holds throughout the chain; an intermediate Delegate that re-derives or forwards a raw value defeats the disclosure minimization this profile exists to provide, even where every attenuation rule in Section 8 is otherwise satisfied.

The agent identifier requirement in Section 13 requires unlinkability across Relying Services specifically so that two Relying Services receiving requests from the same underlying agent cannot, from the identifier alone, determine that the requests originated from the same principal.

This profile does not eliminate the Attestation Issuer's own visibility into the queries it answers. The Attestation Issuer necessarily observes that some verifier asked whether some subject meets some floor, even though the verifier itself learns only a boolean result. Where the query pattern itself is sensitive, this residual, issuer-side query log is a real and unsolved limitation of this profile, not an oversight to be corrected by better protocol design; deployments that need to eliminate it need a mechanism outside this document's scope.

Because the Agent-Delegation field is visible to any intermediary able to read HTTP headers, and this document defines no confidentiality mechanism for it, links in the chain MUST NOT carry personal data belonging to the Delegator, any intermediate Delegate, or any subject the floor condition concerns.

18. Implementation Status

This section records the status of a known implementation of this profile, in accordance with [RFC7942]. The information provided is believed to be accurate at the time of writing, has not been independently verified, and does not constitute an endorsement of this profile by any party. The provided implementation is a single-author, open-source proof-of-concept, written in zero-dependency Node.js and available at https://github.com/hamr0/justabit. It has not undergone external review and has not been adopted by any organization; no Attestation Issuer has reviewed or validated this profile.

The following elements of this profile are implemented and exercised by automated tests in that proof-of-concept:

The following elements of this profile are NOT implemented in the proof-of-concept, and this is stated here without qualification:

The catalogue survey behind the honest-limit paragraph of Section 10.7 is included in this same repository, alongside its underlying data.

19. Security Considerations

Lazy verification is the primary risk this profile is designed against. A verifier that checks only the final link of a delegation chain, rather than every link per Section 14, defeats the entire mechanism: an intermediate Delegate can present a leaf link that is internally well-formed while the chain that produced it silently widened scope, loosened a floor, or extended an expiry at an earlier hop. Conformance to this profile requires whole-chain verification; there is no partial-conformance mode that checks only the leaf.

Nonce echo does not prevent replay. As stated in Section 12.2, a verifier that treats nonce presence as sufficient replay protection can be presented with the same attestation more than once within its expiry window. A conforming deployment needs its own replay defense, such as tracking consumed nonces within their validity window; this document does not supply that defense.

Cost-based floors price resets; they do not create uniqueness. A floor expressed in terms of tenure or credential age raises the cost of obtaining a fresh credential that satisfies it, but it does not establish that the underlying subscription or account is held by a unique human, or that one subject cannot back multiple agents. A deployment that needs a uniqueness guarantee needs a mechanism this profile does not provide.

This profile defines no revocation mechanism. Once an attestation or a delegation link is issued, this profile provides no way to invalidate it before its stated expiry. The only mitigation this profile offers is short expiry; a deployment's exposure window is bounded by how short its issuers set expiries, not by any revocation capability.

Key distribution is a centralization point. Verification of the root attestation depends on a trust source that resolves an Attestation Issuer's public key, per step 5 of Section 14. That trust source is, by construction, a point every verifier in a deployment must trust and a point whose compromise or unavailability affects every verification that depends on it.

Rejection MUST be uniform. As stated in Section 14, a verifier MUST NOT return different, distinguishable outcomes for different verification failures (an expired link versus a bad signature versus a loosened floor versus a depth overflow). A verifier that leaks which step failed hands an unauthenticated party an oracle it can use to probe the boundaries of a valid chain without ever constructing one.

An Attestation Issuer's query log is a residual disclosure channel even though the verifier learns only a boolean. This is restated here as a security consideration, not only a privacy one, because a party able to observe or compel disclosure of that query log gains information the wire protocol itself was designed to withhold.

An unforgeable chain identifier is what makes a write budget a cap rather than a suggestion. The derivation of Section 11.3, from L(0)'s own signature rather than from any value the request supplies, is what prevents the presenter of a chain from choosing its own identifier and thereby choosing its own remaining count. A verifier that accepts a presenter-supplied chain identifier converts the hard cap of Section 11 into something the presenter can reset at will, simply by supplying a different value on the next request; Appendix C's V11 exists to catch exactly that failure. Per-verifier counting, by contrast, per Section 11.4, is a stated limitation of this document, not a defect to be concealed: a deployment that needs a single shared count across verifiers needs a mechanism this document does not supply.

A declared menu is an owner-signed downgrade surface. classSource declared lets the Resource Owner's menu lower a request's actionClass below its method default, for example declaring a POST as r. The origin-binding rule and the out-of-band key requirement of Section 10.4 are what keep a third party from supplying that downgrade for a resource it does not own: without both checks, a verifier that resolves one key for more than one Resource Owner could be handed a validly-signed menu for the wrong resource and apply its declared classes to a request it was never meant to cover. A verifier that accepts a menu without checking both the signature and the origin match does not conform to this document.

20. IANA Considerations

This document registers the following entry in the "Hypertext Transfer Protocol (HTTP) Field Name Registry" defined by [RFC9110]:

Table 5
Field Name Status Structured Type Reference
Agent-Delegation permanent List This document, Section 5

This document requests IANA create the "HAMR Floor Axis Registry", referenced from Section 9.1. Registration policy for this registry is Specification Required [RFC8126]. The registration template is the five fields defined in Section 9.1: Name, Comparator, Value Domain, Tightening Direction, and Omitted-Axis Rule. The Comparator field of a registration MUST be exactly one of min, max, rank, or one_of; a registration naming any other comparator is not valid. The initial contents of the registry are the eight entries given in the table in Section 9.1: subjectClass, tenureMin, credentialAgeMin, accountClass, partialPolicy, actionClass, classSource, and writeBudget.

No other IANA actions are required by this document.

21. Normative References

[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/info/rfc2119>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/info/rfc8174>.
[RFC8941]
Nottingham, M. and P. Kamp, "Structured Field Values for HTTP", RFC 8941, DOI 10.17487/RFC8941, , <https://www.rfc-editor.org/info/rfc8941>.
[RFC9421]
Backman, A., Ed., Richer, J., Ed., and M. Sporny, "HTTP Message Signatures", RFC 9421, DOI 10.17487/RFC9421, , <https://www.rfc-editor.org/info/rfc9421>.
[RFC9110]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP Semantics", STD 97, RFC 9110, DOI 10.17487/RFC9110, , <https://www.rfc-editor.org/info/rfc9110>.
[RFC8126]
Cotton, M., Leiba, B., and T. Narten, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 8126, DOI 10.17487/RFC8126, , <https://www.rfc-editor.org/info/rfc8126>.
[RFC7515]
Jones, M., Bradley, J., and N. Sakimura, "JSON Web Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, , <https://www.rfc-editor.org/info/rfc7515>.
[RFC6454]
Barth, A., "The Web Origin Concept", RFC 6454, DOI 10.17487/RFC6454, , <https://www.rfc-editor.org/info/rfc6454>.
[RFC6234]
Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)", RFC 6234, DOI 10.17487/RFC6234, , <https://www.rfc-editor.org/info/rfc6234>.

22. Informative References

[RFC7942]
Sheffer, Y. and A. Farrel, "Improving Awareness of Running Code: The Implementation Status Section", BCP 205, RFC 7942, DOI 10.17487/RFC7942, , <https://www.rfc-editor.org/info/rfc7942>.
[RFC9396]
Lodderstedt, T., Richer, J., and B. Campbell, "OAuth 2.0 Rich Authorization Requests", RFC 9396, DOI 10.17487/RFC9396, , <https://www.rfc-editor.org/info/rfc9396>.
[I-D.das-agentic-tool-binding]
Das, S., "tool_use Is Not invoke(): Binding Execution-Finality to Agentic Tool-Call Interfaces and MCP", Work in Progress, Internet-Draft, draft-das-agentic-tool-binding-02, , <https://datatracker.ietf.org/doc/html/draft-das-agentic-tool-binding-02>.
[I-D.schrock-ep-authorization-receipts]
Schrock, I., "Authorization Receipts for High-Risk Agent Actions", Work in Progress, Internet-Draft, draft-schrock-ep-authorization-receipts-12, , <https://datatracker.ietf.org/doc/html/draft-schrock-ep-authorization-receipts-12>.
[I-D.klrc-aiagent-auth]
Kasselman, P., Lombardo, J., Rosomakho, Y., Campbell, B., Steele, N., and A. Parecki, "AI Agent Authentication and Authorization", Work in Progress, Internet-Draft, draft-klrc-aiagent-auth-03, , <https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/>.
[I-D.asor-wimse-agent-delegation-chain]
Asor, R., "Verifiable Attenuated Delegation for AI Agent Chains", Work in Progress, Internet-Draft, draft-asor-wimse-agent-delegation-chain-00, , <https://datatracker.ietf.org/doc/html/draft-asor-wimse-agent-delegation-chain-00>.
[I-D.reece-wimse-cross-org-delegation]
Reece, M., "Cross-Organizational Delegation for Workload and Agent Identity: Problem Statement and Requirements", Work in Progress, Internet-Draft, draft-reece-wimse-cross-org-delegation-02, , <https://datatracker.ietf.org/doc/draft-reece-wimse-cross-org-delegation/>.
[I-D.sweeney-wimse-credential-delegation]
Sweeney, K., "Credential Delegation Protocol for AI Agents in Multi-System Environments", Work in Progress, Internet-Draft, draft-sweeney-wimse-credential-delegation-00, , <https://datatracker.ietf.org/doc/draft-sweeney-wimse-credential-delegation/>.
[CAMARA-PROPOSAL]
Hassan, A., "CarrierAttestation: An API Proposal for Attested Windowed Disclosure", , <https://github.com/camaraproject/APIBacklog/issues/330>.
[OAUTH-CHARTER]
IETF, "Web Authorization Protocol (oauth) Working Group Charter", , <https://datatracker.ietf.org/wg/oauth/about/>.
[I-D.google-cfrg-libzk]
Frigo, M. and A. shelat, "Longfellow ZK", Work in Progress, Internet-Draft, draft-google-cfrg-libzk-02, , <https://datatracker.ietf.org/doc/draft-google-cfrg-libzk/>.
[ISO18013-5]
International Organization for Standardization, "ISO/IEC 18013-5: Personal identification - ISO-compliant driving licence - Part 5: Mobile driving licence (mDL) application", , <https://www.iso.org/standard/69084.html>.
[ICAO9303]
International Civil Aviation Organization, "ICAO Doc 9303, Machine Readable Travel Documents, Eighth Edition", .

Appendix A. Non-Normative Instantiations

This appendix is entirely non-normative. It describes concrete ways to satisfy the abstract requirements of the body of this document, to help a reader evaluate the profile's deployability. Nothing in this appendix constrains a conforming implementation; a deployment satisfying the normative sections above by any other means is equally conforming.

Three directions are sketched below. They differ in what makes the underlying credential scarce; none is required, preferred, or assumed by this profile.

Direction 1: subscription attestation. A mobile network operator can act as the Attestation Issuer described in Section 12. In such an instantiation, a subscriber identity module credential grounds the Delegator's identity, and the operator attests floor conditions about the line associated with that credential without disclosing the line's underlying attributes. The floor axes named abstractly in Section 9 map, in this instantiation, to concrete network-observable attributes as follows: subjectClass maps to the line's service type (for example, voice-and-data versus machine-to-machine); tenureMin maps to the line's subscription tenure; credentialAgeMin maps to the elapsed time since the credential (for example, the SIM) was last swapped; accountClass maps to a billing relationship category (for example, postpaid versus prepaid); partialPolicy maps to how the operator's attestation service handles a query for which it holds incomplete data. This direction's scarcity is economic: obtaining many qualifying credentials costs money over time. Economic cost does not establish uniqueness. A companion document, [CAMARA-PROPOSAL], is an open, unreviewed API proposal that proposes a horizontal profile to a telecommunications standards body along these lines, independently of this document and without either document depending on the other's adoption; nothing about it has been approved, accepted, endorsed, reviewed, or adopted.

Direction 2: a government-issued document credential proved in zero knowledge. A credential in the ISO/IEC 18013-5 [ISO18013-5] mobile document (mdoc) format, held in a wallet, can be used to prove a predicate over the credential in zero knowledge, rather than to disclose the credential's contents. The verifier learns one bit and learns nothing else, and separate presentations are unlinkable. This direction's scarcity is cryptographic: it is rooted in a document issued by a government authority rather than in a purchasable subscription. Work on this class of scheme is in progress in the IETF; see [I-D.google-cfrg-libzk]. This appendix does not state that any particular implementation of this direction exists or has been demonstrated.

Direction 3: a document chip signature with accompanying evidence. A holder's device can read the signature on a government-issued document's chip and verify it against the issuing authority's public-key infrastructure, a process known as passive authentication and specified in ICAO Doc 9303 [ICAO9303]. This establishes that the document is genuine and unaltered. On its own, the chip-signature check says nothing about who presented the document or about the software that performed the read, so deployments of this class pair it with additional evidence, and what that evidence is varies: an attestation from the device platform that unmodified software performed the read; a signature from a party the verifier trusts, vouching for the value reported; a zero-knowledge proof of the predicate itself; or no accompanying evidence at all.

The trust model differs by evidence type, and it matters which one a deployment picks. Where the accompanying evidence is an attestation, the verifier does not check a proof; it checks that trustworthy code ran, and then relies on the value that code reported, so breaking the attestation collapses the claim. Where the accompanying evidence is a zero-knowledge proof, the verifier checks the proof itself, and breaking a platform attestation buys an attacker nothing, because the mathematics still has to hold. Where there is no accompanying evidence, the claim rests on the chip signature alone. A deployment describing this direction needs to state which evidence it uses, because the strength of the claim is not a property of the document root. One limit does not change regardless of which evidence is paired with it: passive authentication proves the document is genuine and unaltered; it does NOT bind the presenter to the document. This direction therefore bounds the number of identities a holder can obtain to the number of documents held; it does NOT establish one identity per human, and must not be described as doing so.

All three directions satisfy the same abstract Attestation Issuer role defined in the body of this document; they differ in what makes a credential scarce. A deployment may use any of them, or another mechanism entirely. This profile does not require, prefer, or depend on any of them.

Direction 4: harness-side experience. These are three open-source, single-author experiments, available at https://github.com/hamr0/bareguard, https://github.com/hamr0/bareloop, and https://github.com/hamr0/bareagent, that exercise the boundary drawn in Section 4; they are not implementations of this profile, which is why they are not listed in Section 18. Non-normative, like the rest of this appendix.

bareguard is one chokepoint between an agent and the world: every action passes through a single gate and comes back allow, deny, or ask a human.

bareloop has an agent author its own workflow scaffolding as a constrained, validated config, with every run executing under an outer gate the agent cannot reach or argue with.

bareagent is the think, act, observe loop: a goal goes in and coordinated actions come out.

In each case the point of control sat at the credential boundary or the resource, never in the harness, which is the placement Section 4 assumes.

Appendix B. Open Reconciliation Item: Identifier Stability Versus Unlinkability

This appendix records an unresolved tension rather than a solution. [I-D.klrc-aiagent-auth]'s Section 6 wants an agent identifier that is stable for the lifetime of a workload identity, so that audit records can be correlated over time. This document's Section 13 wants an identifier that is unlinkable across distinct Relying Services, so that no two Relying Services can correlate requests from the same underlying agent by identifier alone.

These two properties are in tension: an identifier stable enough to support long-lived audit correlation at one Relying Service is, by that same stability, a stronger correlation handle if it is reused, or is derivable in a reused form, across Relying Services. A per-Relying-Service unlinkable identifier, taken to its logical conclusion, complicates the kind of long-lived, cross-request audit trail Section 6 of that draft wants.

This document does not resolve this tension. It is recorded here as an open item for the Working Group's consideration, not presented as solved by either document.

Appendix C. Test Vectors

This appendix is non-normative. The vectors below are an aid to implementers checking their own understanding of the attenuation rules in Section 8 against a common set of examples; they are not an additional requirement, and they do not modify or extend the normative requirements stated in the body of this document. Where a vector appears to conflict with a normative section, the normative section governs.

Each vector is a delegation chain: an ordered sequence of links, each carrying a scope (Section 7), a floor, and an expiry, together with the outcome a conforming verifier is expected to reach and, where that outcome is rejection, which rule of Section 8 the chain violates. An implementation checking itself against this appendix is expected to reproduce every expected outcome listed below, using the scope and floor values shown.

In every vector, subjectClass is interactive on every link, and accountClass and partialPolicy are not exercised and are omitted from every link; a floor is a set of axis/value pairs and is not required to name every axis in Section 9. Only scope, tenureMin, credentialAgeMin, and expiry vary below, and those are what the tables show.

Table 6
Link Scope
V1 L(0) booking:read, booking:create, payment:authorize
V1 L(1) booking:read, booking:create
V2 L(0) booking:read
V2 L(1) booking:read, booking:create
V3 L(0) booking:read
V3 L(1) booking:read
V4 L(0) booking:read
V4 L(1) booking:read
V5 L(0) booking:read
V5 L(1) booking:read
V6 L(0) booking:read
V6 L(1) booking:read, booking:create
V6 L(2) booking:read

The floor and expiry for the same links follow.

Table 7
Link tenureMin credentialAgeMin Expiry
V1 L(0) P1Y P90D 2026-12-01T00:00:00Z
V1 L(1) P1Y P180D 2026-11-30T00:00:00Z
V2 L(0) P1Y P90D 2026-12-01T00:00:00Z
V2 L(1) P1Y P90D 2026-11-30T00:00:00Z
V3 L(0) P2Y P180D 2026-12-01T00:00:00Z
V3 L(1) P1Y P180D 2026-11-30T00:00:00Z
V4 L(0) P2Y P180D 2026-12-01T00:00:00Z
V4 L(1) P2Y (omitted) 2026-11-30T00:00:00Z
V5 L(0) P1Y P90D 2026-11-01T12:00:00Z
V5 L(1) P1Y P90D 2026-11-01T13:00:00Z
V6 L(0) P2Y P180D 2026-12-01T00:00:00Z
V6 L(1) P2Y P180D 2026-11-30T00:00:00Z
V6 L(2) P2Y P180D 2026-11-29T00:00:00Z
Table 8
Vector Expected outcome Rule violated
V1 accept n/a - chain tightens correctly on all three rules
V2 reject Rule 1 (scope containment): L(1) names booking:create, which L(0) did not hold
V3 reject Rule 2 (floor non-relaxation): L(1)'s tenureMin of P1Y is looser than L(0)'s P2Y
V4 reject Rule 2 (floor non-relaxation): L(1) omits credentialAgeMin, an axis L(0) constrained
V5 reject Rule 3 (expiry non-extension): L(1)'s expiry is later than L(0)'s
V6 reject Rule 1 (scope containment), violated at the interior pair (L(0), L(1)); the pair (L(1), L(2)) is correctly attenuated

V4 is a distinct and easily missed failure mode from V3: a floor can be relaxed either by widening a value already present on an axis, as in V3, or by dropping the axis from the link entirely, as in V4. Rule 2 in Section 8 treats both as the same violation, and an implementation that checks only for a looser value on an axis both links carry, without also checking for an axis the parent link carried and the child link dropped, does not detect V4.

V6 is the negative control. The violation is not at the leaf link: the pair (L(1), L(2)) attenuates correctly by every rule, and only the interior pair (L(0), L(1)) violates rule 1. A verifier that checks only the final link, or only the pair adjacent to the leaf, accepts this chain; such a verifier does not conform to Section 14, which requires every adjacent pair in the chain to be checked. This vector exists specifically to detect that failure.

Vectors V7 through V9 below extend this appendix to the actionClass and classSource axes of Section 10. In all three, subjectClass is interactive, and scope, tenureMin, and credentialAgeMin are omitted and not exercised, following the same convention stated above for V1 through V6. V7 is a two-link delegation chain, checked the same way as V1 through V6. V8 and V9 each involve a single link and one incoming request, together with, where noted, one menu; they exercise Section 10.3 and Section 10.4 rather than the three attenuation rules, so no parent link is shown for them.

Table 9
Link actionClass classSource
V7 L(0) x method
V7 L(1) w method
V8 L(0) r declared
V9 L(0) r declared

V8 and V9 each present a request "POST /check" against a link whose actionClass is r. Both are accompanied by a menu, signed by the Resource Owner's key, declaring "POST /check" as r. In V8 the menu's iss value is the origin of the request target, https://api.example.com, matching it exactly. In V9 the menu carries the same declared entry and the same valid signature, but its iss value is https://attacker.example.com, an origin different from the request target.

Table 10
Vector Expected outcome Basis
V7 accept; effective actionClass is w n/a - actionClass lowered from x to w, the direction Section 10.1 permits
V8 the request classifies as r; admitted against the actionClass r floor Section 10.4: the menu verifies and its iss matches the request target, so the declared value governs
V9 the request classifies as x, exceeding the actionClass r floor; refused Section 10.4: the menu's iss does not match the request target, so the menu fails and the method default for POST, x, applies instead of the menu's declared r

V9 is the negative control for the origin-binding rule of Section 10.4. A verifier that checks the menu's signature but skips the comparison between iss and the request target's origin reaches the same outcome as V8 - it accepts the declared value r - and so admits a request that a verifier applying the origin-binding rule correctly refuses. The two vectors present an identical menu and an identical requested actionClass floor and differ only in the menu's iss value, so any difference in outcome between an implementation and this table is attributable to that one comparison.

More generally, a suite composed only of chains expected to be accepted cannot distinguish a verifier that correctly implements the rules of Section 8 from one that accepts every chain unconditionally; both pass such a suite identically. At least one vector expected to be rejected is therefore required for a suite to have any diagnostic value at all, and V6 in particular is required to detect a verifier that checks only the leaf of the chain.

Vectors V10 and V11 below extend this appendix to the writeBudget axis of Section 11. Both present the same single-link chain, L(0) with actionClass x and writeBudget 2, against a series of x-class requests; the chain identifier is whatever Section 11.3 derives from L(0)'s signature, and is the same value across every request in both vectors, since both present the same L(0).

Table 11
Step Request Vector Expected outcome Remaining count after step
1 x-class request 1 V10 admitted 1
2 x-class request 2 V10 admitted 0
3 x-class request 3 V10 refused, budget exhausted 0
4 x-class request 4, presented with a different, caller-supplied chain identifier value V11 refused, budget exhausted 0

V10 is the positive case: two successive x-class requests against the chain are admitted, dropping the remaining count from 2 to 1 to 0, and a third is refused because the remaining count is already 0, per the admission rule of Section 11.2.

V11 is the negative control. It re-presents the identical chain used in V10, after its budget is already exhausted, but the fourth request carries a different chain identifier value of the caller's own choosing rather than one the verifier derived from L(0). Because Section 11.3 requires the verifier to derive the chain identifier itself and forbids taking one from the request, the count is not refilled and step 4 is refused on the same exhausted count as step 3. A verifier that instead took the chain identifier from the request would treat the fourth request as belonging to a fresh chain with a full budget of 2, and would wrongly admit it; V11 exists specifically to detect that failure.

These vectors are illustrative and not exhaustive. Passing them does not establish conformance to this document as a whole: V1 through V6 cover the three attenuation rules of Section 8, V7 through V9 cover the actionClass tightening rule, the declared-value substitution rule, and the menu origin-binding rule of Section 10, and V10 through V11 cover the writeBudget admission rule and the chain-identifier-derivation rule of Section 11; together they do not exercise signature verification of a delegation link itself, nonce binding, expiry evaluation against wall-clock time, or the header field encoding described in Section 5 and Section 6. These vectors have not been executed against any implementation by anyone, including the author; they were derived by hand from the rules in Section 8, Section 10, and Section 11.

Appendix D. Changes since -00

This section is non-normative and is expected to be removed before this document leaves individual submission status.

Author's Address

Amr Hassan
Independent
San Francisco, California
United States of America