<?xml version="1.0" encoding="utf-8"?>
<?xml-model href="rfc7991bis.rnc"?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     docName="draft-saha-aadp-bound-permit-00"
     category="std"
     ipr="trust200902"
     submissionType="IETF"
     tocInclude="true"
     sortRefs="true"
     symRefs="true"
     version="3">

  <front>
    <title abbrev="AADP Bound Permits">Action-Bound Permits for the Agent Action Decision Protocol (AADP): Carrying a Per-Action Decision Across a Trust Boundary</title>
    <seriesInfo name="Internet-Draft" value="draft-saha-aadp-bound-permit-00"/>
    <author fullname="Shamik Saha" initials="S." surname="Saha">
      <organization>Independent</organization>
      <address>
        <postal>
          <city>Amsterdam</city>
          <country>Netherlands</country>
        </postal>
        <email>shamik.saha.rcciit@gmail.com</email>
      </address>
    </author>
    <date year="2026"/>
    <area>sec</area>
    <keyword>AI agents</keyword>
    <keyword>authorization</keyword>
    <keyword>proof of possession</keyword>
    <keyword>HTTP message signatures</keyword>

    <abstract>
      <t>The Agent Action Decision Protocol (AADP) decides, per action,
      whether an agent's concrete request may proceed now, and assumes that
      the decision is consumed by an enforcement point on the same secured
      channel that issued it. AADP identifies, as a planned extension, a
      permit that must be honoured across a trust boundary, and defines the
      "present_bound" obligation as its hook. This document specifies that
      extension. An action-bound permit is a permit signed by the decision
      point, bound to one recipient, one presenter key, one HTTP request and
      one decided action instance, and short-lived. It composes with
      mandate formats such as the Agent Authorization Envelope, which carry
      what a recipient can evaluate for itself; the bound permit carries a
      decision the recipient cannot recompute, because it depends on state
      held by the issuer: cumulative budgets, live reservations, approval
      lifecycle and escalation. The document adds scoped trust in issuers,
      a declared currentness mode, a mandate reference, a verification
      order with registered refusal reasons, and a confirmation the
      recipient signs. It builds on existing specifications for every
      mechanism it can, and defines no new signature format, token format
      or policy language.</t>
    </abstract>
  </front>

  <middle>

    <section anchor="intro">
      <name>Introduction</name>

      <section anchor="gap">
        <name>What Already Exists, and What Does Not</name>
        <t>A consequential request from an agent that crosses into another
        organization's system raises three questions, and existing work
        answers two of them.</t>
        <table anchor="questions">
          <name>Three Questions at a Trust Boundary</name>
          <thead>
            <tr><th>Question</th><th>Answered by</th></tr>
          </thead>
          <tbody>
            <tr><td>Who is acting, and what may it reach?</td>
                <td>Workload identity and access tokens (for example SPIFFE, mutual TLS, OAuth 2.0)</td></tr>
            <tr><td>What has the agent been mandated to do, within what constraints, until when?</td>
                <td>Mandate formats such as <xref target="I-D.kroehl-agentic-trust-aae"/>, which the relying party evaluates itself</td></tr>
            <tr><td>Was this exact request decided, under the sender's current authorization state, and is what arrived the request that was decided?</td>
                <td>Inside one domain, <xref target="AADP"/>. Across a boundary, nothing.</td></tr>
          </tbody>
        </table>
        <t>The third question is the gap. A mandate evaluation of the kind AAE
        specifies is stateless by design: AAE lists state carried across
        presentations and cumulative budgets as future work. An AADP
        evaluation is stateful by design: a verdict depends on cumulative
        spend, reservations, approvals that expire, prior executions and a
        kill switch. A recipient in another domain holds none of that state
        and cannot recompute the decision. It can only verify that a decision
        was made, by whom, about what, and that the request it received is
        the one decided.</t>
        <t>That is all a bound permit does. It is an envelope for one
        decision, not a system for cross-domain authorization, and
        <xref target="notscope"/> says what it leaves to others.</t>
      </section>

      <section anchor="composition">
        <name>The Composition Rule</name>
        <t>A recipient that supports this profile acts on a request only when
        all three of the following hold:</t>
        <ol>
          <li>The decision verifies: a bound permit from an issuer the
          recipient trusts for this class of action (<xref target="scope"/>)
          verifies, and binds this request (<xref target="permit"/> and
          <xref target="binding"/>).</li>
          <li>The mandate holds, where one is referenced: if the permit
          carries a mandate reference (<xref target="mandate"/>), the
          recipient evaluates that mandate against the same request, and its
          verdict permits the action.</li>
          <li>The recipient's own policy permits.</li>
        </ol>
        <t>This is a conjunction. Each layer can refuse; none can overrule
        another. The issuer's decision never widens what the mandate allows,
        and the mandate never substitutes for a decision the issuer did not
        make.</t>
      </section>

      <section anchor="notscope">
        <name>Not in Scope</name>
        <ul>
          <li>Establishing trust between organizations: discovery, vetting,
          onboarding and federation. This document assumes a recipient has a
          configured table of issuers (<xref target="scope"/>) and says
          nothing about how it was built; <xref target="OIDFED"/> and
          <xref target="SPIFFE"/> address this.</li>
          <li>Mandates and their delegation. This document references a
          mandate; it does not define one.</li>
          <li>A policy language, for the same reason <xref target="AADP"/>
          declines to define one.</li>
          <li>Exactly-once external effect. As in <xref target="AADP"/>
          Section 7, a protocol cannot guarantee it; the recipient's
          idempotency store is what collapses retries
          (<xref target="idem"/>).</li>
          <li>Multi-hop chains of per-action decisions. This version refuses
          them (<xref target="hops"/>).</li>
          <li>Prompt injection. A bound permit faithfully carries a decision
          that policy should not have made. Its claim is narrower: a decided
          request cannot be altered, redirected, reused or presented by
          another party without detection.</li>
        </ul>
      </section>

      <section anchor="reqlang">
        <name>Requirements Language and Terminology</name>
        <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>",
        "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
        NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>",
        "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
        "<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document
        are to be interpreted as described in BCP 14
        <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only
        when, they appear in all capitals, as shown here.</t>
        <dl>
          <dt>Issuer:</dt><dd>the AADP Policy Decision Point (PDP), or a
          signing service acting for it, that signs the bound permit.</dd>
          <dt>Presenter:</dt><dd>the AADP Policy Enforcement Point (PEP) that
          sends the request and holds the key named in the permit.</dd>
          <dt>Recipient:</dt><dd>the party that receives the request and
          performs the effect. Its enforcement point is the Recipient
          Enforcement Point (REP).</dd>
          <dt>Action object (A):</dt><dd>the JSON object, derived from the
          request by the rules of its action type, that the decision was
          taken about (<xref target="actiondigest"/>).</dd>
          <dt>Bound permit:</dt><dd>the action-bound permit of
          <xref target="AADP"/> Section 1.2, in the form defined in
          <xref target="permit"/>.</dd>
        </dl>
      </section>
    </section>

    <section anchor="flow">
      <name>Roles and Flow</name>
      <figure anchor="fig-flow">
        <name>Bound Permit Flow</name>
        <artwork type="ascii-art"><![CDATA[
 Agent --proposal--> PEP --decide--> PDP (issuer)
                      |  <--permit + bound permit--+
                      |
                      |  request + Content-Digest + Signature
                      |  (key in cnf) + bound permit
                      |  + Idempotency-Key
                      v
          +--------- trust boundary ----------+
                      v
                     REP -- verify permit, binding, scope,
                      |     currentness
                      |  -- evaluate referenced mandate (if any)
                      |  -- apply local policy
                      |  -- consume (iss, jti) atomically
                      v
                  Recipient system -- effect
                      |
                      v
          signed recipient confirmation --> PEP --report--> PDP
]]></artwork>
      </figure>
      <t>The PEP never signs the decision and the PDP never signs the
      request. Each signature says one thing: the issuer's says this was
      decided; the presenter's says this is the request, and I am the party
      the decision named. A compromised PEP can still substitute a request,
      but not silently: the substituted request will not match the decided
      action, and the mismatch lands in the recipient's signed record as
      evidence (<xref target="compromisedpep"/>).</t>
    </section>

    <section anchor="permit">
      <name>The Bound Permit</name>

      <section anchor="envelope">
        <name>Envelope</name>
        <t>A bound permit is a JSON Web Token <xref target="RFC7519"/> in JWS
        compact serialization <xref target="RFC7515"/>, explicitly typed as
        <xref target="RFC8725"/> recommends:</t>
        <sourcecode type="json"><![CDATA[
{ "alg": "EdDSA", "kid": "<issuer key id>",
  "typ": "aadp-permit+jwt" }
]]></sourcecode>
        <t>EdDSA over Ed25519 is <bcp14>RECOMMENDED</bcp14>. A recipient
        <bcp14>MUST</bcp14> reject a bound permit whose "typ" is not
        "aadp-permit+jwt", and <bcp14>MUST</bcp14> reject any header
        parameter listed in "crit" that it does not implement.</t>
      </section>

      <section anchor="claims">
        <name>Claims</name>
        <table anchor="tab-claims">
          <name>Bound Permit Claims</name>
          <thead><tr><th>Claim</th><th>Presence</th><th>Meaning</th></tr></thead>
          <tbody>
            <tr><td>iss</td><td>REQUIRED</td><td>The issuer. Looked up in the recipient's issuer table (<xref target="scope"/>).</td></tr>
            <tr><td>sub</td><td>REQUIRED</td><td>The presenter, as an identifier the recipient can associate with the key in "cnf" (for example, a SPIFFE ID). Informational: the binding is "cnf", not "sub".</td></tr>
            <tr><td>aud</td><td>REQUIRED</td><td>The recipient, as a single value. A permit with more than one audience <bcp14>MUST</bcp14> be rejected. It equals the value of the permit's "present_bound" obligation.</td></tr>
            <tr><td>jti</td><td>REQUIRED</td><td>An identifier for this bound permit, unique per issuer, minted by the issuer (see below). Also the idempotency key (<xref target="idem"/>).</td></tr>
            <tr><td>iat, nbf, exp</td><td>REQUIRED</td><td>Issue time, not-before and expiry (<xref target="time"/>).</td></tr>
            <tr><td>cnf</td><td>REQUIRED across a trust boundary</td><td>The presenter's key <xref target="RFC7800"/>, as "jkt", a JWK thumbprint <xref target="RFC7638"/> as used by <xref target="RFC9449"/> (<xref target="sigbinding"/>).</td></tr>
            <tr><td>authorization_details</td><td>REQUIRED</td><td>What was decided, as typed entries <xref target="RFC9396"/> (<xref target="authzdetails"/>).</td></tr>
            <tr><td>action_digest</td><td>REQUIRED</td><td>The digest of the action object A (<xref target="actiondigest"/>).</td></tr>
            <tr><td>verdict</td><td>REQUIRED</td><td>Always "permit". Present so that the object states its own meaning; no other AADP verdict produces a bound permit.</td></tr>
            <tr><td>tier</td><td>OPTIONAL</td><td>The nominal and effective autonomy tiers from the AADP decide response.</td></tr>
            <tr><td>policy_version</td><td>RECOMMENDED</td><td>The content digest of the policy in force at the decision, as recorded in the issuer's AADP evidence.</td></tr>
            <tr><td>mandate</td><td>OPTIONAL</td><td>A mandate reference (<xref target="mandate"/>).</td></tr>
            <tr><td>currentness</td><td>REQUIRED</td><td>"time-bounded" or "status-checked" (<xref target="currentness"/>).</td></tr>
            <tr><td>status</td><td>REQUIRED if "currentness" is "status-checked"</td><td>Where the recipient checks status (<xref target="modes"/>).</td></tr>
            <tr><td>evidence</td><td>OPTIONAL</td><td>References into the issuer's AADP evidence record, to the presenter's preceding evidence record (for example a stage receipt <xref target="STAGE-RECEIPTS"/>), and to earlier hops ("upstream", <xref target="upstream"/>). Carried and echoed; never required for verification and never a source of authority.</td></tr>
          </tbody>
        </table>
        <t>The "jti" is not the AADP "permit_id". <xref target="AADP"/>
        Section 7 forbids presenting "permit_id" to a target resource as a
        credential or as a key carrying meaning there, because "permit_id"
        is the identity of AADP's decision and report lifecycle inside the
        governed domain. A bound permit is, by design, presented outside that
        domain, so it carries an identity of its own. The issuer
        <bcp14>MUST</bcp14> mint "jti" independently of "permit_id",
        <bcp14>MUST NOT</bcp14> make one derivable from the other, and
        <bcp14>MUST</bcp14> record the mapping between them in its AADP
        evidence entry for the decision.</t>
        <t>This profile is stricter than <xref target="AADP"/> Section 7,
        which recommends that a PEP derive a target's idempotency key
        deterministically from the permit, for example from "permit_id".
        Where the target is a bound-permit recipient, "jti" is that key
        (<xref target="idem"/>), and the independent minting required above
        replaces the derivation Section 7 recommends. Nothing else in
        <xref target="AADP"/> Section 7 changes.</t>
        <t>A claim is <bcp14>REQUIRED</bcp14> only if the recipient's
        verification consumes it. "policy_version", "mandate" and "evidence"
        are carried so that both sides' records can name them, not because
        the recipient can dereference them. Making a recipient depend on
        fields it cannot use would condition adoption on infrastructure it
        does not have.</t>
      </section>

      <section anchor="time">
        <name>Time</name>
        <ul>
          <li>"exp" <bcp14>MUST NOT</bcp14> be later than the deadline of the
          permit's "execute_within" obligation, where the AADP decide response
          carries one. A bound permit never outlives the permit it
          carries.</li>
          <li>Absent "execute_within", "exp" minus "iat" <bcp14>MUST
          NOT</bcp14> exceed 120 seconds. Registrations for particular action
          types <bcp14>MAY</bcp14> set a lower ceiling; none may set a higher
          one.</li>
          <li>Recipients <bcp14>MUST</bcp14> apply a declared clock-skew
          allowance not exceeding 60 seconds, and <bcp14>MUST</bcp14> record
          the allowance applied.</li>
        </ul>
      </section>

      <section anchor="authzdetails">
        <name>authorization_details</name>
        <t>Each entry has a "type" naming a registered action type
        (<xref target="iana"/>) and the fields that type defines. The type's
        registration states, for each field, its meaning, its units and its
        comparison rule against an issuer's scope (<xref target="scope"/>).
        For example:</t>
        <sourcecode type="json"><![CDATA[
"authorization_details": [{
  "type": "payments.transfer/1",
  "payee": "acme-gmbh",
  "amount": { "currency": "EUR", "max": "40.00" },
  "reference": "invoice-8841"
}]
]]></sourcecode>
        <t>A recipient <bcp14>MUST</bcp14> reject a permit carrying an
        "authorization_details" entry whose "type" it does not implement.
        Unknown types fail closed, as unknown obligations do in
        <xref target="AADP"/> Section 6.</t>
      </section>

      <section anchor="example">
        <name>Example</name>
        <sourcecode type="json"><![CDATA[
{
  "iss": "https://pdp.payer.example",
  "sub": "spiffe://payer.example/pep/accounts-payable",
  "aud": "https://payments.example.net",
  "jti": "bp-4f0c2a91d7e3",
  "iat": 1790152800, "nbf": 1790152800, "exp": 1790152860,
  "cnf": { "jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I" },
  "authorization_details": [{
    "type": "payments.transfer/1",
    "payee": "acme-gmbh",
    "amount": { "currency": "EUR", "max": "40.00" },
    "reference": "invoice-8841"
  }],
  "action_digest": "sha-256=:9Bh7a3W0...=:",
  "verdict": "permit",
  "tier": { "nominal": 1, "effective": 1 },
  "policy_version": "sha-256=:3f775k...=:",
  "mandate": { "type": "aae", "id": "urn:uuid:7c1e...",
               "digest": "sha256:ab41..." },
  "currentness": "time-bounded",
  "evidence": { "evidence_id": "aud-88213" }
}
]]></sourcecode>
      </section>
    </section>

    <section anchor="binding">
      <name>Binding the Permit to the Request</name>
      <t>A bound permit is useless if the request it accompanies can be
      altered. Three bindings close that: the body to its bytes, the request
      to the presenter's key, and the bytes to the decided action.</t>

      <section anchor="contentdigest">
        <name>Body: Content-Digest</name>
        <t>The presenter <bcp14>MUST</bcp14> send a Content-Digest field
        <xref target="RFC9530"/> computed over the exact content bytes sent,
        using sha-256 or a stronger registered algorithm. The recipient
        <bcp14>MUST</bcp14> recompute it over the bytes it received, before
        any parsing, decompression it did not negotiate, or
        re-serialization. The parsed object is not necessarily the object
        that was sent; <xref target="STAGE-RECEIPTS"/> applies the same
        boundary rule to external calls.</t>
      </section>

      <section anchor="sigbinding">
        <name>Request and Presenter: HTTP Message Signatures</name>
        <t>The presenter <bcp14>MUST</bcp14> sign the request with HTTP
        Message Signatures <xref target="RFC9421"/>, using the private key
        whose thumbprint is the permit's "cnf.jkt". The signature
        <bcp14>MUST</bcp14> cover, at minimum, the components "@method",
        "@authority", "@path", "@query", "content-digest", "idempotency-key"
        and "aadp-permit", where "aadp-permit" is the field carrying the
        bound permit (<xref target="iana"/>). Covering the permit field binds
        the signature to this permit; covering "content-digest" binds it to
        the body; the derived components bind it to the target. Action types
        <bcp14>MAY</bcp14> require further components in their registration.
        Volatile, hop-by-hop or intermediary-rewritten fields <bcp14>MUST
        NOT</bcp14> be covered.</t>
        <t>The recipient <bcp14>MUST</bcp14> verify the signature under the
        key identified by "cnf.jkt", and <bcp14>MUST</bcp14> reject a request
        whose signature verifies under any other key. Without this binding a
        bound permit is a bearer token: whoever holds it can present it until
        it expires. Bearer permits <bcp14>MAY</bcp14> be used inside one
        governed domain, where the channel assumptions of
        <xref target="AADP"/> Section 1.2 hold, and <bcp14>MUST</bcp14> be
        declared as bearer in that deployment's documentation. They
        <bcp14>MUST NOT</bcp14> be accepted across a trust boundary.</t>
      </section>

      <section anchor="actiondigest">
        <name>Semantics: the Action Object and action_digest</name>
        <t>Wire bytes and meaning are different things, and the permit binds
        both.</t>
        <ul>
          <li>Each registered action type defines how the action object A is
          derived from a request: which fields of the parsed body, path or
          query form A, with what types. A <bcp14>MUST</bcp14> be a JSON
          object.</li>
          <li>action_digest = "sha-256=:" || BASE64( SHA-256( "aadp:action:v1"
          || 0x00 || JCS(A) ) ) || ":", where JCS is the JSON Canonicalization
          Scheme <xref target="RFC8785"/>. The domain tag separates this
          digest from every other digest over the same object.</li>
          <li>The recipient derives A from the request it received, by the
          type's rules, and <bcp14>MUST</bcp14> reject the request if the
          recomputed digest differs from the permit's "action_digest".</li>
        </ul>
        <t>This digest is an instance binding. <xref target="I-D.kroehl-agentic-trust-aae"/>
        Section 2.2.2 binds a grant to an action type: the values that
        distinguish one instance from another (amounts, recipients, sequence
        numbers) are kept out of its action object and travel beside it,
        bounded by the grant's constraints, and a binding that commits to an
        action instance is outside that specification, which neither defines
        nor forbids it. The "action_digest" here is such an instance binding:
        its A carries the concrete values the decision was taken about. A
        recipient that also evaluates an AAE therefore derives two objects
        from the one request it received, each bound by its own digest under
        its own domain tag: the mandate says the kind of action is allowed
        within per-action bounds; the permit says this action was decided.
        Neither digest can stand in for the other. An action-type
        registration <bcp14>SHOULD</bcp14> state both derivations, so that
        they are taken from the same request fields.</t>
      </section>
    </section>

    <section anchor="mandate">
      <name>The Mandate Reference</name>

      <section anchor="whyref">
        <name>Why a Reference, and Not a Mandate</name>
        <t>Two requests can be identical in every respect AADP evaluates --
        identity, arguments, budget, approval state -- and differ only in the
        authority under which they are caused: an account suspension under a
        fraud-response mandate, and the same suspension under an
        employee-support mandate. The decision may be right in both cases;
        what fails without a reference is re-derivability, because a reader
        of the record cannot say under which mandate the action was
        authorized. This document does not define a mandate. It defines a
        reference to one:</t>
        <sourcecode type="json"><![CDATA[
"mandate": { "type": "aae", "id": "<credential id>",
             "digest": "<mandate digest>" }
]]></sourcecode>
        <ul>
          <li>"type" names the mandate format. "aae" is the only value defined
          here; others are registered (<xref target="iana"/>).</li>
          <li>"id" identifies the mandate object.</li>
          <li>"digest" is the mandate's own content digest as its format
          defines it. For "aae", it is the mandate digest of
          <xref target="I-D.kroehl-agentic-trust-aae"/> Section 2.5.3.</li>
        </ul>
      </section>

      <section anchor="mandaterules">
        <name>Rules at the Issuer</name>
        <ul>
          <li>The mandate reference <bcp14>MUST</bcp14> be established the way
          <xref target="AADP"/> Section 5.1 requires authorization-relevant
          provenance to be established: through an authenticated channel or
          credential, represented as context the policy evaluates. A caller
          <bcp14>MUST NOT</bcp14> be able to name its own mandate in request
          parameters.</li>
          <li>The issuer <bcp14>SHOULD</bcp14> record, in its AADP evidence,
          the mandate reference and the mandate's status as observed at
          decision time (for AAE, the result of its revocation check where
          present). Without that, a later reader can say which mandate was
          named but not that it was valid when the decision was taken.</li>
          <li>Where the issuer consumes a mandate-layer verdict, the
          correspondence of <xref target="AADP"/> Section 8.1 applies: no
          bound permit is issued while that verdict denies or defers the
          action.</li>
        </ul>
      </section>

      <section anchor="mandaterecipient">
        <name>At the Recipient</name>
        <t>Where "mandate.type" is "aae" and the AAE is presented or
        retrievable:</t>
        <ol>
          <li>The recipient evaluates the AAE under
          <xref target="I-D.kroehl-agentic-trust-aae"/> Section 5, including
          subject binding, validity, single use, revocation and delegation,
          against the transaction it derives from the same request.</li>
          <li>The recipient <bcp14>MUST</bcp14> compare the evaluated AAE's
          mandate digest with the permit's "mandate.digest" and reject on
          mismatch ("mandate-mismatch").</li>
          <li>The AAE verdict governs as follows: PERMIT, continue; DENY,
          refuse ("mandate-denied"); PENDING, refuse ("mandate-pending"). The
          request <bcp14>MAY</bcp14> be resubmitted once ratification under
          that document's Section 6.3 has occurred. A bound permit never
          converts PENDING into PERMIT.</li>
        </ol>
        <t>Where the permit names a mandate the recipient cannot obtain or
        evaluate, the recipient <bcp14>MUST</bcp14> refuse
        ("mandate-unavailable") unless its local policy explicitly treats
        that action type as mandate-optional, and it <bcp14>MUST</bcp14>
        record which it did.</t>
      </section>
    </section>

    <section anchor="scope">
      <name>Issuer Scope</name>
      <t>A trust store that merely lists issuers lets any trusted issuer
      authorize anything the recipient's local policy fails to stop. This
      profile replaces the list with a table of scopes. Each entry
      states:</t>
      <artwork type="ascii-art"><![CDATA[
issuer:        https://pdp.payer.example
keys:          jwks_uri or pinned keys
action types:  [ payments.transfer/1 ]
limits:        per type, per field, using the type's comparison rule
               e.g. payments.transfer/1: amount.max <= EUR 1,000.00
not_after:     2027-01-01T00:00:00Z
currentness:   minimum mode accepted from this issuer
]]></artwork>
      <ul>
        <li>Every "authorization_details" entry in a permit <bcp14>MUST</bcp14>
        fall inside the issuer's scope: its "type" listed, and every limited
        field within its limit by the type's comparison rule. Otherwise the
        recipient refuses ("issuer-out-of-scope"), naming the entry and field,
        not the limit.</li>
        <li>An issuer <bcp14>MAY</bcp14> publish the action types it decides
        in a metadata document modeled on <xref target="RFC8414"/>, under
        "aadp_action_types_supported". Publication informs a recipient's
        table; it never grants scope. The recipient's table is authoritative
        and is changed only by the recipient.</li>
        <li>Changes to a recipient's issuer table <bcp14>SHOULD</bcp14> be
        recorded with who made them, when, and what changed. A scope that
        widens without a record is a permission granted by nobody.</li>
      </ul>
    </section>

    <section anchor="currentness">
      <name>Currentness</name>
      <section anchor="currentproblem">
        <name>The Problem</name>
        <t>A bound permit proves that a decision was taken under a policy
        version and, where named, a mandate. It does not by itself prove that
        either was still in force when the request arrived. A policy
        superseded, or a mandate revoked, between decision and execution is
        invisible to the recipient. Short lifetimes bound that window in
        time; they do not close it. Inside one domain the PEP can ask again;
        across a boundary the recipient cannot.</t>
        <t>This profile does not claim to close the window by default. It
        requires every permit to declare which of two modes it is in, and
        every recipient record to state which mode applied, so that the
        limitation is visible in each record instead of hidden in each
        deployment.</t>
      </section>
      <section anchor="modes">
        <name>Modes</name>
        <dl>
          <dt>time-bounded:</dt>
          <dd>The permit is current until "exp" and no further check is
          made. Declared limitation: a change of policy version or a
          revocation of the mandate before "exp" is not detected. A recipient
          <bcp14>MAY</bcp14> accept "time-bounded" only for action types and
          issuers whose table entry allows it.</dd>
          <dt>status-checked:</dt>
          <dd>The permit carries "status", and the recipient
          <bcp14>MUST</bcp14> establish, at the moment of verification, that
          neither the permit, its "policy_version", nor its "mandate" has been
          revoked or superseded. Two mechanisms are defined. Status list:
          "status" identifies an entry in a status list published by the
          issuer, following <xref target="I-D.ietf-oauth-status-list"/>.
          Stapled freshness: the presenter includes an issuer-signed
          freshness statement in the "AADP-Permit-Status" field, of the form
          { "jti", "policy_version", "mandate_digest", "status": "current",
          "iat" }, whose "iat" is within the table's freshness window
          (<bcp14>RECOMMENDED</bcp14>: no more than 30 seconds).</dd>
        </dl>
        <t>If a "status-checked" permit's status cannot be determined --
        endpoint unreachable, response unparseable, statement stale -- the
        recipient <bcp14>MUST</bcp14> refuse ("status-unavailable"). A
        recipient <bcp14>MAY</bcp14> apply an explicit, locally configured,
        audited fail-open policy for action types whose risk classification
        permits it, and <bcp14>MUST</bcp14> record that it did.</t>
      </section>
    </section>

    <section anchor="verify">
      <name>Recipient Verification</name>
      <section anchor="order">
        <name>Order</name>
        <t>The REP performs these steps in order, stopping at the first
        failure. Cheap structural checks come before cryptography,
        cryptography before state, and state is consumed last, so that a
        request refused for any other reason does not consume its
        permit.</t>
        <table anchor="tab-order">
          <name>Verification Order</name>
          <thead><tr><th>#</th><th>Step</th><th>Refusal reason</th></tr></thead>
          <tbody>
            <tr><td>1</td><td>Permit present, parses, "typ" is "aadp-permit+jwt", "crit" understood</td><td>malformed</td></tr>
            <tr><td>2</td><td>"iss" in the issuer table; signature verifies under a current key for that issuer</td><td>issuer-unknown, signature-invalid</td></tr>
            <tr><td>3</td><td>"aud" identifies this recipient, as a single value</td><td>audience-mismatch</td></tr>
            <tr><td>4</td><td>nbf &lt;= now &lt;= exp within the declared skew; the rules of <xref target="time"/> hold</td><td>expired, not-yet-valid, lifetime-invalid</td></tr>
            <tr><td>5</td><td>Every "authorization_details" type implemented</td><td>unknown-authorization-type</td></tr>
            <tr><td>6</td><td>Every entry within the issuer's scope</td><td>issuer-out-of-scope</td></tr>
            <tr><td>7</td><td>Content-Digest recomputed over the received bytes matches</td><td>content-mismatch</td></tr>
            <tr><td>8</td><td>An HTTP message signature is present, made with the key "cnf.jkt" names, verifies, and covers the required components</td><td>request-signature-missing, presenter-key-mismatch, request-signature-invalid, binding-incomplete</td></tr>
            <tr><td>9</td><td>A derived from the request; its digest equals "action_digest"</td><td>action-mismatch</td></tr>
            <tr><td>10</td><td>Currentness per "currentness" (<xref target="currentness"/>)</td><td>stale-policy, mandate-revoked, status-unavailable</td></tr>
            <tr><td>11</td><td>Referenced mandate evaluated (<xref target="mandaterecipient"/>)</td><td>mandate-mismatch, mandate-denied, mandate-pending, mandate-unavailable</td></tr>
            <tr><td>12</td><td>Recipient-local policy permits</td><td>local-policy</td></tr>
            <tr><td>13</td><td>(iss, jti) consumed atomically (<xref target="idem"/>)</td><td>replayed, idempotency-conflict</td></tr>
          </tbody>
        </table>
        <t>Only after step 13 does the recipient perform the effect.</t>
      </section>
      <section anchor="failclosed">
        <name>Fail Closed</name>
        <ul>
          <li>A recipient that requires bound permits for an endpoint
          <bcp14>MUST NOT</bcp14> fall back to other authorization when a
          permit is absent or fails. Absence is "malformed", not a reason to
          accept another credential.</li>
          <li>If any verification dependency is unavailable -- key discovery,
          status, mandate retrieval, the consume store -- the recipient
          <bcp14>MUST</bcp14> refuse, with the reason naming the dependency,
          and <bcp14>MUST</bcp14> distinguish that refusal from a policy
          denial in its record, as <xref target="AADP"/> Section 5.1
          distinguishes a PDP defect from a policy outcome.</li>
        </ul>
      </section>
      <section anchor="states">
        <name>Verification States in the Record</name>
        <t>A recipient's record states, per request, one of: "verified" (every
        step passed); "refused", with its reason code and the step at which
        it stopped; or "could-not-check", with the dependency that was
        unavailable. There is no partial verification: a verification in
        which a required check was not performed is "could-not-check".</t>
      </section>
    </section>

    <section anchor="refusals">
      <name>Refusal Reasons</name>
      <t>Refusals are returned as HTTP status 403 (409 for
      "idempotency-conflict", 503 for "could-not-check") with a Problem
      Details body <xref target="RFC9457"/> whose "type" is the registered
      reason URI (<xref target="iana"/>). Reason codes say which check
      failed. They <bcp14>MUST NOT</bcp14> disclose the recipient's policy,
      limits or issuer table: "issuer-out-of-scope" names the entry and
      field that fell outside, not the bound it exceeded. The initial set is
      the reason column of <xref target="tab-order"/>, plus
      "chained-permit-unsupported" (<xref target="hops"/>).</t>
    </section>

    <section anchor="confirmation">
      <name>The Recipient Confirmation</name>
      <t>Each side of a cross-domain action keeps its own record; without
      more, a dispute is two self-authored accounts. This profile asks for
      one object the recipient signs and both sides keep. On completing step
      13 and attempting the effect, the recipient returns a recipient
      confirmation, a JWS signed by the recipient, in the
      "AADP-Recipient-Confirmation" response field:</t>
      <sourcecode type="json"><![CDATA[
{
  "typ": "aadp-confirmation+jwt",
  "iss": "https://payments.example.net",
  "permit": { "iss": "https://pdp.payer.example",
              "jti": "bp-4f0c2a91d7e3",
              "digest": "sha-256=:<digest of the permit's compact
                         serialization>:" },
  "request": { "content_digest": "sha-256=:...:",
               "signature_base_digest": "sha-256=:<digest of the
                                        RFC 9421 signature base>:" },
  "action_digest": "sha-256=:...:",
  "mandate_verdict": { "type": "aae", "core_digest": "sha256:..." },
  "outcome": "effected | refused | effect-unknown",
  "reason": "<reason code if refused>",
  "recipient_action_id": "TX-77120",
  "iat": 1790152803
}
]]></sourcecode>
      <ul>
        <li>"mandate_verdict.core_digest", where an AAE was evaluated, is the
        digest of the recipient's AAE verdict core
        (<xref target="I-D.kroehl-agentic-trust-aae"/> Section 2.5.3), so that
        the mandate layer's record and this confirmation reference each
        other.</li>
        <li>The presenter <bcp14>MUST</bcp14> include the confirmation's
        digest in its AADP report for the permit; this is the discharge
        evidence of the "present_bound" obligation (<xref target="AADP"/>
        Section 6). A presenter that keeps per-stage evidence -- for example
        stage receipts <xref target="STAGE-RECEIPTS"/> -- <bcp14>SHOULD</bcp14>
        also include it in the record of the sending stage. This profile does
        not require stage receipts: the chain holds with the AADP report
        alone.</li>
        <li>Direction of reference, so that nothing is circular: the permit
        <bcp14>MAY</bcp14> reference the presenter's preceding evidence
        record; the sending stage's record references the permit and the
        confirmation; the confirmation references the permit and the
        request. Nothing references a record that is written after it.</li>
      </ul>
      <t>A recipient that refuses at any step <bcp14>SHOULD</bcp14> still
      return a confirmation with "outcome": "refused" and the reason. A
      refusal the recipient signs is evidence; a refusal it merely returns
      is an assertion.</t>
    </section>

    <section anchor="hops">
      <name>More Than One Hop</name>
      <t>A bound permit authorizes one request, to one audience, by one
      presenter. This version of the profile is single-hop:</t>
      <ul>
        <li>A permit <bcp14>MUST NOT</bcp14> carry a "parent" claim. A
        recipient receiving one <bcp14>MUST</bcp14> refuse with
        "chained-permit-unsupported".</li>
        <li>A recipient that is itself about to perform a consequential action
        in a further domain takes its own decision in its own domain and
        presents its own bound permit there. A decision is not forwarded; it
        is taken again where the state is.</li>
        <li>Delegation of mandate across hops belongs to the mandate format:
        a downstream permit may reference a delegated mandate whose chain
        leads back to the original principal. That carries the authority
        across hops without carrying the decision.</li>
      </ul>
      <t>A capability declared unsupported must be refused by the code, not
      merely left out of the text. Chained permits, in which an intermediary
      derives a permit from one it received, are out of scope for this
      version; any future design would be expected to build on existing
      delegation mechanisms such as <xref target="RFC8693"/>.</t>
      <t>Two patterns cover most multi-party cases without a chain. Both
      are informative: they use only what this document already
      defines.</t>

      <section anchor="fanout">
        <name>Issuer Fan-Out</name>
        <t>When one piece of work is carried out as several requests -- an
        orchestrating agent that hands sub-tasks to sub-agents, or a batch of
        payments executed by several workers -- each request is decided by
        the issuer on its own, and the issuer issues one bound permit per
        request. Each permit names its own audience, its own presenter key
        in "cnf" (the sub-agent's or worker's key, not the orchestrator's),
        its own "authorization_details" and "action_digest", and its own
        "jti".</t>
        <t>Budgets are then enforced where they live. Each sub-request
        reserves against the issuer's cumulative budgets under
        <xref target="AADP"/> Section 4, so the total across all sub-requests
        is bounded by the same budgets that bound any other set of actions,
        and each reservation is resolved by its own report. No party other
        than the issuer derives or narrows a permit, and no recipient has to
        verify more than one issuer signature.</t>
      </section>

      <section anchor="upstream">
        <name>Upstream Reference</name>
        <t>When a recipient in one domain acts, in turn, towards a further
        domain -- A's request causes B to call C -- B takes its own decision
        and presents its own bound permit to C, as above. C may still want to
        know what caused B's request. B's issuer <bcp14>MAY</bcp14> include,
        in the permit's "evidence" claim, an "upstream" member: an array of
        references to earlier hops, each of the form
        { "iss", "jti", "digest" }, where "digest" is the digest of that
        hop's bound permit or of the recipient confirmation B returned for it
        (<xref target="confirmation"/>).</t>
        <ul>
          <li>An upstream reference is evidence, not authority. A recipient
          <bcp14>MUST NOT</bcp14> treat it as authorizing anything, and
          <bcp14>MUST NOT</bcp14> relax any verification step because of
          it.</li>
          <li>A recipient <bcp14>MAY</bcp14> record upstream references, and
          <bcp14>MAY</bcp14> refuse under local policy a request that lacks
          one where its policy requires provenance ("local-policy").</li>
          <li>Because each hop's confirmation names the permit it answered,
          and each later permit names the earlier confirmation, the hops form
          a sequence of records that each side signed, readable after the
          fact, without any hop deriving its authority from another.</li>
        </ul>
      </section>
    </section>

    <section anchor="idem">
      <name>Idempotency and Single Use</name>
      <ul>
        <li>The presenter <bcp14>MUST</bcp14> send an Idempotency-Key field
        (as described in <xref target="I-D.ietf-httpapi-idempotency-key-header"/>)
        equal to the permit's "jti". No secret is involved: the key is already
        bound by the issuer's signature and the presenter's. This profile
        states the semantics below in full and does not depend on that
        document.</li>
        <li>The recipient keeps a consume store keyed by (iss, jti), durable
        and atomic, shared across every node that can serve the endpoint. A
        recipient that cannot keep such a store <bcp14>MUST NOT</bcp14>
        accept bound permits.</li>
        <li>First presentation: consume, perform, store the result.</li>
        <li>Same (iss, jti) and same Content-Digest, after completion: return
        the stored result and record the presentation as a repeat, not a
        second effect.</li>
        <li>Same (iss, jti), different Content-Digest: refuse
        ("idempotency-conflict").</li>
        <li>Same (iss, jti) while the first is still in progress: refuse with
        a retryable status.</li>
        <li>Unknown outcome: a presenter that sent a request and cannot
        establish whether the effect occurred <bcp14>MUST NOT</bcp14> obtain a
        new permit and resend. It reports "timeout" under
        <xref target="AADP"/>, reconciles with the recipient using "jti" or
        "recipient_action_id", and escalates if reconciliation fails.</li>
      </ul>
    </section>

    <section anchor="aadp">
      <name>Relationship to AADP</name>
      <ul>
        <li>A bound permit is issued only from a decide response whose verdict
        is "permit" and which carries a "present_bound" obligation
        (<xref target="AADP"/> Section 6); its "aud" is that obligation's
        value. "propose", "dry_run", "observe" and "replay" produce no bound
        permit.</li>
        <li>Report outcomes. Effect confirmed: "success", with the
        confirmation digest. Recipient refused, with a signed confirmation
        whose outcome is "refused": "failure" with "no_effect": true, because
        the recipient established, and signed, that it acted on nothing.
        Presenter declined, for example because the permit expired before
        sending: "not_attempted". Outcome unknown, including a confirmation
        whose outcome is "effect-unknown" or no confirmation at all:
        "timeout". Exactly one report per permit, as <xref target="AADP"/>
        requires.</li>
        <li>Budgets follow <xref target="AADP"/> Section 4.1 unchanged: the
        reservation is committed on "success" and "timeout", and released on
        "not_attempted" and on "failure" with "no_effect": true. A refusal
        the recipient did not sign does not establish that no effect
        occurred, and the presenter <bcp14>MUST NOT</bcp14> set "no_effect"
        on its strength. Rate budgets are never released. This stateful
        accounting is the part a stateless mandate layer leaves as future
        work, and the reason the two compose rather than overlap.</li>
      </ul>
    </section>

    <section anchor="related">
      <name>Related Work</name>
      <t><xref target="I-D.kroehl-agentic-trust-aae"/> specifies signed
      mandates, action-type binding, delegation by strict subordination,
      single-use presentation, revocation checking and verdict records. This
      document references those rather than re-specifying them, and adds
      only what that document places outside its scope: action-instance
      binding, state across presentations, HTTP request binding, issuer
      scope, currentness and a recipient-signed confirmation.</t>
      <t><xref target="I-D.schrock-ep-authorization-receipts"/> binds a
      named human approver's signature to one canonical action, for offline
      verification and single use. A bound permit instead carries a policy
      decision point's decision, which may depend on budgets and approval
      state, and binds it to one presenter key and one HTTP request. The two
      can meet: an AADP approval (<xref target="AADP"/> Section 8) could be
      evidenced by such a receipt, and the resulting decision carried as a
      bound permit.</t>
      <t><xref target="I-D.sirkkavaara-vaara-receipt"/> defines a signed,
      recomputable record of a decision about an autonomous action and its
      execution. It records a decision after the fact; a bound permit is the
      form in which a decision is presented to the party that performs the
      action, and the recipient confirmation is that party's signed account
      of what it did with it.</t>
      <t>Several documents already carry parts of this profile, and this
      document claims only their combination. <xref target="I-D.lee-orprg-permit-receipts"/>
      states requirements and an abstract data model for authorizing an
      effect before it is committed at an effect boundary, with binding to
      an action digest, replay protection by durable state, reason codes for
      refusal and conformance vectors; it defines no wire format and no
      presenter binding. <xref target="I-D.toraman-noa-action-digest"/>
      defines a domain-separated digest over the canonical form of an
      approved action, verified in a fixed order with distinct failures;
      this document's "action_digest" (<xref target="actiondigest"/>) is a
      construction of the same kind, and a later revision may align the
      two rather than keep both. <xref target="I-D.mih-agent-bilateral-attestation"/>
      has the performing organization countersign a record of the request
      it acted on, which is the role of the recipient confirmation
      (<xref target="confirmation"/>). <xref target="I-D.munoz-scitt-permit-profile"/>
      and <xref target="I-D.munoz-wimse-authorization-evidence"/> record a
      decision point's pre-execution decision over the request bytes, as
      evidence for transparency and audit; the permit here is instead
      presented to, and consumed once by, the party that performs the
      action. <xref target="I-D.ruvalcaba-nhe-authz"/> issues a single-use,
      short-lived credential bound to a digest of the operation's
      parameters after human approval. <xref target="I-D.schrock-ep-bounded-capability-receipts"/>
      combines holder proof, an action snapshot and single use per operation
      with durable spend control.</t>
      <t>What this document adds to those is the combination: a decision
      that depends on the issuer's state (budgets, reservations, approvals)
      carried to one audience, bound to a presenter key and to one HTTP
      request by message signature, consumed once by the recipient after a
      fixed verification order, and answered by a confirmation the recipient
      signs.</t>
      <t><xref target="I-D.ietf-oauth-transaction-tokens"/> propagate
      identity and authorization context along a call chain within a trusted
      domain. A bound permit is for the case that document excludes: one
      decided request presented across a trust boundary.</t>
    </section>

    <section anchor="security">
      <name>Security Considerations</name>
      <section anchor="bearer">
        <name>Bearer Versus Bound</name>
        <t>Without "cnf" and the request signature, a bound permit is a bearer
        token. <xref target="sigbinding"/> forbids bearer permits across a
        trust boundary. The single most likely deployment mistake is accepting
        the permit without verifying the request signature; conformance
        vectors for it are mandatory (<xref target="vectors"/>).</t>
      </section>
      <section anchor="keys">
        <name>Key Compromise</name>
        <t>A compromised issuer key forges permits until the recipient's table
        stops trusting it. Lifetimes are bounded (<xref target="time"/>), and
        "status-checked" permits can be revoked at the status list. A
        compromised presenter key lets an attacker present permits already
        issued to that presenter, within their lifetimes, to their audiences,
        for their decided actions only.</t>
      </section>
      <section anchor="compromisedpep">
        <name>A Compromised Presenter</name>
        <t>A presenter that holds direct capability to the recipient can bypass
        this profile entirely; <xref target="AADP"/> Section 1.2 already
        disclaims that case. What this profile changes is the case in which
        the presenter uses the profile and substitutes a request: the
        substituted request fails "action-mismatch" or "content-mismatch", and
        the recipient's signed refusal records a decided action and a
        presented action that differ. That is not prevention. It turns a
        silent substitution into evidence that names both sides.</t>
      </section>
      <section anchor="upstreamsec">
        <name>Upstream References</name>
        <t>An upstream reference is written by the issuer of the permit that
        carries it and is not verified by this profile. A recipient that
        granted it authority would accept, from a compromised or careless
        issuer, a claim about another domain's decision that no one checked.
        <xref target="upstream"/> therefore makes it evidence only.</t>
      </section>
      <section anchor="timesec">
        <name>Time</name>
        <t>Clock skew is bounded and recorded (<xref target="time"/>). A
        recipient <bcp14>MUST NOT</bcp14> extend "exp" by its skew allowance
        beyond the "execute_within" deadline.</t>
      </section>
      <section anchor="intermediaries">
        <name>Intermediaries</name>
        <t>A proxy that re-encodes the body, rewrites the path or strips
        unknown fields will cause refusals by design. Deployments with such
        intermediaries either terminate the profile at the intermediary,
        which then becomes the recipient with its own record, or configure it
        to pass covered components untouched.</t>
      </section>
      <section anchor="notprotect">
        <name>What the Profile Does Not Protect</name>
        <t>A request that policy should not have permitted; a recipient that
        lies in its confirmation; issuer and recipient in collusion; a mandate
        that was wrongly granted. Each needs controls outside this
        document.</t>
      </section>
    </section>

    <section anchor="privacy">
      <name>Privacy Considerations</name>
      <t>A bound permit discloses, to its recipient, the decided action, the
      presenter's identifier, the issuer, a policy version digest and
      optionally a mandate reference. It discloses no policy content and no
      other action. "authorization_details" <bcp14>SHOULD</bcp14> carry the
      minimum fields the action type needs. Recipient confirmations disclose
      the recipient's action identifier to the presenter. Digests of personal
      data <bcp14>SHOULD NOT</bcp14> be used as identifiers where the data is
      guessable.</t>
    </section>

    <section anchor="vectors">
      <name>Conformance Vectors</name>
      <t>Each vector is a refusal with a named reason, and each is paired
      with the valid case beside it, so that a verifier that refuses
      everything does not pass. Where a rule has an absent case and an empty
      case, both are vectors.</t>
      <table anchor="tab-vectors">
        <name>Conformance Vectors</name>
        <thead><tr><th>#</th><th>Mutation</th><th>Expected</th></tr></thead>
        <tbody>
          <tr><td>V01</td><td>Body changed after signing</td><td>content-mismatch</td></tr>
          <tr><td>V02</td><td>Path changed after signing</td><td>request-signature-invalid</td></tr>
          <tr><td>V03</td><td>Presented to a different recipient</td><td>audience-mismatch</td></tr>
          <tr><td>V04</td><td>"aud" carries two values</td><td>audience-mismatch</td></tr>
          <tr><td>V05</td><td>Presented after "exp"</td><td>expired</td></tr>
          <tr><td>V06</td><td>"exp" later than "execute_within"</td><td>lifetime-invalid</td></tr>
          <tr><td>V07</td><td>Same permit presented twice, same body</td><td>stored result returned; recorded as a repeat</td></tr>
          <tr><td>V08</td><td>Same "jti", different body</td><td>idempotency-conflict</td></tr>
          <tr><td>V09</td><td>Request signed by a key other than "cnf.jkt"</td><td>presenter-key-mismatch</td></tr>
          <tr><td>V10</td><td>Request not signed at all (bearer presentation)</td><td>request-signature-missing</td></tr>
          <tr><td>V11</td><td>Signature omits "content-digest" from the covered components</td><td>binding-incomplete</td></tr>
          <tr><td>V12</td><td>Body re-serialized with the same meaning, different bytes</td><td>content-mismatch</td></tr>
          <tr><td>V13</td><td>Same bytes, A derived differently from what was decided</td><td>action-mismatch</td></tr>
          <tr><td>V14</td><td>"authorization_details" type unknown to the recipient</td><td>unknown-authorization-type</td></tr>
          <tr><td>V15</td><td>Issuer trusted, action type outside its scope</td><td>issuer-out-of-scope</td></tr>
          <tr><td>V16</td><td>Issuer trusted for the type, amount above its limit</td><td>issuer-out-of-scope</td></tr>
          <tr><td>V17</td><td>"status-checked", policy version since superseded</td><td>stale-policy</td></tr>
          <tr><td>V18</td><td>"status-checked", status endpoint unreachable</td><td>status-unavailable (could-not-check)</td></tr>
          <tr><td>V19</td><td>Mandate digest differs from the evaluated AAE</td><td>mandate-mismatch</td></tr>
          <tr><td>V20</td><td>Referenced AAE evaluates to PENDING</td><td>mandate-pending</td></tr>
          <tr><td>V21</td><td>Permit carries "parent"</td><td>chained-permit-unsupported</td></tr>
          <tr><td>V22</td><td>Recipient confirmation names a different request digest</td><td>detected by the presenter; the report records the discrepancy</td></tr>
          <tr><td>V23</td><td>Consume store unavailable</td><td>could-not-check, not a policy denial</td></tr>
          <tr><td>V24</td><td>Permit valid, local policy denies</td><td>local-policy, and the permit is not consumed</td></tr>
        </tbody>
      </table>
    </section>

    <section anchor="iana">
      <name>IANA Considerations</name>
      <t>This document requests the following registrations. Registration
      procedures and templates will be completed in a later revision.</t>
      <ol>
        <li>Media types: "application/aadp-permit+jwt" and
        "application/aadp-confirmation+jwt".</li>
        <li>HTTP fields: "AADP-Permit" (request; the bound permit),
        "AADP-Permit-Status" (request; stapled freshness) and
        "AADP-Recipient-Confirmation" (response).</li>
        <li>An AADP Action Types registry: name and version, fields, units,
        the derivation of A, the comparison rule per field for scope checks,
        and any further covered components the type requires.</li>
        <li>An AADP Mandate Reference Types registry, with the initial value
        "aae", referencing <xref target="I-D.kroehl-agentic-trust-aae"/>.</li>
        <li>An AADP Bound Permit Refusal Reasons registry: the codes of
        <xref target="refusals"/>, each with a Problem Details type URI.</li>
        <li>JSON Web Token claims: "action_digest", "verdict", "tier",
        "policy_version", "mandate", "currentness", "status" and "evidence",
        where not already registered.</li>
        <li>OAuth authorization server metadata:
        "aadp_action_types_supported".</li>
      </ol>
    </section>

  </middle>

  <back>
    <references>
      <name>References</name>
      <references>
        <name>Normative References</name>
        <reference anchor="AADP" target="https://datatracker.ietf.org/doc/draft-saha-aadp/">
          <front>
            <title>The Agent Action Decision Protocol (AADP): Per-Action Authorization for AI Agents</title>
            <author initials="S." surname="Saha" fullname="Shamik Saha"/>
            <date year="2026" month="September"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-saha-aadp-04"/>
        </reference>
        <reference anchor="I-D.kroehl-agentic-trust-aae" target="https://datatracker.ietf.org/doc/draft-kroehl-agentic-trust-aae/">
          <front>
            <title>Agent Authorization Envelope (AAE): A Machine-Evaluable Authorization Structure for Autonomous AI Agents</title>
            <author initials="L. K." surname="Kroehl" fullname="Lars Kersten Kroehl">
              <organization>CryptoKRI GmbH</organization>
            </author>
            <date year="2026" month="September" day="6"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-kroehl-agentic-trust-aae-02"/>
          <annotation>Normative only for implementations that support the "aae" mandate type.</annotation>
        </reference>
        <reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119">
          <front><title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author initials="S." surname="Bradner" fullname="S. Bradner"/><date year="1997" month="March"/></front>
          <seriesInfo name="BCP" value="14"/><seriesInfo name="RFC" value="2119"/>
        </reference>
        <reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174">
          <front><title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author initials="B." surname="Leiba" fullname="B. Leiba"/><date year="2017" month="May"/></front>
          <seriesInfo name="BCP" value="14"/><seriesInfo name="RFC" value="8174"/>
        </reference>
        <reference anchor="RFC7515" target="https://www.rfc-editor.org/info/rfc7515">
          <front><title>JSON Web Signature (JWS)</title>
            <author initials="M." surname="Jones"/><author initials="J." surname="Bradley"/><author initials="N." surname="Sakimura"/><date year="2015" month="May"/></front>
          <seriesInfo name="RFC" value="7515"/>
        </reference>
        <reference anchor="RFC7519" target="https://www.rfc-editor.org/info/rfc7519">
          <front><title>JSON Web Token (JWT)</title>
            <author initials="M." surname="Jones"/><author initials="J." surname="Bradley"/><author initials="N." surname="Sakimura"/><date year="2015" month="May"/></front>
          <seriesInfo name="RFC" value="7519"/>
        </reference>
        <reference anchor="RFC7638" target="https://www.rfc-editor.org/info/rfc7638">
          <front><title>JSON Web Key (JWK) Thumbprint</title>
            <author initials="M." surname="Jones"/><author initials="N." surname="Sakimura"/><date year="2015" month="September"/></front>
          <seriesInfo name="RFC" value="7638"/>
        </reference>
        <reference anchor="RFC7800" target="https://www.rfc-editor.org/info/rfc7800">
          <front><title>Proof-of-Possession Key Semantics for JSON Web Tokens (JWTs)</title>
            <author initials="M." surname="Jones"/><author initials="J." surname="Bradley"/><author initials="H." surname="Tschofenig"/><date year="2016" month="April"/></front>
          <seriesInfo name="RFC" value="7800"/>
        </reference>
        <reference anchor="RFC8725" target="https://www.rfc-editor.org/info/rfc8725">
          <front><title>JSON Web Token Best Current Practices</title>
            <author initials="Y." surname="Sheffer"/><author initials="D." surname="Hardt"/><author initials="M." surname="Jones"/><date year="2020" month="February"/></front>
          <seriesInfo name="BCP" value="225"/><seriesInfo name="RFC" value="8725"/>
        </reference>
        <reference anchor="RFC8785" target="https://www.rfc-editor.org/info/rfc8785">
          <front><title>JSON Canonicalization Scheme (JCS)</title>
            <author initials="A." surname="Rundgren"/><author initials="B." surname="Jordan"/><author initials="S." surname="Erdtman"/><date year="2020" month="June"/></front>
          <seriesInfo name="RFC" value="8785"/>
        </reference>
        <reference anchor="RFC9396" target="https://www.rfc-editor.org/info/rfc9396">
          <front><title>OAuth 2.0 Rich Authorization Requests</title>
            <author initials="T." surname="Lodderstedt"/><author initials="J." surname="Richer"/><author initials="B." surname="Campbell"/><date year="2023" month="May"/></front>
          <seriesInfo name="RFC" value="9396"/>
        </reference>
        <reference anchor="RFC9421" target="https://www.rfc-editor.org/info/rfc9421">
          <front><title>HTTP Message Signatures</title>
            <author initials="A." surname="Backman"/><author initials="J." surname="Richer"/><author initials="M." surname="Sporny"/><date year="2024" month="February"/></front>
          <seriesInfo name="RFC" value="9421"/>
        </reference>
        <reference anchor="RFC9449" target="https://www.rfc-editor.org/info/rfc9449">
          <front><title>OAuth 2.0 Demonstrating Proof of Possession (DPoP)</title>
            <author initials="D." surname="Fett"/><author initials="B." surname="Campbell"/><author initials="J." surname="Bradley"/><author initials="T." surname="Lodderstedt"/><author initials="M." surname="Jones"/><author initials="D." surname="Waite"/><date year="2023" month="September"/></front>
          <seriesInfo name="RFC" value="9449"/>
        </reference>
        <reference anchor="RFC9457" target="https://www.rfc-editor.org/info/rfc9457">
          <front><title>Problem Details for HTTP APIs</title>
            <author initials="M." surname="Nottingham"/><author initials="E." surname="Wilde"/><author initials="S." surname="Dalal"/><date year="2023" month="July"/></front>
          <seriesInfo name="RFC" value="9457"/>
        </reference>
        <reference anchor="RFC9530" target="https://www.rfc-editor.org/info/rfc9530">
          <front><title>Digest Fields</title>
            <author initials="R." surname="Polli"/><author initials="L." surname="Pardue"/><date year="2024" month="February"/></front>
          <seriesInfo name="RFC" value="9530"/>
        </reference>
      </references>
      <references>
        <name>Informative References</name>
        <reference anchor="STAGE-RECEIPTS" target="https://datatracker.ietf.org/doc/draft-saha-stage-receipts/">
          <front>
            <title>Stage Receipts: A Verifiable Record Format for Staged Pipelines</title>
            <author initials="S." surname="Saha" fullname="Shamik Saha"/>
            <date year="2026" month="September"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-saha-stage-receipts-00"/>
        </reference>
        <reference anchor="RFC8414" target="https://www.rfc-editor.org/info/rfc8414">
          <front><title>OAuth 2.0 Authorization Server Metadata</title>
            <author initials="M." surname="Jones"/><author initials="N." surname="Sakimura"/><author initials="J." surname="Bradley"/><date year="2018" month="June"/></front>
          <seriesInfo name="RFC" value="8414"/>
        </reference>
        <reference anchor="RFC8693" target="https://www.rfc-editor.org/info/rfc8693">
          <front><title>OAuth 2.0 Token Exchange</title>
            <author initials="M." surname="Jones"/><author initials="A." surname="Nadalin"/><author initials="B." surname="Campbell"/><author initials="J." surname="Bradley"/><author initials="C." surname="Mortimore"/><date year="2020" month="January"/></front>
          <seriesInfo name="RFC" value="8693"/>
        </reference>
        <reference anchor="I-D.ietf-oauth-status-list" target="https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/">
          <front><title>Token Status List (TSL)</title>
            <author initials="T." surname="Looker"/><author initials="P." surname="Bastian"/><author initials="C." surname="Bormann"/><date year="2026" month="June"/></front>
          <seriesInfo name="Internet-Draft" value="draft-ietf-oauth-status-list-21"/>
        </reference>
        <reference anchor="RFC7942" target="https://www.rfc-editor.org/info/rfc7942">
          <front><title>Improving Awareness of Running Code: The Implementation Status Section</title>
            <author initials="Y." surname="Sheffer"/><author initials="A." surname="Farrel"/><date year="2016" month="July"/></front>
          <seriesInfo name="BCP" value="205"/><seriesInfo name="RFC" value="7942"/>
        </reference>
        <reference anchor="I-D.ietf-httpapi-idempotency-key-header" target="https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/">
          <front><title>The Idempotency-Key HTTP Header Field</title>
            <author initials="J." surname="Jena"/><author initials="S." surname="Dalal"/><date year="2025"/></front>
          <seriesInfo name="Internet-Draft" value="draft-ietf-httpapi-idempotency-key-header-07"/>
        </reference>
        <reference anchor="I-D.ietf-oauth-transaction-tokens" target="https://datatracker.ietf.org/doc/draft-ietf-oauth-transaction-tokens/">
          <front><title>Transaction Tokens</title>
            <author initials="A." surname="Tulshibagwale"/><author initials="G." surname="Fletcher"/><author initials="P." surname="Kasselman"/><date year="2026" month="July"/></front>
          <seriesInfo name="Internet-Draft" value="draft-ietf-oauth-transaction-tokens-11"/>
        </reference>
        <reference anchor="I-D.schrock-ep-authorization-receipts" target="https://datatracker.ietf.org/doc/draft-schrock-ep-authorization-receipts/">
          <front><title>Authorization Receipts for High-Risk Agent Actions</title>
            <author initials="I." surname="Schrock" fullname="Iman Schrock"><organization>EMILIA Protocol, Inc.</organization></author>
            <date year="2026" month="September"/></front>
          <seriesInfo name="Internet-Draft" value="draft-schrock-ep-authorization-receipts-13"/>
        </reference>
        <reference anchor="I-D.lee-orprg-permit-receipts" target="https://datatracker.ietf.org/doc/draft-lee-orprg-permit-receipts/">
          <front><title>Permit Receipts for Permit-Before-Commit Authorization of AI-Agent and Workload External Effects</title>
            <author initials="Y. B." surname="Lee"/><date year="2026" month="July"/></front>
          <seriesInfo name="Internet-Draft" value="draft-lee-orprg-permit-receipts-00"/>
        </reference>
        <reference anchor="I-D.toraman-noa-action-digest" target="https://datatracker.ietf.org/doc/draft-toraman-noa-action-digest/">
          <front><title>The NOA Action Digest: a Domain-Separated Correlation Value for Human-Approved Agent Actions</title>
            <author initials="T." surname="Toraman"/><date year="2026" month="August"/></front>
          <seriesInfo name="Internet-Draft" value="draft-toraman-noa-action-digest-01"/>
        </reference>
        <reference anchor="I-D.mih-agent-bilateral-attestation" target="https://datatracker.ietf.org/doc/draft-mih-agent-bilateral-attestation/">
          <front><title>Bilateral Attestation of Cross-Organization Agent Actions</title>
            <author initials="S." surname="Mih"/><date year="2026" month="September"/></front>
          <seriesInfo name="Internet-Draft" value="draft-mih-agent-bilateral-attestation-02"/>
        </reference>
        <reference anchor="I-D.munoz-scitt-permit-profile" target="https://datatracker.ietf.org/doc/draft-munoz-scitt-permit-profile/">
          <front><title>A SCITT Profile for Pre-Execution AI Action Authorization Records</title>
            <author initials="C." surname="Munoz"/><date year="2026" month="July"/></front>
          <seriesInfo name="Internet-Draft" value="draft-munoz-scitt-permit-profile-01"/>
        </reference>
        <reference anchor="I-D.munoz-wimse-authorization-evidence" target="https://datatracker.ietf.org/doc/draft-munoz-wimse-authorization-evidence/">
          <front><title>Signed Authorization-Evidence Records for WIMSE-Authorized AI Agent Actions</title>
            <author initials="C." surname="Munoz"/><date year="2026" month="July"/></front>
          <seriesInfo name="Internet-Draft" value="draft-munoz-wimse-authorization-evidence-01"/>
        </reference>
        <reference anchor="I-D.ruvalcaba-nhe-authz" target="https://datatracker.ietf.org/doc/draft-ruvalcaba-nhe-authz/">
          <front><title>NHE Backchannel Authorization: Graduated Autonomy and Intent-Scoped Credentials for Autonomous Agent Actions</title>
            <author initials="C. X." surname="Ruvalcaba"/><date year="2026" month="August"/></front>
          <seriesInfo name="Internet-Draft" value="draft-ruvalcaba-nhe-authz-00"/>
        </reference>
        <reference anchor="I-D.schrock-ep-bounded-capability-receipts" target="https://datatracker.ietf.org/doc/draft-schrock-ep-bounded-capability-receipts/">
          <front><title>Bounded Capability Receipts and Durable Spend Control for Agent Actions</title>
            <author initials="I." surname="Schrock"><organization>EMILIA Protocol, Inc.</organization></author><date year="2026" month="September"/></front>
          <seriesInfo name="Internet-Draft" value="draft-schrock-ep-bounded-capability-receipts-06"/>
        </reference>
        <reference anchor="I-D.sirkkavaara-vaara-receipt" target="https://datatracker.ietf.org/doc/draft-sirkkavaara-vaara-receipt/">
          <front><title>The Vaara Receipt: A Recomputable Receipt Format for Decisions About Autonomous Actions</title>
            <author surname="Sirkkavaara"/><date year="2026" month="September"/></front>
          <seriesInfo name="Internet-Draft" value="draft-sirkkavaara-vaara-receipt-11"/>
        </reference>
        <reference anchor="OIDFED" target="https://openid.net/specs/openid-federation-1_0.html">
          <front><title>OpenID Federation 1.0</title>
            <author><organization>OpenID Foundation</organization></author><date/></front>
        </reference>
        <reference anchor="SPIFFE" target="https://spiffe.io/docs/latest/spiffe-about/overview/">
          <front><title>Secure Production Identity Framework for Everyone (SPIFFE)</title>
            <author><organization>Cloud Native Computing Foundation</organization></author><date/></front>
        </reference>
      </references>
    </references>

    <section anchor="incident" numbered="true">
      <name>The Incident View (Informative)</name>
      <t>The form a record-reading tool should use when a bound-permit
      request fails, stating what is and is not known:</t>
      <artwork type="ascii-art"><![CDATA[
Action:           payments.transfer/1
First failure:    content-mismatch (step 7)
Established:      Permit bp-4f0c2a91d7e3 decided EUR 40.00 to
                  acme-gmbh. The recipient received a body whose
                  digest does not match. The recipient refused
                  before any effect, and signed the refusal.
Not established:  Which component changed the body.
                  Whether the cause was an attacker or a defect.
Evidence:         permit digest, request content digest,
                  signature base digest, recipient confirmation,
                  the sending stage's record
]]></artwork>
    </section>

    <section anchor="implstatus" numbered="true">
      <name>Implementation Status</name>
      <t>[Note to the RFC Editor: please remove this section before
      publication, as described in RFC 7942.]</t>
      <t>This section records the status of known implementations of this
      profile at the time of posting, following <xref target="RFC7942"/>.
      Listing an implementation here is not an endorsement.</t>
      <dl>
        <dt>Implementation:</dt>
        <dd>onedoor, module "onedoor.permit": the issuer and the standalone
        recipient check (<xref target="verify"/>).</dd>
        <dt>Source:</dt>
        <dd>https://github.com/shamiksaharcciit-oss/onedoor, commit
        38acd847372067d74fbc9ae99b8cab3843778406. Readers checking these
        results should build from that commit.</dd>
        <dt>Licence:</dt>
        <dd>Apache-2.0.</dd>
        <dt>Maturity:</dt>
        <dd>Reference implementation, written to test this profile. It is
        not a production service.</dd>
        <dt>Coverage:</dt>
        <dd>22 of the 24 conformance vectors in <xref target="vectors"/>
        are implemented, each with a passing test. The vector table is kept
        as data in "tests/permit/vectors/manifest.json", and
        "tests/permit/test_conformance_vectors.py" names the test for each
        vector and fails if a vector has no stated status.</dd>
        <dt>Not implemented:</dt>
        <dd>V17 ("stale-policy"): no status mechanism is implemented, so a
        superseded policy version cannot be detected. V22: the recipient
        confirmation (<xref target="confirmation"/>) is not implemented, so there is
        no presenter-side check. Both are stated in the implementation's own
        vector table.</dd>
        <dt>Result:</dt>
        <dd>At that commit, the permit test suite ("tests/permit") passes
        in full: 92 tests on CPython 3.12.</dd>
        <dt>Contact:</dt>
        <dd>The author.</dd>
      </dl>
    </section>

    <section anchor="openissues" numbered="true">
      <name>Open Issues</name>
      <ol>
        <li>Should this profile's action-type registry and a mandate format's
        action vocabulary be one registry, with each entry stating both
        derivations from one request?</li>
        <li>Should the recipient confirmation carry the mandate layer's
        verdict core digest in the form proposed here, or in a form that
        layer defines?</li>
        <li>Is recording the mandate status observed at decision time enough
        for a later reader to reconstruct why the reference was valid, or does
        a mandate format need to expose its own history?</li>
        <li>Should "policy_version" and the mandate reference share one
        currentness mechanism, since the window in which either can go stale
        is the same?</li>
        <li>Should "time-bounded" be permitted at all for action types above a
        risk threshold, or only declared?</li>
        <li>Should the recipient confirmation be required, rather than
        recommended, for refusals?</li>
      </ol>
    </section>
  </back>
</rfc>
