<?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-01"
     category="std"
     ipr="trust200902"
     submissionType="IETF"
     tocInclude="true"
     sortRefs="true"
     symRefs="true"
     version="3">

  <front>
    <title abbrev="AADP">The Agent Action Decision Protocol (AADP): Per-Action Authorization for AI Agents</title>
    <seriesInfo name="Internet-Draft" value="draft-saha-aadp-01"/>
    <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" month="August" day="20"/>
    <area>sec</area>
    <keyword>AI agents</keyword>
    <keyword>authorization</keyword>
    <keyword>policy</keyword>
    <keyword>guardrails</keyword>

    <abstract>
      <t>The Agent Action Decision Protocol (AADP) separates per-action
      authorization from an agent's identity and its standing capabilities,
      and gives that authorization semantics that a stateless tool permission
      or access grant cannot express: whether a specific proposed action,
      with specific argument values, may be performed now, given mutable
      state such as cumulative budgets, live reservations, approval lifecycle,
      and a kill switch. Existing agent-security work concentrates on identity
      -- who an agent is, what credentials it holds, and which tools it may
      reach; AADP addresses the complementary decision, and composes with
      that work rather than replacing it. This document defines a two-phase
      wire contract between a Policy Decision Point (PDP) that authorizes
      agent actions and the Policy Enforcement Points (PEPs) that perform
      them: verdicts with machine-readable reasons, obligations that fail
      closed, atomic budget reservation, an approval lifecycle, idempotency
      behavior, evidence sufficient to re-derive every verdict, and a set of
      evaluation invariants any conformant decision point must observe --
      including the rule that an irreversible action is never executed
      autonomously. The protocol is transport-agnostic and is designed so
      that decision points and enforcement points can be implemented
      independently, in different languages, by different parties.</t>
    </abstract>
  </front>

  <middle>

    <section anchor="intro">
      <name>Introduction</name>
      <t>AI agents increasingly perform consequential actions: moving money,
      modifying data, sending communications, executing code, and
      provisioning infrastructure. The frameworks that host these agents
      typically decide what an agent can do by controlling which tools are
      exposed to it. Recent analysis has shown that this conflates two
      distinct questions: capability (whether an agent can invoke a tool)
      and authorization (whether this call, with these argument values,
      should be performed now) <xref target="SCOPEGATE"/>. A tool that is
      legitimately exposed for one purpose can be invoked for another; an
      agent authorized to make one payment can attempt a second; and a
      generic capability such as an HTTP client can reach effects its name
      does not declare.</t>

      <t>Parallel work standardizes agent identity and credential flows
      <xref target="I-D.klrc-aiagent-auth"/>
      <xref target="I-D.ni-wimse-ai-agent-identity"/> and hardens the
      authorization of protocol connections <xref target="MCP"/>. That work
      answers who the agent is and what it may reach; the WIMSE
      applicability work states explicitly that whether an agent inherits
      its user's permissions is out of its scope. It deliberately leaves
      per-action concerns -- budget enforcement, obligation tracking,
      approval lifecycle -- as deployment-specific. AADP specifies exactly
      that remainder: a small, implementable contract for per-action
      authorization decisions, designed to compose with identity-layer
      standards rather than replace them.</t>
      <t>Closer to this document,
      <xref target="I-D.liu-agent-operation-authorization"/> authorizes
      individual operations delegated from a human principal, carrying
      policy as Rego, signed confirmation evidence, and a delegation chain
      across multiple agents. It addresses a different remainder: consent
      and delegation lineage. It does not constrain cumulative spend, and
      the approval it records is a binary confirmation rather than a
      lifecycle with expiry and re-evaluation. AADP is complementary to it:
      the state that must persist between calls -- budgets shared across
      differently-named tools, reservations that outlive a decision,
      approvals that can expire -- is what this document specifies, and it
      cannot be expressed by evaluating one call at a time however the
      policy is written.</t>

      <t>The Policy Decision Point / Policy Enforcement Point split, and the
      concept of obligations attached to a permission, originate in XACML
      <xref target="XACML"/>. AADP inherits that vocabulary deliberately and
      differs in what it standardizes: agent-action semantics that stateless
      policy evaluation cannot express -- stateful budget reservation shared
      across differently-named tools, autonomy tiers with reversibility as a
      precondition, a two-phase exchange separated by a durability boundary,
      and typed evidence sufficient to re-derive every verdict.</t>

      <t>The distinction from OAuth-style authorization
      <xref target="RFC6749"/> is the same one, stated in the vocabulary a
      reviewer is most likely to reach for. An access grant or a bearer or
      sender-constrained access token answers a standing question: may this
      principal access this resource under this scope. It is issued ahead of
      use, is coarse relative to a single call, and carries no notion of how
      much of a budget the principal has already spent this window or whether
      a human approval it once relied on has since expired. AADP answers a
      per-invocation question over the concrete action and its argument
      values, and the answer is a function of mutable authorization state --
      cumulative spend, live reservations, approval lifecycle, kill switch,
      and prior executions -- that a grant or token does not carry and cannot
      be made to carry without becoming a decision point itself. AADP is
      therefore best deployed beneath an identity and token layer, not in
      place of one: the token establishes who is acting and what they may
      reach; AADP decides whether this action proceeds now.</t>

      <section anchor="scope">
        <name>Scope</name>
        <t>AADP specifies: the request an enforcement point sends to ask
        permission; the answers a decision point may return; the obligation
        that accompanies a permission; the report that must follow an
        attempted action; the ordered evaluation invariants a conformant
        decision point must observe; and idempotency, approval, and failure
        semantics.</t>
        <t>AADP does not specify a policy language, a storage engine, an
        approval user interface, or a particular transport. It does specify
        the security properties the transport must provide
        (<xref target="transportsec"/>) and the trust boundary within which
        its guarantees hold (<xref target="trustmodel"/>). Policy composition
        -- tiers, effect labels, budgets, parameter bounds -- is internal to
        the decision point. Only the verdict and its reason cross the
        wire.</t>
      </section>

      <section anchor="trustmodel">
        <name>Trust Model</name>
        <t>AADP's guarantees hold within a single governed domain in which
        the PDP and its PEPs trust one another and communicate over a
        secured channel (<xref target="transportsec"/>). The PEP is the point
        at which an action actually happens; within the domain it is trusted
        to submit the action it intends to perform and to perform the action
        it was permitted. AADP does not, in this revision, defend against a
        compromised or malicious PEP that submits one action and performs
        another: such a PEP already possesses the ability to act, and no
        message from the PDP can constrain code that has decided to ignore
        it. What AADP does require is that no <em>unsupervised</em> path to a
        governed capability exists (<xref target="security"/>); a capability
        reachable without a PEP is outside the protocol's view.</t>
        <t>Two deployment shapes fall outside this trusted-domain model and
        are addressed as future work rather than by this revision. The first
        is an intermediary that terminates the secured channel -- a gateway,
        a service mesh sidecar, or a proxy that sees cleartext -- which is a
        credentialed party in the path rather than a passive relay. The
        second is a permit that must be honoured across a trust boundary,
        stored, or forwarded, rather than consumed on the same secured
        channel that issued it. Both require a permit that is a verifiable,
        action-bound capability in its own right: signed by the PDP, carrying
        a digest of the exact authorized action and its parameters, an
        expiry, and a binding to the PEP entitled to use it, so that an
        intermediary or holder can neither substitute the action nor transfer
        the permit. Such a permit is a compatible extension of the decide
        response and is a planned addition; deployments requiring it today
        <bcp14>MUST NOT</bcp14> assume the bearer permit of this revision
        provides it.</t>
      </section>

      <section anchor="reqlang">
        <name>Requirements Language</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>
      </section>
    </section>

    <section anchor="roles">
      <name>Roles</name>
      <dl>
        <dt>Policy Decision Point (PDP):</dt>
        <dd>Evaluates a proposed action and returns a verdict. Owns policy,
        budget state, approval state, and the evidence record. There is
        exactly one logical PDP per governed domain. A logical PDP
        <bcp14>MAY</bcp14> be realized by several replicas, provided they
        present linearizable semantics for every piece of authorization state
        that can affect a verdict -- budgets, reservations, approval state,
        and the kill switch. A replication scheme under which two replicas
        can independently reserve the same budget, or under which a kill
        switch engaged at one replica is not observed at another before it
        decides, is not conformant: it breaks the atomic-reservation
        invariant of <xref target="invariants"/>.</dd>
        <dt>Policy Enforcement Point (PEP):</dt>
        <dd>Sits wherever actions actually happen -- an agent-framework tool
        wrapper, a protocol proxy, an API gateway filter, a container or
        micro-VM supervisor, a workflow engine. A PEP <bcp14>MUST NOT</bcp14>
        perform a governed action without a permit, and <bcp14>MUST</bcp14>
        report the outcome of every permit it receives.</dd>
      </dl>
      <t>A deployment <bcp14>MAY</bcp14> have many PEPs consulting one PDP.
      Every PEP is bound by the same contract.</t>
    </section>

    <section anchor="model">
      <name>Protocol Model</name>

      <section anchor="tiers">
        <name>Autonomy Tiers</name>
        <t>Tiers are ordered integers; higher means less autonomy.</t>
        <table anchor="tiertable">
          <name>Autonomy Tiers</name>
          <thead>
            <tr><th>Tier</th><th>Name</th><th>Meaning</th></tr>
          </thead>
          <tbody>
            <tr><td>0</td><td>observe</td><td>read-only; no external effect</td></tr>
            <tr><td>1</td><td>auto</td><td>may execute automatically; MUST be reversible</td></tr>
            <tr><td>2</td><td>auto_capped</td><td>may execute automatically within budgets</td></tr>
            <tr><td>3</td><td>confirm</td><td>requires human approval before execution</td></tr>
          </tbody>
        </table>
        <t>A verdict carries both the nominal tier (configured for the
        action) and the effective tier (after escalation). When they differ,
        the action was escalated, and the reason code states why. PDPs
        <bcp14>MUST</bcp14> report both.</t>
      </section>

      <section anchor="verdicts">
        <name>Verdicts</name>
        <table anchor="verdicttable">
          <name>Verdicts and PEP Obligations</name>
          <thead>
            <tr><th>Verdict</th><th>Meaning</th><th>PEP obligation</th></tr>
          </thead>
          <tbody>
            <tr><td>permit</td><td>the action may proceed now</td>
                <td>execute, then report exactly once</td></tr>
            <tr><td>deny</td><td>the action must not proceed</td>
                <td>do not act; no report</td></tr>
            <tr><td>propose</td><td>a human must approve first</td>
                <td>do not act; surface the approval id</td></tr>
            <tr><td>dry_run</td><td>rehearsal only; no real effect</td>
                <td>do not act; MAY simulate</td></tr>
            <tr><td>observe</td><td>read classified as no-effect</td>
                <td>act freely; no report required</td></tr>
            <tr><td>replay</td><td>this request id was already decided</td>
                <td>honour the original verdict; do not act again</td></tr>
          </tbody>
        </table>
      </section>

      <section anchor="reasons">
        <name>Reason Codes</name>
        <t>Every verdict <bcp14>MUST</bcp14> carry exactly one
        machine-readable reason. Conformant PDPs <bcp14>MUST</bcp14> use
        the identifiers in <xref target="reasontable"/> where they apply,
        and <bcp14>MAY</bcp14> define additional codes under a vendor
        prefix ("x-" followed by a vendor token and a dot).</t>
        <table anchor="reasontable">
          <name>Reason Codes</name>
          <thead>
            <tr><th>Code</th><th>Meaning</th></tr>
          </thead>
          <tbody>
            <tr><td>passed</td><td>all checks satisfied</td></tr>
            <tr><td>kill_switch</td><td>the global stop is engaged</td></tr>
            <tr><td>default_deny</td><td>no policy exists for this action type</td></tr>
            <tr><td>bounds</td><td>a parameter violated its declared bounds</td></tr>
            <tr><td>budget_exhausted</td><td>a value or quantity budget is exhausted (detail in the "budget" object)</td></tr>
            <tr><td>rate_exhausted</td><td>a rate budget (events per window) is exhausted (detail in the "budget" object)</td></tr>
            <tr><td>dry_run</td><td>the action is in rehearsal mode</td></tr>
            <tr><td>tier_confirm</td><td>policy requires human approval</td></tr>
            <tr><td>effect_floor</td><td>an effect class imposes a stricter tier than the action's own</td></tr>
            <tr><td>no_compensating_command</td><td>the action is irreversible and cannot auto-execute</td></tr>
            <tr><td>observe</td><td>the action is a governed read</td></tr>
            <tr><td>malformed</td><td>the request could not be parsed or validated</td></tr>
          </tbody>
        </table>
        <t>The budget reason codes are deliberately currency- and
        unit-neutral: a budget may be denominated in a currency, in tokens,
        in API calls, or in any other countable dimension, and the protocol
        does not privilege one. A verdict carrying "budget_exhausted" or
        "rate_exhausted" <bcp14>SHOULD</bcp14> include a "budget" object
        naming the exhausted budget and the dimension it counts:</t>
        <sourcecode type="json"><![CDATA[
"budget": {
  "name": "payments.daily",
  "dimension": "EUR",
  "limit": "10000.00",
  "remaining": "0.00",
  "window": "24h"
}
]]></sourcecode>
        <t>"dimension" is an opaque unit label ("EUR", "tokens", "calls").
        Values counted in a currency or other divisible quantity are decimal
        strings, as elsewhere in this document. A recipient that does not
        recognize the dimension still learns that a budget was the cause and
        can surface the object verbatim.</t>
      </section>
    </section>

    <section anchor="exchange">
      <name>The Two-Phase Exchange</name>
      <t>AADP is a two-phase protocol. The phases are separated by a
      durability boundary because the act between them may be slow, remote,
      or fatal.</t>
      <artwork align="center"><![CDATA[
 PEP                              PDP
  |  decide(request)               |
  |------------------------------->|  evaluate, reserve budget,
  |                                |  record intent, COMMIT
  |<-------------------------------|
  |  permit(permit_id, obligations)|
  |                                |
 [ PEP performs the action ]       |
  |                                |
  |  report(permit_id, outcome)    |
  |------------------------------->|  record linked result, COMMIT
  |<-------------------------------|
]]></artwork>
      <t>A PEP that receives a permit <bcp14>MUST</bcp14> send exactly one
      report, whatever happened -- success, failure, timeout, or refusal to
      act. A PDP <bcp14>MUST</bcp14> treat a permit with no report as an
      unresolved intent and <bcp14>MUST NOT</bcp14> release its budget
      reservation implicitly.</t>
      <t>An unresolved intent therefore holds budget that no action ever
      spent, and a caller that requests permits and never reports can
      exhaust a budget at the cost of one decide call per permit, leaving
      no effect at the sink to detect. Reservations <bcp14>MUST</bcp14>
      therefore be reclaimable explicitly rather than only implicitly: a
      PDP that issues a permit carrying "execute_within"
      (<xref target="obligations"/>) <bcp14>MUST</bcp14>, once that
      deadline has passed with no report, append an expiry entry to the
      evidence record and release that permit's reservation. The release
      is an audited event, not a silent timeout, and the permit is void
      from that point. A PDP <bcp14>SHOULD</bcp14> attach "execute_within"
      to every permit that reserves budget; one that does not
      <bcp14>MUST</bcp14> document how reservations are otherwise
      reclaimed.</t>
      <t>Combining the two phases into a single call is not conformant: a
      decision point that waits for the act holds state across an operation
      it does not control.</t>
    </section>

    <section anchor="messages">
      <name>Messages</name>
      <t>All messages are JSON objects <xref target="RFC8259"/>. Timestamps
      are <xref target="RFC3339"/> in UTC. Monetary values are decimal
      strings (for example, "12.50"), never floating-point numbers.</t>

      <section anchor="decidereq">
        <name>Decide Request</name>
        <sourcecode type="json"><![CDATA[
{
  "protocol": "aadp/0.1",
  "request_id": "0a5f2c3e-1b7e-4f2a-9c1d-6b0f9e2a77c1",
  "action_type": "payments.transfer",
  "params": { "payee": "acme-gmbh", "amount_eur": "40.00" },
  "source": "llm",
  "rationale": "user asked to settle invoice 8841",
  "session_id": "sess-91f3",
  "cost_eur": "40.00",
  "created_at": "2026-08-17T09:14:02Z",
  "parent_id": null
}
]]></sourcecode>
        <t>Field requirements: "protocol", "request_id" (a UUID; the
        idempotency key, see <xref target="idem"/>), "action_type" (an
        opaque identifier; the PDP's policy key), "params" (a JSON object,
        possibly empty, treated as untrusted input), "source" (one of
        "scheduler", "rule", "llm", "ui", "undo", "system"), "rationale"
        (human-readable justification, recorded as evidence), and
        "created_at" are <bcp14>REQUIRED</bcp14>. "session_id" and
        "cost_eur" are <bcp14>RECOMMENDED</bcp14>; "cost_eur" is
        <bcp14>REQUIRED</bcp14> when value budgets apply. "parent_id"
        identifies the permit being reversed when "source" is "undo".
        "approval_ref" carries the "approval_id" of a granted approval when
        a PEP itself resumes a previously proposed action
        (<xref target="approvals"/>); it is <bcp14>REQUIRED</bcp14> in that
        case and <bcp14>MUST</bcp14> be absent otherwise. A PDP
        <bcp14>MUST</bcp14> verify that an "approval_ref" refers to an
        approval that is granted, unexpired, unconsumed, and raised for an
        action equivalent to the one now requested; if it does not, the
        request is evaluated as though no approval had been supplied.</t>
        <t>"source" is informational and self-asserted. A conformant PDP
        <bcp14>MUST NOT</bcp14> treat "source" as an authenticated authority
        assertion, and <bcp14>MUST NOT</bcp14> let its self-declared value
        change the verdict: an action's governance binds to the action, not
        to a claim about who proposed it, and a caller that could set
        "source" to escalate its own privilege would make the field an
        attack surface. Where provenance is genuinely authorization-relevant
        -- for example, distinguishing a trusted scheduler from an untrusted
        model -- it <bcp14>MUST</bcp14> be established by an authenticated
        channel or credential (<xref target="transportsec"/>) and
        represented as authorization context the policy evaluates, not read
        from this field.</t>
        <t>Evaluation is total with respect to its input. A PDP that
        receives a request it cannot parse or validate -- a missing
        required field, a value of the wrong type, a malformed identifier,
        an unparseable body -- <bcp14>MUST</bcp14> return a "deny" verdict
        with reason "malformed", and <bcp14>MUST NOT</bcp14> signal the
        failure only as a transport-level or runtime error. Fail-closed
        behavior is a property of the decision point; a PDP that raises
        instead of deciding delegates that property to each enforcement
        point, where it holds only by convention. Where the supplied
        "request_id" is itself well-formed the response
        <bcp14>SHOULD</bcp14> echo it; where it is not, the response
        <bcp14>MUST NOT</bcp14> invent one.</t>
        <t>This requirement covers invalid input, not internal failure. A
        PDP that cannot evaluate because its own policy store, evidence
        record or budget ledger is unavailable <bcp14>SHOULD NOT</bcp14>
        report that condition as a routine denial, since that makes a
        defect indistinguishable from a policy outcome; such a PDP is
        unreachable for this request, and <xref target="failure"/> governs
        what the enforcement point does next.</t>
      </section>

      <section anchor="decideresp">
        <name>Decide Response</name>
        <sourcecode type="json"><![CDATA[
{
  "protocol": "aadp/0.1",
  "request_id": "0a5f2c3e-1b7e-4f2a-9c1d-6b0f9e2a77c1",
  "verdict": "permit",
  "reason": "passed",
  "detail": "",
  "nominal_tier": 1,
  "effective_tier": 1,
  "permit_id": "int-88213",
  "obligations": [
    { "type": "report_result", "required": true },
    { "type": "undo_available_until",
      "value": "2026-08-17T09:29:02Z" }
  ],
  "approval_id": null,
  "evidence_id": "aud-88213"
}
]]></sourcecode>
        <t>"permit_id" is present if and only if the verdict is "permit".
        "approval_id" is present if and only if the verdict is "propose".
        "evidence_id" references the entry in the PDP's evidence record and
        <bcp14>SHOULD</bcp14> be present for every verdict.</t>
      </section>

      <section anchor="reportreq">
        <name>Report Request and Response</name>
        <sourcecode type="json"><![CDATA[
{
  "protocol": "aadp/0.1",
  "permit_id": "int-88213",
  "outcome": "success",
  "payload": { "confirmation": "TX-77120" },
  "error": null,
  "completed_at": "2026-08-17T09:14:03Z"
}
]]></sourcecode>
        <t>"outcome" <bcp14>MUST</bcp14> be one of "success", "failure",
        "timeout", or "not_attempted". "not_attempted" is the correct value
        when a PEP declines to act, for example because it cannot satisfy
        an obligation (<xref target="obligations"/>).</t>
        <sourcecode type="json"><![CDATA[
{ "protocol": "aadp/0.1", "accepted": true,
  "evidence_id": "aud-88214" }
]]></sourcecode>
      </section>
    </section>

    <section anchor="obligations">
      <name>Obligations</name>
      <t>An obligation is a condition attached to a permit that the PEP
      <bcp14>MUST</bcp14> satisfy for the permission to be valid.
      Obligations are typed and extensible; they are the protocol's
      extension point for enforcement capabilities the PDP itself cannot
      provide.</t>
      <table anchor="obltable">
        <name>Obligation Types</name>
        <thead>
          <tr><th>Type</th><th>Meaning</th></tr>
        </thead>
        <tbody>
          <tr><td>report_result</td><td>the PEP MUST send exactly one report (always implied)</td></tr>
          <tr><td>undo_available_until</td><td>a reversal window; the PEP SHOULD retain what is needed to reverse</td></tr>
          <tr><td>execute_within</td><td>a deadline after which the permit is void</td></tr>
          <tr><td>isolate</td><td>the action MUST be performed inside an isolation boundary meeting the stated profile</td></tr>
        </tbody>
      </table>
      <t>Unknown obligations fail closed. A PEP that receives an obligation
      whose type it does not recognize <bcp14>MUST NOT</bcp14> perform the
      action, and <bcp14>MUST</bcp14> report "not_attempted" with an error
      naming the unsupported type. A PEP <bcp14>MUST NOT</bcp14> silently
      ignore an obligation: a policy author has to be able to assume that
      an issued obligation was either honoured or visibly refused.</t>
      <t>Where an obligation admits evidence of its discharge (for example,
      the identity of the isolation boundary used), the PEP
      <bcp14>SHOULD</bcp14> include that evidence in its report payload, so
      that the discharge is recorded alongside the decision.</t>
      <t>Vendor-specific obligations <bcp14>MUST</bcp14> use a vendor
      prefix as in <xref target="reasons"/>.</t>
    </section>

    <section anchor="idem">
      <name>Idempotency, Replay, and Exactly-Once Semantics</name>
      <t>"request_id" is the idempotency key for the decision. A PDP that
      receives a decide request for a request_id it has already decided
      <bcp14>MUST</bcp14> return the original verdict with "verdict" and
      "reason" unchanged, and <bcp14>MUST NOT</bcp14> re-reserve any budget.
      Implementations <bcp14>MAY</bcp14> additionally set "replayed": true.</t>
      <t>A "permit_id" is single-use. A second report for the same permit_id
      <bcp14>MUST</bcp14> be rejected with "accepted": false.</t>
      <t>Resuming an approved action (<xref target="approvals"/>) is a new
      decision with a new request_id; reusing the proposed request's id
      would return the "propose" verdict forever.</t>
      <t>Three distinct "exactly once" properties must be kept apart, because
      AADP provides two of them and cannot provide the third alone:</t>
      <dl>
        <dt>At-most-once permit consumption:</dt>
        <dd>a permit authorizes one attempt. The single-use rules above
        guarantee it.</dd>
        <dt>Exactly-once decision and report state:</dt>
        <dd>each request reaches exactly one recorded verdict, and each
        permit exactly one recorded resolution. The idempotency key and the
        durable evidence record (<xref target="evidence"/>,
        <xref target="invariants"/>) guarantee it.</dd>
        <dt>Exactly-once external effect:</dt>
        <dd>the governed action takes effect on the target resource at most
        once. AADP cannot guarantee this on its own, because
        the effect happens at the target, outside the protocol.</dd>
      </dl>
      <t>The gap is the interval in which a PEP has issued the action to the
      target but has not recorded its outcome. If the PEP fails there and
      recovers, it holds a permit whose effect is unknown: the transfer may
      or may not have completed. A PEP <bcp14>MUST NOT</bcp14> re-issue the
      action -- under this permit, a replayed decision, or a fresh one --
      unless it can positively establish that the action did not take effect.
      Where it cannot, it <bcp14>MUST</bcp14> report the outcome as "timeout"
      (the honest value for "attempted, result unknown") and leave
      reconciliation to the target system or an operator, rather than risk a
      duplicate effect. Blind retry of an uncertain action is the most likely
      way a governed system performs a consequential action twice.</t>
      <t>To narrow the gap, a PEP <bcp14>SHOULD</bcp14>, where the target
      resource accepts an idempotency or client-reference key, derive that
      key deterministically from the permit (for example, from "permit_id")
      and pass it to the target, so that a retried or duplicated downstream
      call collapses to a single effect at the target. AADP standardizes the
      authorization exchange; exactly-once effect is achieved only when the
      target participates, and this is the mechanism by which it does.</t>
    </section>

    <section anchor="approvals">
      <name>Approvals</name>
      <t>When the verdict is "propose", the PDP creates an approval record
      with a deadline and returns "approval_id". Approval state transitions
      are pending to approved, denied, or expired.</t>
      <t>A conformant PDP <bcp14>MUST</bcp14>: transition pending to
      approved atomically, rejecting a second approval and any approval
      after the deadline; record who approved and when; and re-evaluate the
      action on resumption. An approval authorizes the action, not the
      outcome: a kill switch engaged between approval and resumption
      <bcp14>MUST</bcp14> still deny.</t>
      <t>A granted approval is single-use: the PDP <bcp14>MUST</bcp14>
      mark it consumed when a resumed request is decided against it, so
      that one human decision cannot authorize repeated actions.</t>
      <t>Resumption may be driven from either side, and both forms are
      conformant provided the guarantees above hold. In PEP-driven
      resumption, the PEP issues a fresh decide request carrying
      "approval_ref". In PDP-driven resumption, granting the approval is
      itself the resumption: the PDP re-evaluates the recorded request
      under a fresh request_id and returns a decide response. A PDP
      <bcp14>MUST</bcp14> document which form it implements, and
      <bcp14>MUST NOT</bcp14> let either form bypass re-evaluation.</t>
      <t>Approval transport -- who is notified, and how -- is out of
      scope.</t>
    </section>

    <section anchor="invariants">
      <name>Evaluation Invariants</name>
      <t>This section is the normative core. AADP is a stateful protocol:
      a verdict depends not only on the request and the policy but on
      authorization state -- cumulative budgets, live reservations, approval
      records, and the kill switch -- that changes over time. The
      determinism the protocol requires is therefore determinism over the
      full evaluation input. Two conformant PDPs evaluating the same request
      against the same policy and the same authorization state, at the same
      evaluation time, <bcp14>MUST</bcp14> reach the same verdict; equally,
      the same request may be permitted at one moment and denied moments
      later once a budget is spent or a kill switch is engaged, with no
      inconsistency. The evidence record (<xref target="evidence"/>)
      preserves the policy and the request; a record intended to re-derive a
      past verdict <bcp14>MUST</bcp14> also capture the authorization state
      the verdict was evaluated against, since without it the verdict is not
      reproducible. Implementations <bcp14>MAY</bcp14> optimize freely --
      caching, compiling, reordering pure checks -- provided these
      observable orderings hold.</t>
      <ol>
        <li>The kill switch is evaluated first, before policy lookup. An
        engaged kill switch <bcp14>MUST NOT</bcp14> be overridable by a
        policy entry.</li>
        <li>Unknown actions are denied. Absence of policy is a denial with
        reason "default_deny", never a permission.</li>
        <li>Irreversibility escalates. An action at tier "auto" with no
        registered means of reversal <bcp14>MUST NOT</bcp14> auto-execute;
        it escalates to "confirm" with reason
        "no_compensating_command".</li>
        <li>Bounds are validated before proposal. A human <bcp14>MUST
        NOT</bcp14> be asked to approve an action whose parameters already
        violate policy.</li>
        <li>Rehearsals do not spend. If an action is in dry-run, the PDP
        <bcp14>MUST NOT</bcp14> reserve any budget.</li>
        <li>Budget reservation is atomic and all-or-nothing. Where an
        action draws on several budgets, all <bcp14>MUST</bcp14> be checked
        before any is reserved, and a failure on any <bcp14>MUST</bcp14>
        leave every counter untouched. Concurrent requests <bcp14>MUST
        NOT</bcp14> be able to over-reserve a budget.</li>
        <li>Escalation is recorded, not hidden. When effective tier exceeds
        nominal tier, the reason <bcp14>MUST</bcp14> identify the
        escalating check.</li>
        <li>Reversal is governed. An undo is itself an action subject to
        the full pipeline. There is no privileged path.</li>
        <li>Intent precedes action. The evidence record of a permit
        <bcp14>MUST</bcp14> be durable before the permit is returned. A
        crash between phases <bcp14>MUST</bcp14> leave a recoverable,
        detectable state: an intent with no result.</li>
      </ol>
    </section>

    <section anchor="evidence">
      <name>Evidence</name>
      <t>A conformant PDP <bcp14>MUST</bcp14> maintain an append-only
      record in which each entry carries at minimum: the request, the
      verdict, the reason code, both tiers, a timestamp, and -- for results
      -- a link to the intent it resolves.</t>
      <t>Entries <bcp14>MUST NOT</bcp14> be updated or deleted. Corrections
      are appended, never applied in place. Implementations
      <bcp14>SHOULD</bcp14> enforce this structurally (storage-level
      constraints), not only by convention.</t>
      <t>The record <bcp14>MUST</bcp14> be sufficient to re-derive every
      verdict it contains, given the policy version in force at the time.
      A PDP <bcp14>MUST</bcp14> therefore identify, on every entry, the
      policy that produced it, and <bcp14>MUST</bcp14> retain that policy
      for as long as the entries referring to it are retained. Identifying
      the policy by a content digest of its normative content is
      <bcp14>RECOMMENDED</bcp14>, so that an unchanged policy keeps one
      identity and a reverted change is recognisable as a return to an
      earlier one rather than as a third state.</t>
      <t>This requirement exists because policy is usually stored mutably.
      A decision point that overwrites policy in place, and records only
      the verdict, has an evidence record that cannot be checked: it
      cannot show that a permit issued last week was correct under last
      week's rules, and an operator who loosens policy, acts, and restores
      it leaves nothing behind. Retaining superseded policy is what makes
      the rest of this section meaningful.</t>
    </section>

    <section anchor="failure">
      <name>Failure Semantics</name>
      <t>If a PEP cannot reach the PDP, its behavior is a deployment policy
      that <bcp14>MUST</bcp14> be configured explicitly, not defaulted
      silently:</t>
      <dl>
        <dt>fail_closed (RECOMMENDED):</dt>
        <dd>perform no governed action.</dd>
        <dt>fail_static:</dt>
        <dd>permit only actions on a locally cached list of statically
        decidable, unbudgeted, tier-0/1 permissions, and queue evidence for
        later delivery.</dd>
        <dt>fail_open:</dt>
        <dd>perform the action. Conformant implementations <bcp14>MUST
        NOT</bcp14> make this the default and <bcp14>SHOULD</bcp14> require
        it to be named in configuration.</dd>
      </dl>
      <t>A PEP operating under fail_static <bcp14>MUST</bcp14> deliver
      queued evidence when the PDP becomes reachable, and <bcp14>MUST
      NOT</bcp14> locally permit any action that would require a budget, an
      approval, or an obligation.</t>
    </section>

    <section anchor="transport">
      <name>Transport Bindings</name>
      <t>The core protocol is transport-agnostic. This section states the
      security properties every binding <bcp14>MUST</bcp14> provide, then
      defines two concrete bindings.</t>

      <section anchor="transportsec">
        <name>Transport Security</name>
        <t>The channel between a PEP and a PDP carries requests whose
        integrity determines a verdict, verdicts a PEP acts on, and permits
        that are bearer capabilities: possession of a permit is, in this
        revision, sufficient to act. The channel <bcp14>MUST</bcp14>
        therefore provide confidentiality, integrity, and mutual
        authentication of both endpoints. Mutually-authenticated TLS
        following the recommendations of <xref target="RFC9325"/>
        satisfies this requirement and is <bcp14>RECOMMENDED</bcp14>; the
        local socket binding (<xref target="uds"/>) satisfies it by
        operating-system peer authentication on a single host. A permit or a
        role credential <bcp14>MUST NOT</bcp14> be transmitted over a channel
        that does not meet this requirement.</t>
        <t>This closes the man-in-the-middle exposure that the protocol
        otherwise carries. Without endpoint authentication and integrity, an
        attacker on the path can rewrite a decide request before the PDP
        evaluates it (so that a valid permit is issued for an action the
        agent never proposed), flip a "deny" into a "permit" on the return
        path, or capture a bearer permit and replay it. None of these is
        addressed by any property of the permit itself; all are addressed by
        securing the channel, which is why this requirement is normative
        rather than left to deployment.</t>
        <t>Because the permit is a bearer capability, an implementation
        <bcp14>SHOULD</bcp14> make it sender-constrained: bound to the
        authenticated identity or key of the PEP to which it was issued -- for
        example by the certificate-bound-token mechanism of
        <xref target="RFC8705"/> or the proof-of-possession mechanism of
        <xref target="RFC9449"/> -- so that a permit captured or leaked from
        one PEP cannot be used by another party. Sender-constraint reduces
        the protocol's reliance on channel confidentiality alone and is the
        recommended defence against permit theft; the action-bound signed
        permit of <xref target="trustmodel"/> is the stronger measure for
        deployments that must also survive a terminating intermediary.</t>
      </section>

      <section anchor="http">
        <name>HTTP/JSON Binding</name>
        <table anchor="httptable">
          <name>HTTP/JSON Operations</name>
          <thead>
            <tr><th>Operation</th><th>Method and path</th></tr>
          </thead>
          <tbody>
            <tr><td>decide</td><td>POST /v1/decide</td></tr>
            <tr><td>report</td><td>POST /v1/report</td></tr>
            <tr><td>list approvals</td><td>GET /v1/approvals</td></tr>
            <tr><td>approve / deny</td><td>POST /v1/approvals/{id}/approve, POST /v1/approvals/{id}/deny</td></tr>
            <tr><td>kill switch</td><td>POST /v1/killswitch</td></tr>
            <tr><td>health</td><td>GET /v1/health</td></tr>
          </tbody>
        </table>
        <t>The HTTP binding <bcp14>MUST</bcp14> run over a channel meeting
        <xref target="transportsec"/>. Caller authorization is bearer-token
        by default, with at minimum two roles: a decide role (decide, report)
        and an admin role (approve, deny, kill switch, policy reload).
        Separation of duties is a governance property and
        <bcp14>MUST NOT</bcp14> be collapsed into a single credential.
        Deployments <bcp14>MAY</bcp14> substitute identity-layer mechanisms
        such as those of <xref target="I-D.klrc-aiagent-auth"/>, and
        <bcp14>SHOULD</bcp14> prefer sender-constrained credentials and
        permits (<xref target="transportsec"/>) over plain bearer tokens.</t>
        <t>HTTP status is 200 for any evaluated verdict, including "deny":
        a denial is a successful evaluation, not an error. 4xx and 5xx are
        reserved for protocol and infrastructure failures, which a PEP
        <bcp14>MUST</bcp14> treat under <xref target="failure"/>.</t>
      </section>
      <section anchor="uds">
        <name>Local Socket Binding</name>
        <t>Implementations <bcp14>SHOULD</bcp14> offer a Unix domain socket
        binding carrying the same messages, for enforcement points on the
        same host. Peer credentials <bcp14>MAY</bcp14> be used in place of
        bearer tokens.</t>
      </section>
    </section>

    <section anchor="conformance">
      <name>Conformance</name>
      <t>A conformant PDP returns a verdict for every request it accepts,
      including one it cannot parse (<xref target="decidereq"/>). It
      implements the messages of <xref target="messages"/>, the invariants of
      <xref target="invariants"/>, the idempotency rules of
      <xref target="idem"/>, the approval semantics of
      <xref target="approvals"/>, and the evidence requirements of
      <xref target="evidence"/>.</t>
      <t>A conformant PEP never performs a governed action without a
      permit; sends exactly one report per permit; fails closed on unknown
      obligations; and has an explicitly configured unreachability
      behavior.</t>
      <t>A minimal PEP <bcp14>MAY</bcp14> implement only decide and report
      and refuse any verdict other than "permit" and "deny". It
      <bcp14>MUST</bcp14> then treat "propose" and "dry_run" as refusals to
      act and report "not_attempted".</t>
    </section>

    <section anchor="versioning">
      <name>Versioning</name>
      <t>The "protocol" field carries "aadp/MAJOR.MINOR". Minor versions add
      optional fields, reason codes, and obligation types; a recipient
      <bcp14>MUST</bcp14> ignore unknown fields. Unknown obligations and
      unknown verdicts are the exception: both <bcp14>MUST</bcp14> fail
      closed. Major versions may change required semantics.</t>
    </section>

    <section anchor="iana">
      <name>IANA Considerations</name>
      <t>This document requests the creation of an "Agent Action Decision
      Protocol (AADP)" registry group with three registries, each operating
      under the Specification Required policy <xref target="RFC8126"/>.
      Names beginning with "x-" are reserved for vendor use and are not
      registered.</t>
      <t>Registration requests for all three registries use the same
      template: Name (the wire identifier), Description (one sentence),
      Change Controller, and Reference (the specification defining the
      semantics). For the Obligation Types registry, two further fields
      are required: Value Syntax, and Discharge Evidence (what a PEP
      <bcp14>MUST</bcp14> supply in a report to demonstrate the obligation
      was met). The designated expert should confirm that the semantics
      are implementable by a PEP without access to PDP-internal state, and
      that no existing entry already expresses them.</t>

      <section>
        <name>AADP Verdicts Registry</name>
        <t>Initial contents are the six verdicts defined in
        <xref target="verdicts"/>: "permit", "deny", "propose", "dry_run",
        "observe", and "replay". For each, the Reference is this document,
        the Change Controller is the IETF, and the Description is the
        corresponding "Meaning" cell of
        <xref target="verdicttable"/>.</t>
        <t>A new verdict changes what a conformant PEP must do on receipt.
        The designated expert <bcp14>MUST NOT</bcp14> approve a
        registration whose PEP obligation is not stated, and
        <bcp14>SHOULD</bcp14> reject a verdict whose behavior an existing
        verdict plus a reason code already expresses.</t>
      </section>
      <section>
        <name>AADP Reason Codes Registry</name>
        <t>Initial contents are the twelve reason codes defined in
        <xref target="reasons"/>: "passed", "kill_switch", "default_deny",
        "bounds", "budget_exhausted", "rate_exhausted", "dry_run",
        "tier_confirm", "effect_floor", "no_compensating_command",
        "observe", and "malformed". For each, the Reference
        is this document, the Change Controller is the IETF, and the
        Description is the corresponding "Meaning" cell of
        <xref target="reasontable"/>.</t>
        <t>Reason codes are the audit vocabulary: a registration
        <bcp14>SHOULD</bcp14> state which verdicts it may accompany.</t>
      </section>
      <section>
        <name>AADP Obligation Types Registry</name>
        <t>Initial contents are the four obligation types defined in
        <xref target="obligations"/>: "report_result",
        "undo_available_until", "execute_within", and "isolate". For each,
        the Reference is this document and the Change Controller is the
        IETF.</t>
        <t>Because an unrecognized obligation type causes a conformant PEP
        to decline the action (<xref target="obligations"/>), registration
        in this registry is the only way an obligation becomes usable
        across independent implementations. The designated expert
        <bcp14>MUST</bcp14> verify that the Discharge Evidence field
        describes something a PEP can actually produce and a PDP can
        actually check.</t>
      </section>
    </section>

    <section anchor="security">
      <name>Security Considerations</name>
      <t>Parameters are untrusted input, commonly authored by a language
      model that may itself be under prompt-injection influence. PDPs
      <bcp14>MUST</bcp14> validate parameters against declared bounds and
      <bcp14>SHOULD</bcp14> reject unknown parameters rather than pass them
      through. The "rationale" field is model-authored text in the common
      case; it is evidence, never input to the decision.</t>
      <t>AADP mitigates a class of confused-deputy failures
      <xref target="SCOPEGATE"/> by evaluating argument values per call
      rather than gating tool exposure. It does not defend against a PEP
      that can act without consulting the PDP: a capability reachable
      outside the governed perimeter is outside this protocol's ability to
      detect. Deployments <bcp14>SHOULD</bcp14> verify that no unsupervised
      path to a governed capability exists. The boundary within which AADP's
      guarantees hold, and the compromised-PEP and terminating-intermediary
      cases they do not cover, are stated in <xref target="trustmodel"/>.</t>

      <t>The PDP-PEP channel is the protocol's principal network attack
      surface. An attacker able to read, alter, or inject on it can rewrite a
      request before evaluation, forge or flip a verdict, or replay a
      captured bearer permit; <xref target="transportsec"/> makes
      confidentiality, integrity, mutual authentication, and (recommended)
      sender-constrained permits normative precisely to close this, and its
      requirements are load-bearing for every guarantee in this document.</t>
      <t>The interval between a permit and its execution is a
      time-of-check/time-of-use window. The "execute_within" obligation
      bounds it; deployments with strict requirements <bcp14>SHOULD</bcp14>
      issue it. A revoked or expired permit discovered at execution time is
      reported "not_attempted". Budget state cannot drift within this window,
      because reservation is taken at decision time
      (<xref target="invariants"/>), not at execution; and an approval cannot
      outlive a kill switch, because a resumed action is re-evaluated
      (<xref target="approvals"/>). The residual window is the effect gap of
      <xref target="idem"/>, governed there.</t>

      <t>Reversibility, in this protocol, is an operational property, not a
      guarantee about the world. An action counts as reversible when a
      compensating path is registered and is itself governed
      (<xref target="invariants"/>, invariant 3); it does not follow that the
      external effect can always be undone. A compensating transfer may
      arrive after a counterparty has moved the funds, a correcting message
      cannot unsend the first, and a rollback cannot recall data already
      read. Compensation is therefore best-effort recovery, and the
      "irreversible action is never executed autonomously" rule exists
      precisely because the alternative -- trusting that an undo will restore
      the prior state -- is not sound for consequential effects. Policy
      authors <bcp14>SHOULD NOT</bcp14> classify an action as reversible on
      the strength of a compensating command whose success depends on a party
      the deployment does not control.</t>
      <t>The approval queue is a denial-of-service surface: an agent that
      can flood proposals can bury a human approver. Rate caps on
      proposal-generating action types, and approval TTLs, bound the
      queue.</t>
      <t>Budget reservation is a second denial-of-service surface, and a
      quieter one. A caller that requests permits and never reports
      consumes the reserved budget without performing any action, so
      nothing appears at the sink and nothing fails; legitimate work is
      simply denied for the rest of the window. The explicit reclamation
      required in <xref target="exchange"/> bounds the exposure to the
      "execute_within" deadline, which is why that obligation is
      <bcp14>RECOMMENDED</bcp14> on every budgeted permit rather than
      reserved for time-critical actions.</t>
      <t>The evidence record may contain sensitive parameter values.
      Implementations <bcp14>SHOULD</bcp14> support field-level redaction
      at write time and <bcp14>MUST NOT</bcp14> achieve redaction by
      mutating existing entries. Access to the evidence record
      <bcp14>SHOULD</bcp14> be governed no less strictly than the actions
      it records.</t>
      <t>Monetary values as decimal strings avoid floating-point rounding
      as an attack surface on budget arithmetic.</t>
    </section>

  </middle>

  <back>
    <references>
      <name>References</name>
      <references>
        <name>Normative References</name>
        <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="RFC3339" target="https://www.rfc-editor.org/info/rfc3339">
          <front>
            <title>Date and Time on the Internet: Timestamps</title>
            <author initials="G." surname="Klyne" fullname="G. Klyne"/>
            <author initials="C." surname="Newman" fullname="C. Newman"/>
            <date year="2002" month="July"/>
          </front>
          <seriesInfo name="RFC" value="3339"/>
        </reference>
        <reference anchor="RFC8259" target="https://www.rfc-editor.org/info/rfc8259">
          <front>
            <title>The JavaScript Object Notation (JSON) Data Interchange Format</title>
            <author initials="T." surname="Bray" fullname="T. Bray" role="editor"/>
            <date year="2017" month="December"/>
          </front>
          <seriesInfo name="STD" value="90"/>
          <seriesInfo name="RFC" value="8259"/>
        </reference>
        <reference anchor="RFC8126" target="https://www.rfc-editor.org/info/rfc8126">
          <front>
            <title>Guidelines for Writing an IANA Considerations Section in RFCs</title>
            <author initials="M." surname="Cotton" fullname="M. Cotton"/>
            <author initials="B." surname="Leiba" fullname="B. Leiba"/>
            <author initials="T." surname="Narten" fullname="T. Narten"/>
            <date year="2017" month="June"/>
          </front>
          <seriesInfo name="BCP" value="26"/>
          <seriesInfo name="RFC" value="8126"/>
        </reference>
        <reference anchor="RFC9325" target="https://www.rfc-editor.org/info/rfc9325">
          <front>
            <title>Recommendations for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)</title>
            <author initials="Y." surname="Sheffer" fullname="Y. Sheffer"/>
            <author initials="P." surname="Saint-Andre" fullname="P. Saint-Andre"/>
            <author initials="T." surname="Fossati" fullname="T. Fossati"/>
            <date year="2022" month="November"/>
          </front>
          <seriesInfo name="BCP" value="195"/>
          <seriesInfo name="RFC" value="9325"/>
        </reference>
      </references>
      <references>
        <name>Informative References</name>
        <reference anchor="RFC6749" target="https://www.rfc-editor.org/info/rfc6749">
          <front>
            <title>The OAuth 2.0 Authorization Framework</title>
            <author initials="D." surname="Hardt" fullname="D. Hardt" role="editor"/>
            <date year="2012" month="October"/>
          </front>
          <seriesInfo name="RFC" value="6749"/>
        </reference>
        <reference anchor="RFC8705" target="https://www.rfc-editor.org/info/rfc8705">
          <front>
            <title>OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens</title>
            <author initials="B." surname="Campbell" fullname="B. Campbell"/>
            <author initials="J." surname="Bradley" fullname="J. Bradley"/>
            <author initials="N." surname="Sakimura" fullname="N. Sakimura"/>
            <author initials="T." surname="Lodderstedt" fullname="T. Lodderstedt"/>
            <date year="2020" month="February"/>
          </front>
          <seriesInfo name="RFC" value="8705"/>
        </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" fullname="D. Fett"/>
            <author initials="B." surname="Campbell" fullname="B. Campbell"/>
            <author initials="J." surname="Bradley" fullname="J. Bradley"/>
            <author initials="T." surname="Lodderstedt" fullname="T. Lodderstedt"/>
            <author initials="M." surname="Jones" fullname="M. Jones"/>
            <author initials="D." surname="Waite" fullname="D. Waite"/>
            <date year="2023" month="September"/>
          </front>
          <seriesInfo name="RFC" value="9449"/>
        </reference>
        <reference anchor="I-D.klrc-aiagent-auth" target="https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/">
          <front>
            <title>AI Agent Authentication and Authorization</title>
            <author><organization>IETF Individual Submission</organization></author>
            <date year="2026"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-klrc-aiagent-auth-03"/>
        </reference>
        <reference anchor="SCOPEGATE" target="https://arxiv.org/abs/2606.28679">
          <front>
            <title>Capability Gates Are Not Authorization: Confused-Deputy Failures in LLM Agent Frameworks</title>
            <author initials="D." surname="Mellafe Zuvic" fullname="David Mellafe Zuvic"/>
            <date year="2026" month="June"/>
          </front>
          <seriesInfo name="arXiv" value="2606.28679"/>
        </reference>
        <reference anchor="XACML" target="https://docs.oasis-open.org/xacml/3.0/xacml-3.0-core-spec-os-en.html">
          <front>
            <title>eXtensible Access Control Markup Language (XACML) Version 3.0</title>
            <author><organization>OASIS</organization></author>
            <date year="2013" month="January"/>
          </front>
        </reference>
        <reference anchor="I-D.liu-agent-operation-authorization" target="https://datatracker.ietf.org/doc/draft-liu-agent-operation-authorization/">
          <front>
            <title>Agent Operation Authorization</title>
            <author><organization>IETF Individual Submission</organization></author>
            <date year="2026" month="March"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-liu-agent-operation-authorization-02"/>
        </reference>
        <reference anchor="I-D.ni-wimse-ai-agent-identity" target="https://datatracker.ietf.org/doc/draft-ni-wimse-ai-agent-identity/">
          <front>
            <title>WIMSE Applicability for AI Agents</title>
            <author><organization>IETF Individual Submission</organization></author>
            <date year="2026" month="February"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-ni-wimse-ai-agent-identity-02"/>
        </reference>
        <reference anchor="MCP" target="https://modelcontextprotocol.io/specification">
          <front>
            <title>Model Context Protocol Specification</title>
            <author><organization>MCP Project</organization></author>
            <date year="2026"/>
          </front>
        </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" fullname="Y. Sheffer"/>
            <author initials="A." surname="Farrel" fullname="A. Farrel"/>
            <date year="2016" month="February"/>
          </front>
          <seriesInfo name="BCP" value="205"/>
          <seriesInfo name="RFC" value="7942"/>
        </reference>
      </references>
    </references>

    <section anchor="example">
      <name>Worked Exchange (Tier 3)</name>
      <artwork><![CDATA[
-> decide  { request_id: A, action_type: "payments.transfer",
             cost_eur: "4000.00" }
<- propose { reason: "effect_floor", nominal_tier: 2,
             effective_tier: 3, approval_id: "apr-77" }

   [ human approves apr-77 ]

-> decide  { request_id: B, ..., approval_ref: "apr-77" }
<- permit  { permit_id: "int-88220",
             obligations: [ {type:"report_result"},
               {type:"execute_within",
                value:"2026-08-17T09:20:00Z"} ] }

   [ PEP performs the transfer ]

-> report  { permit_id: "int-88220", outcome: "success" }
<- accepted { evidence_id: "aud-88221" }
]]></artwork>
    </section>

    <section anchor="implstatus">
      <name>Implementation Status</name>
      <t>This section records the status of known implementations, per the
      practice described in <xref target="RFC7942"/>; it is to be removed
      before publication as an RFC.</t>
      <t>onedoor (Apache-2.0, Python, version 0.3.5 on PyPI at the time of
      writing) is the reference implementation of the PDP, with a test
      suite covering the invariants of <xref target="invariants"/>. Its
      decision service exposes the HTTP operations of
      <xref target="http"/>.</t>
      <t>Five enforcement points have been written against it, at differing
      levels of maturity. Three are packaged with the implementation: an
      in-process library binding, a Model Context Protocol stdio proxy, and a
      LangChain agent middleware that governs every tool an agent holds
      through the framework's wrap_tool_call hook, verified by a test suite
      that runs a real agent loop including graph-interrupt approval and the
      asynchronous path. Two are experimental examples rather than supported
      adapters: a LiteLLM gateway guardrail and a LangGraph tool wrapper with
      interrupt-based approval flow. The LiteLLM example is known to
      deviate from <xref target="exchange"/> in that it reports on issue of
      the permit rather than after the act; it is therefore not conformant
      as written, and is included here as evidence that the gateway hook
      point is viable, not as a conformant PEP.</t>
      <t>onedoor records policy provenance as required by
      <xref target="evidence"/>: the policy set is content-hashed, retained
      in an append-only table, and the hash is stamped on every evidence
      entry. This was added after the requirement was written. Its cost, from
      interleaved A/B runs on one host, is roughly 4 per cent of a
      decide-and-report cycle -- around 0.02 ms on a cycle of about 0.49 ms.
      Single-run figures at this scale are not distinguishable from run-to-run
      variation, which is itself of that order, so the figure is offered as an
      order of magnitude and not as a benchmark.</t>
      <t>Its evidence writes are split along the durability boundary this
      document draws: the intent row is always written synchronously before
      the permit is returned, while result rows may optionally be batched.
      Batching results costs nothing this document requires -- a lost result
      leaves an intent with no result, which is the recoverable state
      <xref target="invariants"/> already mandates -- and on one host it
      raised throughput from roughly 1,940 to 2,840 decide-and-report cycles
      per second, saturating at a batch of about 16. Exactly-once reporting
      is preserved by checking the pending batch as well as the record.</t>
      <t>onedoor now implements the explicit reservation reclamation
      required by <xref target="exchange"/>. A permit that reserves budget
      records the exact counter deltas and a deadline; once the deadline
      passes with no report, the reserved budget is subtracted back out and
      a "reservation_expired" row is appended to the audit log, voiding the
      permit. Reclamation runs lazily on each decide (the open-reservation
      lookup is indexed and normally empty) and is also exposed as an
      explicit call for a maintenance loop. The reference implementation
      carries a test suite for it: an unreported permit holds its budget,
      the reclaimer releases it as an audited event once the deadline
      passes, a reported permit is never reclaimed, and a double sweep
      cannot drive a counter negative. In an earlier revision of this
      document this requirement was listed as unimplemented; it is now
      satisfied.</t>
      <t>onedoor implements PDP-driven resumption
      (<xref target="approvals"/>): its approve operation re-evaluates the
      recorded request under a fresh request identifier and returns a
      decide response. It does not currently accept "approval_ref" on a
      decide request; PEP-driven resumption is therefore specified but not
      yet exercised by an implementation.</t>
      <t>Several requirements introduced in this revision are not yet
      exercised by the reference implementation: sender-constrained permits
      and the transport-security profile of <xref target="transportsec"/>
      are deployment concerns the example enforcement points do not yet
      configure; the downstream idempotency-key propagation of
      <xref target="idem"/> is implemented by no adapter; and the structured
      "budget" object of <xref target="reasons"/> replaces the currency-named
      reason codes the implementation currently emits. These are recorded as
      open gaps in the same spirit as the reservation-reclamation gap
      above.</t>
    </section>

    <section anchor="changes">
      <name>Changes from draft-saha-aadp-00</name>
      <t>This section is to be removed before publication as an RFC.</t>
      <ul>
        <li>Added a Trust Model section (<xref target="trustmodel"/>) stating
        the trusted PDP-PEP domain within which the protocol's guarantees
        hold, and naming the compromised-PEP, terminating-intermediary, and
        cross-boundary-permit cases as out of scope for this revision, with
        an action-bound signed permit identified as the planned extension for
        them.</li>
        <li>Added a normative Transport Security section
        (<xref target="transportsec"/>): the PDP-PEP channel MUST provide
        confidentiality, integrity, and mutual authentication; permits and
        role credentials MUST NOT cross an unsecured channel; permits SHOULD
        be sender-constrained. This closes the man-in-the-middle exposure of
        the -00 bearer model.</li>
        <li>Separated three "exactly once" properties in
        <xref target="idem"/> (permit consumption, decision/report state,
        external effect), forbade blind re-execution of an
        uncertain-outcome action, and added downstream idempotency-key
        propagation as the mechanism for exactly-once effect.</li>
        <li>Qualified the determinism invariant
        (<xref target="invariants"/>) to hold over the full evaluation input
        -- policy, request, authorization state, and evaluation time -- rather
        than policy and request alone.</li>
        <li>Generalized the currency-specific budget reason codes
        ("cap_eur_day", "cap_eur_month", "cap_daily_rate") to unit-neutral
        "budget_exhausted" and "rate_exhausted" with a structured "budget"
        detail object (<xref target="reasons"/>), and updated the IANA
        registry contents accordingly.</li>
        <li>Refined the "source" field (<xref target="decidereq"/>): a PDP
        MUST NOT treat it as an authenticated authority assertion;
        authorization-relevant provenance must be authenticated and carried
        as authorization context.</li>
        <li>Required linearizable authorization state across PDP replicas
        (<xref target="roles"/>).</li>
        <li>Foregrounded the stateful-authorization thesis in the abstract
        and added an explicit distinction from OAuth-style grant
        authorization (<xref target="intro"/>).</li>
        <li>Expanded Security Considerations: channel as principal attack
        surface, and reversibility as best-effort operational recovery rather
        than a guarantee about external state.</li>
      </ul>
    </section>

    <section anchor="ack">
      <name>Acknowledgments</name>
      <t>The PDP/PEP decomposition and the obligation concept derive from
      XACML. The capability-versus-authorization framing was independently
      articulated by David Mellafe Zuvic in the analysis cited as
      <xref target="SCOPEGATE"/>, whose limitations section names stateful
      side channels as future work; this document treats that remainder as
      part of its scope.</t>
    </section>
  </back>
</rfc>
