<?xml version='1.0' encoding='UTF-8'?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" version="3" docName="draft-singla-agent-identity-protocol-00" ipr="trust200902" category="std" submissionType="IETF" consensus="true" xml:lang="en">

  <front>
    <title abbrev="AIP">Agent Identity Protocol (AIP): Decentralized Identity and Delegation for AI Agents</title>

    <seriesInfo name="Internet-Draft" value="draft-singla-agent-identity-protocol-00"/>

    <author fullname="Paras Singla" initials="P." surname="Singla">
      <organization>Independent</organization>
      <address>
        <email>paras.singla@inviscel.com</email>
        <uri>https://provai.dev</uri>
      </address>
    </author>

    <date year="2026" month="April" day="17"/>

    <area>Security</area>

    <keyword>agent identity</keyword>
    <keyword>AI agent</keyword>
    <keyword>aip</keyword>
    <keyword>agent identity protocol</keyword>
    <keyword>autonomous agents</keyword>
    <keyword>delegation</keyword>
    <keyword>capability</keyword>
    <keyword>decentralized identifier</keyword>
    <keyword>DID</keyword>
    <keyword>credential token</keyword>
    <keyword>JWT</keyword>
    <keyword>DPoP</keyword>
    <keyword>revocation</keyword>
    <keyword>principal chain</keyword>

    <abstract>
      <t>
        The Agent Identity Protocol (AIP) defines a decentralized identity,
        delegation, and authorization framework for autonomous AI agents. AIP
        combines W3C Decentralized Identifiers (DIDs), capability-based
        authorization, cryptographic delegation chains, and deterministic
        validation to enable secure, auditable multi-agent workflows without
        relying on centralized identity providers.
      </t>
      <t>
        This document specifies the core identity model (did:aip), principal
        token structure, credential token format, registration protocol,
        delegation rules, chained approval envelopes for multi-step workflows,
        capability overlays for context-scoped attenuation, and the validation
        algorithm that all relying parties MUST implement.
      </t>
    </abstract>

    <note removeInRFC="true">
      <t>
        Discussion of this document is taking place on the AIP specification
        repository at https://github.com/provai-dev/aip-spec. Source text and
        issue tracking can be found there.
      </t>
    </note>
  </front>

  <middle>
    <section anchor="introduction" numbered="true" xml:base="sections/01-introduction.xml">
  <name>Introduction</name>
  <t>
    Autonomous AI agents are deployed in production environments to act
    on behalf of human and organisational principals. An agent may send
    emails, book appointments, make purchases, access file systems, spawn
    child agents to complete subtasks, and communicate across multiple
    platforms, all without explicit human approval for each action.
  </t>
  <t>
    This creates an identity gap. When an agent presents itself to an
    API, a payment processor, or another agent, there is no standard
    mechanism to establish: the agent's persistent identity across
    interactions; the human or organisation on whose authority it acts;
    the specific actions it is permitted to take; whether it has been
    compromised or revoked; or whether it has a trustworthy history.
  </t>
  <t>
    AIP addresses this gap. It is designed as neutral, open
    infrastructure analogous to HTTP, OAuth, and JWT, providing
    infrastructure that any application can build on without vendor
    dependency.
  </t>

  <section anchor="motivation" numbered="true">
    <name>Motivation</name>
    <t>
      The absence of an identity standard creates concrete operational
      problems. Services cannot implement fine-grained agent access
      control. Humans have no auditable record of what their agents did.
      Compromised agents cannot be reliably stopped. Agent-to-agent
      systems have no basis for trust.
    </t>
    <t>
      Existing deployments typically operate in one of two modes: no
      access, or full access. AIP introduces fine-grained capability
      declarations. A principal can grant <tt>email.read</tt> without
      <tt>email.send</tt>, or <tt>transactions</tt> with a
      <tt>max_daily_total</tt> constraint.
    </t>
  </section>

  <section anchor="design-philosophy" numbered="true">
    <name>Design Philosophy</name>
    <t>
      AIP is built on five design principles:
    </t>
    <t>
      Neutral and open. The spec is released under CC0 and the
      <tt>did:aip</tt> DID method is registered with the W3C.
    </t>
    <t>
      Build on existing standards. AIP builds on W3C DID, JWT
      <xref target="RFC7519"/>, JWK <xref target="RFC7517"/>, DPoP
      <xref target="RFC9449"/>, and CRL patterns from
      <xref target="RFC5280"/>.
    </t>
    <t>
      Human sovereignty. Every agent action must trace to a human or
      authorised organisational principal.
    </t>
    <t>
      Security proportional to risk. AIP defines three security tiers
      matching overhead to risk level.
    </t>
    <t>
      Deterministic verification. The Validation Algorithm is fully
      deterministic: two independent implementations executing the same
      steps on the same token will reach the same result.
    </t>
    <t>
      Zero Trust Architecture. AIP implements the zero trust principles
      defined in <xref target="SP-800-207"/> and satisfies NIST SP 800-63-4
      <xref target="SP-800-63-4"/> AAL2 for agent-to-service interactions.
      No agent is implicitly trusted by virtue of its origin, network
      location, or prior successful interaction. Every Credential Token
      must be validated against the Registry on every interaction.
      Short-lived Credential Tokens (Section 7.2) bound by a mandatory TTL
      limit the blast radius of any credential compromise. Revocation is
      checked in real time for Tier 2 sensitive operations (Section 17.3).
      DPoP proof-of-possession (Section 17.3) ensures that possession of a
      valid Credential Token is insufficient for impersonation without the
      corresponding private key material, satisfying the anti-replay
      requirement of <xref target="SP-800-207"/> Section 3.
    </t>
    <t>
      Integration with Model Context Protocol (MCP). AIP provides the
      agent identity, authorization, and delegation layer that MCP server
      implementations require but do not define. An AIP-authenticated
      agent can obtain scoped access tokens for MCP servers via the token
      exchange mechanism defined in Section 7.5. AIP does not replace MCP;
      it provides the identity substrate on which MCP authorization
      decisions are made. See Section 7.5 (Token Exchange) and Section 15.5
      (OAuth Authorization Server) for the concrete integration path.
    </t>
    <t>
      Least Privilege. Agents are granted only the specific capabilities
      required for their declared purpose, expressed as signed Capability
      Manifests (Section 8). Capability grants are always additive from
      principal to agent and never exceed the granting principal's own
      capability set. A child agent must not be granted capabilities that
      the delegating parent does not itself hold (Rule D-1, Section 8.2).
      The max_delegation_depth field (Section 8.1) bounds the depth of
      sub-agent hierarchies, limiting transitive capability propagation.
      Capability constraints allow fine-grained least-privilege expression
      within each capability category, consistent with
      <xref target="SP-800-207"/> Section 3 (Tenets 5 and 6).
    </t>
    <t>
      Relationship to SPIFFE/SPIRE. SPIFFE is designed for workload
      identity within enumerable, infrastructure-managed environments. AIP
      is designed for autonomous agents that are created dynamically,
      operate across organisational boundaries, and act on behalf of named
      human principals whose authority must be cryptographically
      traceable. An enterprise may deploy SPIFFE for its internal service
      mesh and AIP for its agent fleet without conflict. The two
      mechanisms are complementary and operate at distinct layers of the
      identity stack.
    </t>
    <t>
      Relationship to MCP. The Model Context Protocol <xref target="MCP"/>
      defines how AI agents discover and invoke tools and data sources.
      AIP is the agent identity layer that sits beneath MCP's
      authorisation flow: AIP establishes that an agent is who it claims
      to be (identification via Credential Token), that it was authorised
      by a named human principal (delegation chain in the Principal
      Token), and that it holds specific capabilities (Capability
      Manifest). AIP does not replace MCP's tool-access OAuth flow - it
      provides the agent identity that OAuth's "sub" claim cannot supply
      when the subject is an autonomous agent rather than a human user.
    </t>
    <t>
      Out of scope - Prompt injection prevention. AIP is an identity and
      authorisation protocol. Whether the content an agent processes
      contains adversarial instructions is an application-layer and
      model-layer concern outside this specification's scope. AIP
      mitigates the persistence window of a successful prompt injection
      attack: a compromised agent may be immediately revoked via
      <tt>full_revoke</tt> (Section 9.1), and revocation propagates to all
      child agents within the TTL window defined in Section 7.2. AIP does
      not prevent the initial injection - it limits the attacker's
      persistence.
    </t>
  </section>

  <section anchor="architecture-layers" numbered="true">
    <name>Architecture Layers</name>
    <t>
      AIP is structured as six ordered layers:
    </t>
    <figure anchor="aip-architecture-layers">
      <name>Figure 1: AIP Architecture Layers</name>
      <artwork>
 +------------------------------------------------------+
 | Layer 6 - Reputation Trust over time |
 +------------------------------------------------------+
 | Layer 5 - Revocation Standard kill switch |
 +------------------------------------------------------+
 | Layer 4 - Credential Token &amp; Verification |
 | Cryptographic proof |
 +------------------------------------------------------+
 | Layer 3 - Capabilities What the agent can do |
 +------------------------------------------------------+
 | Layer 2 - Principal Chain Who authorised it |
 | incl. AIP-GRANT and Approval Envelopes |
 +------------------------------------------------------+
 | Layer 1 - Core Identity Who the agent IS |
 +------------------------------------------------------+
</artwork>
    </figure>
  </section>
</section>
    <section anchor="conventions" numbered="true" xml:base="sections/02-conventions.xml">
  <name>Conventions and Definitions</name>
  <t>
    The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
    "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
    "OPTIONAL" in this document are to be interpreted as described in
    BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and
    only when, they appear in all capitals, as shown here.
  </t>
  <t>
    This document uses "MUST" in preference to "SHALL" throughout for
    consistency. Where earlier drafts used "SHALL", the normative force
    is identical; the term has been normalised to "MUST" per this
    convention.
  </t>
  <t>
    JSON is used throughout this document as defined in
    <xref target="RFC8259"/>. URIs are used as defined in
    <xref target="RFC3986"/>. ABNF notation is used as defined in
    <xref target="RFC5234"/>, including its core rules (ALPHA, DIGIT,
    HEXDIG, SP, etc.) as defined in RFC5234 Appendix B.1. HTTP status
    codes, headers, and semantics are as defined in <xref target="RFC9110"/>.
  </t>
  <t>
    All Ed25519 signatures are computed per <xref target="RFC8032"/>.
    DPoP proofs are constructed per <xref target="RFC9449"/>.
  </t>

  <section anchor="canonical-serialization" numbered="true">
    <name>Canonical JSON Serialization</name>
    <t>
      Several objects in this specification are signed outside the JWT
      framework. For those objects, the following canonical JSON
      serialization procedure MUST be used when computing or verifying
      signatures, and when computing Action Hashes (Section 4.6.7):
    </t>
    <ol type="%d">
      <li>Represent the object as a JSON value per <xref target="RFC8259"/>.</li>
      <li>Recursively sort all object keys in lexicographic ascending order
      (Unicode code point order). Array element order MUST be preserved;
      arrays MUST NOT be sorted.</li>
      <li>Remove all insignificant whitespace (no spaces, tabs, or
      newlines outside string values).</li>
      <li>Encode the result as a UTF-8 byte sequence with no BOM.</li>
      <li>Before computing any signature, set the object's
      <tt>"signature"</tt> field to the empty string <tt>""</tt>. The
      <tt>"signature"</tt> field MUST be included in the serialisation at
      its lexicographically correct position with value <tt>""</tt>.</li>
    </ol>
    <t>
      This procedure is equivalent to the JSON Canonicalization Scheme
      (JCS) defined in <xref target="RFC8785"/>. Implementations SHOULD use
      an RFC8785-conformant library to ensure correctness. When this
      procedure is used to compute Action Hashes (Section 4.6.7),
      implementations MUST use an RFC8785-conformant library; custom
      serializer implementations MUST NOT be used for that purpose.
    </t>
    <t>EXAMPLE (informative):</t>
    <sourcecode>
 {"z": 1, "a": 2, "m": [3,1,2]}  →  {"a":2,"m":[3,1,2],"z":1}
</sourcecode>
    <t>
      NOTE: This canonical serialization applies to Capability Manifests,
      Agent Identity Objects, Revocation Objects, Endorsement Objects,
      GrantRequest Objects (when signed), and Approval Envelopes. It does
      NOT apply to JWT-format objects (Credential Tokens, Principal
      Tokens), which use standard JWS compact serialization per
      <xref target="RFC7515"/>.
    </t>
  </section>
</section>
    <section anchor="terminology" numbered="true" xml:base="sections/03-architecture.xml">
  <name>Terminology</name>
  <t>
    The following terms are used throughout this specification:
  </t>

  <dl newline="false">
    <dt>Agent:</dt>
    <dd>An autonomous software system powered by a large language model
      that can perceive inputs, reason about them, and execute actions
      in the world on behalf of a principal.</dd>

    <dt>Agent Identity Descriptor (AID):</dt>
    <dd><t>A globally unique, persistent identifier for an agent conforming
      to the did:aip DID method defined in Section 6. An AID is
      a W3C DID.</t></dd>

    <dt>Principal:</dt>
    <dd><t>The human or organisational entity on whose authority an agent
      acts. Every AIP delegation chain MUST have a verifiable principal
      at its root. Principals are identified by W3C DIDs, but NOT by did:aip (which is reserved for agents).</t></dd>

    <dt>Agent Deployer:</dt>
    <dd>The party that provisions an agent on behalf of a principal.
      May be the principal themselves or a service acting with the
      principal's explicit consent.</dd>

    <dt>Principal Wallet:</dt>
    <dd>Software that holds the principal's DID private key and
      implements the AIP-GRANT consent and signing ceremony (Section 4.5).
      Throughout this document, "wallet" used alone refers to the Principal
      Wallet unless the context explicitly states otherwise.</dd>

    <dt>Capability Manifest:</dt>
    <dd>A versioned, signed JSON document stored in the Registry that
      declares the specific permissions (scopes and constraints) granted
      to an agent. Defined in Section 4.2.2.</dd>

    <dt>Credential Token:</dt>
    <dd>A signed JWT-format token presented by an agent to a Relying
      Party as proof of identity and authorisation for a specific
      interaction. Defined in Section 7.1.</dd>

    <dt>Step Execution Token:</dt>
    <dd>A short-lived Credential Token issued by the AIP Registry
      attesting that a specific Approval Envelope step has been claimed
      by its designated actor. Defined in Section 4.6.8.</dd>

    <dt>Principal Token:</dt>
    <dd><t>A signed JWT payload encoding one delegation link in the AIP
      principal chain. Principal Tokens are embedded as compact-serialised
      JWTs in the aip_chain array of a Credential Token.
      Defined in Section 4.2.4.</t></dd>

    <dt>Relying Party (RP):</dt>
    <dd>Any service, API, or agent that receives and verifies an AIP
      Credential Token. Responsible for implementing the Validation
      Algorithm (Section 7.3).</dd>

    <dt>Delegation:</dt>
    <dd>The act of granting a subset of capabilities from a principal or
      parent agent to a child agent. Formally expressed as a Principal
      Token in the delegation chain.</dd>

    <dt>Delegation Depth:</dt>
    <dd>A non-negative integer counter incremented at each delegation
      step. An agent directly authorised by the root principal has
      delegation depth 0. A child agent of that agent has delegation
      depth 1, and so on.</dd>

    <dt>Tier:</dt>
    <dd>A threat-model declaration specifying which security properties
      the deploying principal asserts hold at execution time. Tier
      determines the applicable revocation-checking mode, maximum Credential
      Token lifetime, and mandatory security mechanisms. Three Tiers are
      defined in Section 3 below. Tier selection on performance or
      availability grounds alone, without regard to the security properties
      declared, is a misconfiguration and a violation of AIP's zero-trust
      model.</dd>

    <dt>Grant Tier:</dt>
    <dd>One of three standardised AIP-GRANT ceremony profiles: G1
      (Registry-Mediated), G2 (Direct Deployer), G3 (Full Ceremony).
      Defined in Section 4.5.</dd>

    <dt>Cryptographic Admissibility:</dt>
    <dd>The property that an agent's Credential Token and delegation
      chain are cryptographically valid at claim time: signatures verify,
      claims are well-formed, the token is not expired, and the delegation
      chain has not been broken.</dd>

    <dt>Capability Overlay:</dt>
    <dd>A Registry-stored, issuer-signed document that restricts an
      agent's effective capability set for a specific engagement or issuer
      context. Defined in Section 4.7.</dd>

    <dt>Engagement Object:</dt>
    <dd>A mutable Registry resource that models a multi-party engagement,
      serving as the parent container for Capability Overlays, Approval
      Envelopes, and participant rosters. Defined in Section 4.8.</dd>

    <dt>Approval Envelope:</dt>
    <dd>A signed document submitted to the Registry that pre-authorises a
      complete multi-step workflow in a single principal signing ceremony.
      Decouples approval from execution. Defined in Section 4.6.</dd>

    <dt>Action Hash:</dt>
    <dd>A SHA-256 hash of the canonical JSON representation of an approval
      step's action descriptor. Binds approval to a specific operation.
      Computed per Section 4.6.7.</dd>

    <dt>Registry:</dt>
    <dd>The authoritative store and resolver for AIP agents, Capability
      Manifests, revocation state, and delegation chains. Implements the
      Registry Interface (Section 15) and the Validation Algorithm
      (Section 7.3). May be operated by a principal, a service, or a
      consortium.</dd>

    <dt>AIP-GRANT:</dt>
    <dd>The principal authorization protocol defining how principals
      authorize agents through a consent ceremony. Defined in Section
      4.5.</dd>

    <dt>Revocation Object:</dt>
    <dd>A signed document declaring that an agent's grant is revoked,
      either in whole (<tt>full_revoke</tt>) or in part (<tt>scope_revoke</tt>
      or <tt>delegation_revoke</tt>). Defined in Section 9.1.</dd>

    <dt>Certificate Revocation List (CRL):</dt>
    <dd>A Registry-published list of revoked agents. Updated periodically
      (Tier 1) or on-demand (Tier 2). Defined in Section 9.2.</dd>

    <dt>DPoP Proof:</dt>
    <dd>A Demonstration of Proof-of-Possession as defined in <xref target="RFC9449"/>. Mandatory for Tier 2 sensitive scopes. Binds a
      Credential Token to a specific HTTP request and the agent's key
      material.</dd>

    <dt>Endorsement:</dt>
    <dd>A signed statement affirming a positive experience or outcome of
      an interaction with an agent. Contributes to the agent's reputation
      score. Defined in Section 11.1.</dd>
  </dl>
</section>
    <section anchor="core-identity" numbered="true" xml:base="sections/04-identity.xml">
  <name>Core Identity (did:aip)</name>
  <t>     Every AIP agent has a globally unique, persistent identifier conforming     to the W3C Decentralized Identifier (DID) standard [W3C-DID]. The AIP    DID method is <tt>did:aip</tt>, registered with the W3C DID method
    registry.
  </t>

  <section numbered="true">
  <name>AID Syntax</name>
    <t>
      An Agent Identity Descriptor (AID) conforms to the following ABNF:
    </t>
    <artwork>
AID = "did:aip:" namespace ":" agent-id
namespace = ALPHA *(ALPHA / DIGIT / "-")
agent-id = 32*HEXDIG
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39
HEXDIG = DIGIT / "a" / "b" / "c" / "d" / "e" / "f"
    </artwork>
    <t>      Example: <tt>did:aip:personal:9f3a1c82b4e6d7f0a2b5c8e1d4f7a0b3</tt>
    </t>
    <ul>
      <li><tt>namespace</tt>: A lowercase alphanumeric string (ALPHA followed
        by any combination of ALPHA, DIGIT, or hyphens) identifying the agent
        type. Registered namespaces include: <tt>personal</tt>,
        <tt>enterprise</tt>, <tt>service</tt>, <tt>orchestrator</tt>,
        <tt>ephemeral</tt>. Custom namespaces MAY be registered.</li>
      <li><tt>agent-id</tt>: A 32-character hexadecimal string (lowercase)
        computed as the SHA-256 hash of the agent's public Ed25519 key
        material (the JWK <tt>x</tt> value base64url-decoded).</li>
    </ul>
  </section>

  <section numbered="true">
  <name>AID Derivation</name>
    <t>
      An AID is derived deterministically from the agent's Ed25519 public
      key:
    </t>
    <ol>
      <li>Generate an Ed25519 keypair per <xref target="RFC8032"/>.</li>
      <li>Encode the public key as a JWK with <tt>kty="OKP"</tt>,
        <tt>crv="Ed25519"</tt>, and the public key value in the <tt>x</tt>
        field (base64url-encoded).</li>
      <li>Compute <tt>SHA-256(base64url_decode(x))</tt> to obtain a 32-byte
        hash.</li>
      <li>Hex-encode the hash (lowercase) to form the <tt>agent-id</tt>.</li>
      <li>Combine with the chosen <tt>namespace</tt> to form the complete
        AID: <tt>did:aip:&lt;namespace&gt;:&lt;agent-id&gt;</tt>.</li>
    </ol>
    <t>
      This derivation is deterministic and cryptographically self-verifying:
      possession of the private key is both necessary and sufficient to claim
      ownership of the AID.
    </t>
  </section>

  <section numbered="true">
  <name>Registered Namespaces</name>
    <t>
      The following namespaces are registered:
    </t>
    <dl newline="false">
      <dt><tt>personal</tt>:</dt>
      <dd>An AI assistant serving a single human principal. Personal agents
        declare this namespace.</dd>

      <dt><tt>enterprise</tt>:</dt>
      <dd>An agent deployed by an organisation for use within the
        organisation.</dd>

      <dt><tt>service</tt>:</dt>
      <dd>A service-owned agent providing functionality to principals (e.g.,
        payment processor, communication provider).</dd>

      <dt><tt>orchestrator</tt>:</dt>
      <dd><t>An agent that spawns and manages sub-agents. Typically has
        <tt>spawn_agents.create</tt> and <tt>spawn_agents.manage</tt>
        scopes.</t></dd>

      <dt><tt>ephemeral</tt>:</dt>
      <dd>A short-lived agent created for a single task. Ephemeral agents
        MUST include a <tt>task_id</tt> in their Principal Token and MUST
        have a limited <tt>delegation_valid_for_seconds</tt> (typically &lt;
        1 hour).</dd>
    </dl>
    <t>
      Custom namespaces MAY be registered with the W3C and the AIP registry.
      Namespaces are immutable once registered.
    </t>
  </section>

  <section numbered="true">
  <name>Agent Identity Object Schema</name>
    <t>
      The Agent Identity Object is the base document for an agent. It
      includes the AID, public key material, and metadata. The schema is
      defined in Section 4.2.1 (see also agent-identity.schema.json in the
      schemas directory).
    </t>
    <t>
      Key fields:
    </t>
    <ul>
      <li><tt>aid</tt>: The agent's AID (REQUIRED)</li>
      <li><tt>name</tt>: Human-readable agent name (REQUIRED)</li>
      <li><tt>type</tt>: The namespace component of the AID (REQUIRED, MUST
        match)</li>
      <li><tt>model</tt>: AI model information including provider and
        model_id (REQUIRED)</li>
      <li><tt>public_key</tt>: JWK format Ed25519 public key (REQUIRED)</li>
      <li><tt>created_at</tt>: ISO 8601 timestamp (REQUIRED)</li>
      <li><tt>version</tt>: Identity version number for key rotation
        (REQUIRED, minimum 1)</li>
      <li><tt>previous_key_signature</tt>: EdDSA signature by previous key
        when rotating (REQUIRED if version &gt; 1)</li>
    </ul>
  </section>

  <section numbered="true">
  <name>Uniqueness and Immutability</name>
    <t>
      An AID MUST be globally unique. Once an agent is registered in a
      Registry, its AID is immutable. An AID cannot be reused or transferred.
      Two agents with identical public keys will derive identical AIDs, so
      the cryptographic relationship is deterministic.
    </t>
  </section>
</section>
    <section anchor="resource-model" numbered="true" xml:base="sections/05-resource-model.xml">
  <name>Resource Model and Data Structures</name>
  <t>
    This section defines the core data structures in AIP: JSON Schema
    representations of agents, principals, capabilities, tokens, and
    enrollment objects. All objects conform to the canonical JSON
    serialization rules of Section 2.1.
  </t>

  <section anchor="resource-naming" numbered="true">
    <name>Resource Naming</name>
    <t>
      Agent Identities (AIDs) are W3C Decentralized Identifiers [W3C-DID]
      using the did:aip method. The ABNF grammar uses core rules from
      [RFC5234] Appendix B.1 (DIGIT) and defines two additional terminal
      rules:
    </t>
    <artwork type="abnf">
    aip-did    = "did:aip:" namespace ":" unique-id
    namespace  = LOALPHA *( LOALPHA / DIGIT )
                 *( "-" 1*( LOALPHA / DIGIT ) )
    unique-id  = 32LOHEXDIG
    LOALPHA    = %x61-7A        ; a-z only, lowercase
    LOHEXDIG   = DIGIT / "a" / "b" / "c" / "d" / "e" / "f"
                                ; lowercase hex digit only
    </artwork>
    <t>
      The namespace MUST begin with a lowercase alpha character,
      MUST NOT end with a hyphen, and MUST NOT contain consecutive hyphens.
      Implementations MUST reject AIDs containing uppercase hex digits or
      uppercase namespace characters.
    </t>

    <t>
      The following namespace values are defined:
    </t>

    <table>
      <name>Defined Namespace Values</name>
      <thead>
        <tr>
          <th align="left">Namespace</th>
          <th align="left">Description</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>personal</td>
          <td>An agent acting for a single human principal</td>
        </tr>
        <tr>
          <td>enterprise</td>
          <td>An agent acting within an organisational deployment</td>
        </tr>
        <tr>
          <td>service</td>
          <td>A persistent agent providing a capability as a service</td>
        </tr>
        <tr>
          <td>ephemeral</td>
          <td>An agent created for a single task; revoked on completion</td>
        </tr>
        <tr>
          <td>orchestrator</td>
          <td>An agent whose primary function is spawning child agents</td>
        </tr>
        <tr>
          <td>registry</td>
          <td>An AIP Registry instance; at most one active AID per deployment</td>
        </tr>
      </tbody>
    </table>

    <t>
      A registry-namespace AID MUST NOT be registered via the standard
      POST /v1/agents endpoint. It MUST be created exclusively via the
      Registry Genesis procedure (Section 6.3).
    </t>

    <t>
      Compound typed identifiers use prefixed UUID v4 values:
    </t>

    <table>
      <name>Typed Identifier Prefixes</name>
      <thead>
        <tr>
          <th align="left">Object Type</th>
          <th align="left">Prefix</th>
          <th align="left">Example</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Capability Manifest</td>
          <td>cm:</td>
          <td>cm:550e8400-e29b-41d4-a716-...</td>
        </tr>
        <tr>
          <td>Revocation Object</td>
          <td>rev:</td>
          <td>rev:6ba7b810-9dad-11d1-80b4-...</td>
        </tr>
        <tr>
          <td>Endorsement Object</td>
          <td>end:</td>
          <td>end:6ba7b811-9dad-11d1-80b4-...</td>
        </tr>
        <tr>
          <td>Grant Request</td>
          <td>gr:</td>
          <td>gr:550e8401-e29b-41d4-a716-...</td>
        </tr>
        <tr>
          <td>Approval Envelope</td>
          <td>apr:</td>
          <td>apr:550e8402-e29b-41d4-a716-...</td>
        </tr>
      </tbody>
    </table>
  </section>

  <section anchor="agent-identity" numbered="true">
    <name>Agent Identity Object (Section 4.2.1)</name>
    <t>
      The Agent Identity Object is the canonical signed JSON document that
      establishes an agent's identity. Canonical signing field order:
      aid, name, type, model, created_at, version, public_key,
      previous_key_signature.
    </t>

    <dl newline="false">
      <dt>aid</dt>
      <dd><t>Type: string. Required: REQUIRED. Constraints: MUST match did:aip ABNF; pattern is lowercase namespace plus 32 lowercase hexadecimal characters.</t></dd>
      <dt>name</dt>
      <dd><t>Type: string. Required: REQUIRED. Constraints: <tt>minLength: 1</tt>, <tt>maxLength: 64</tt>.</t></dd>
      <dt>type</dt>
      <dd><t>Type: string. Required: REQUIRED. Constraints: MUST exactly match namespace component of <tt>aid</tt>; pattern is lowercase alphanumeric with optional hyphen-separated segments.</t></dd>
      <dt>model</dt>
      <dd><t>Type: object. Required: REQUIRED. Constraints: See sub-fields below.</t></dd>
      <dt>model.provider</dt>
      <dd><t>Type: string. Required: REQUIRED. Constraints: <tt>minLength: 1</tt>, <tt>maxLength: 64</tt>.</t></dd>
      <dt>model.model_id</dt>
      <dd><t>Type: string. Required: REQUIRED. Constraints: <tt>minLength: 1</tt>, <tt>maxLength: 128</tt>.</t></dd>
      <dt>model.attestation_hash</dt>
      <dd><t>Type: string. Required: OPTIONAL. Constraints: pattern <tt>^sha256:[0-9a-f]{64}$</tt>.</t></dd>
      <dt>created_at</dt>
      <dd><t>Type: string. Required: REQUIRED. Constraints: ISO 8601 UTC; format: <tt>date-time</tt>; immutable after registration.</t></dd>
      <dt>version</dt>
      <dd><t>Type: integer. Required: REQUIRED. Constraints: minimum: 1; MUST increment by exactly 1 on key rotation.</t></dd>
      <dt>public_key</dt>
      <dd><t>Type: object. Required: REQUIRED. Constraints: JWK per <xref target="RFC7517"/>; Ed25519 (<tt>kty=OKP</tt>, <tt>crv=Ed25519</tt>) per <xref target="RFC8037"/>.</t></dd>
      <dt>public_key.kty</dt>
      <dd><t>Type: string. Required: REQUIRED. Constraints: const: <tt>"OKP"</tt>.</t></dd>
      <dt>public_key.crv</dt>
      <dd><t>Type: string. Required: REQUIRED. Constraints: const: <tt>"Ed25519"</tt>.</t></dd>
      <dt>public_key.x</dt>
      <dd><t>Type: string. Required: REQUIRED. Constraints: pattern <tt>^[A-Za-z0-9_-]{43}$</tt> (32 bytes base64url, no padding).</t></dd>
      <dt>public_key.kid</dt>
      <dd><t>Type: string. Required: REQUIRED. Constraints: DID URL using the agent AID followed by <tt>#key-N</tt> where <tt>N</tt> is a positive integer.</t></dd>
      <dt>previous_key_signature</dt>
      <dd><t>Type: string. Required: OPTIONAL (version=1); REQUIRED (version&gt;=2). Constraints: base64url EdDSA signature of new object (with this field set to <tt>""</tt>); pattern <tt>^[A-Za-z0-9_-]+$</tt>.</t></dd>
    </dl>

    <t>
      <strong>Normative requirements:</strong>
    </t>
    <ul>
      <li>The aid, type, and created_at fields MUST NOT change after initial registration.</li>
      <li>The type field MUST exactly match the namespace component of the aid field.</li>
      <li>The version field MUST start at 1 and MUST increment by exactly 1 on each key rotation.</li>
      <li>The public_key.kid MUST use format &lt;aid&gt;#key-&lt;n&gt; where &lt;n&gt; starts at 1.</li>
      <li>When version is 2 or greater, previous_key_signature MUST be present and MUST be a non-empty base64url string.</li>
    </ul>
  </section>

  <section anchor="capability-manifest" numbered="true">
    <name>Capability Manifest (Section 4.2.2)</name>
    <t>
      The Capability Manifest is a versioned, signed JSON document that
      declares the specific permissions granted to an agent. Canonical signing
      field order: manifest_id, aid, granted_by, version, issued_at,
      expires_at, capabilities, signature.
    </t>

    <table>
      <name>Capability Manifest Fields</name>
      <thead>
        <tr>
          <th align="left">Field</th>
          <th align="center">Type</th>
          <th align="center">Required</th>
          <th align="left">Constraints</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>manifest_id</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>pattern: ^cm:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$</td>
        </tr>
        <tr>
          <td>aid</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>MUST match did:aip ABNF</td>
        </tr>
        <tr>
          <td>granted_by</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>MUST be a valid W3C DID; pattern: any valid DID method and suffix</td>
        </tr>
        <tr>
          <td>version</td>
          <td>integer</td>
          <td>REQUIRED</td>
          <td>minimum: 1; MUST increment on every update including scope_revoke</td>
        </tr>
        <tr>
          <td>issued_at</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>ISO 8601 UTC; format: date-time</td>
        </tr>
        <tr>
          <td>expires_at</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>ISO 8601 UTC; MUST be after issued_at</td>
        </tr>
        <tr>
          <td>capabilities</td>
          <td>object</td>
          <td>REQUIRED</td>
          <td>See Section 4.3 for all sub-fields</td>
        </tr>
        <tr>
          <td>signature</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>base64url EdDSA signature; pattern: ^[A-Za-z0-9_-]+$</td>
        </tr>
      </tbody>
    </table>

    <t>
      A new manifest_id MUST be generated on every update, including
      scope_revoke operations. Relying Parties MUST verify the manifest
      signature field using the public key of the granted_by DID before
      trusting any capability declared within it.
    </t>
  </section>

  <section anchor="credential-token" numbered="true">
    <name>Credential Token (Section 4.2.3)</name>
    <t>
      An AIP Credential Token is a compact JWT with the following format:
    </t>
    <artwork>
    aip-token = JWT-header "." JWT-payload "." JWT-signature
    </artwork>
    <t>
      The token MUST be a valid JWT as defined by [RFC7519].
    </t>

    <t>
      <strong>JWT Header fields:</strong>
    </t>

    <table>
      <name>JWT Header Fields</name>
      <thead>
        <tr>
          <th align="left">Field</th>
          <th align="center">Requirement</th>
          <th align="left">Value / Constraints</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>typ</td>
          <td>MUST</td>
          <td>"AIP+JWT"</td>
        </tr>
        <tr>
          <td>alg</td>
          <td>MUST</td>
          <td>"EdDSA" (REQUIRED); "ES256" (OPTIONAL); "RS256" (OPTIONAL, legacy enterprise only)</td>
        </tr>
        <tr>
          <td>kid</td>
          <td>MUST</td>
          <td>DID URL identifying the signing key; MUST match the kid in the signing agent's Agent Identity</td>
        </tr>
      </tbody>
    </table>

    <t>
      <strong>Credential Token Payload fields:</strong>
    </t>

    <table>
      <name>Credential Token Payload Fields</name>
      <thead>
        <tr>
          <th align="left">Field</th>
          <th align="center">Type</th>
          <th align="center">Required</th>
          <th align="left">Constraints</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>aip_version</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>MUST be "0.3" for this spec</td>
        </tr>
        <tr>
          <td>iss</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>MUST match did:aip ABNF; MUST equal sub of last aip_chain element</td>
        </tr>
        <tr>
          <td>sub</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>MUST match did:aip ABNF; MUST equal iss for non-delegated tokens</td>
        </tr>
        <tr>
          <td>aud</td>
          <td>string/array</td>
          <td>REQUIRED</td>
          <td>Single string or array; MUST include the Relying Party's identifier</td>
        </tr>
        <tr>
          <td>iat</td>
          <td>integer</td>
          <td>REQUIRED</td>
          <td>Unix timestamp; MUST NOT be in the future (30-second clock skew tolerance)</td>
        </tr>
        <tr>
          <td>exp</td>
          <td>integer</td>
          <td>REQUIRED</td>
          <td>Unix timestamp; MUST be strictly greater than iat; TTL limits per Section 7.2</td>
        </tr>
        <tr>
          <td>jti</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>UUID v4 canonical lowercase</td>
        </tr>
        <tr>
          <td>aip_scope</td>
          <td>array</td>
          <td>REQUIRED</td>
          <td>minItems: 1; uniqueItems: true; each item matches ^[a-z_]+([.][a-z_]+)*$</td>
        </tr>
        <tr>
          <td>aip_chain</td>
          <td>array</td>
          <td>REQUIRED</td>
          <td>minItems: 1; maxItems: 11; each element is a compact-serialised signed Principal Token JWT</td>
        </tr>
        <tr>
          <td>aip_registry</td>
          <td>string</td>
          <td>OPTIONAL</td>
          <td>URI of AIP Registry</td>
        </tr>
        <tr>
          <td>aip_approval_id</td>
          <td>string</td>
          <td>OPTIONAL</td>
          <td>pattern: ^apr:[0-9a-f]{8}-...$; REQUIRED when token is a step-claim token</td>
        </tr>
        <tr>
          <td>aip_approval_step</td>
          <td>integer</td>
          <td>OPTIONAL</td>
          <td>Positive integer (&gt;= 1); REQUIRED when aip_approval_id is present</td>
        </tr>
        <tr>
          <td>aip_engagement_id</td>
          <td>string</td>
          <td>OPTIONAL</td>
          <td>pattern: ^eng:[0-9a-f]{8}-...$; present when token is scoped to an Engagement Object</td>
        </tr>
      </tbody>
    </table>
  </section>

  <section anchor="principal-token" numbered="true">
    <name>Principal Token (Section 4.2.4)</name>
    <t>
      A Principal Token is a JWT payload encoding one delegation link in
      the AIP principal chain. Principal Tokens are embedded as compact-
      serialised JWTs in the aip_chain array of a Credential Token.
    </t>

    <table>
      <name>Principal Token Fields</name>
      <thead>
        <tr>
          <th align="left">Field</th>
          <th align="center">Type</th>
          <th align="center">Required</th>
          <th align="left">Constraints</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>iss</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>W3C DID or did:aip AID; for delegation_depth 0: MUST equal principal.id; for delegation_depth &gt; 0: MUST equal delegated_by</td>
        </tr>
        <tr>
          <td>sub</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>MUST match did:aip ABNF</td>
        </tr>
        <tr>
          <td>principal</td>
          <td>object</td>
          <td>REQUIRED</td>
          <td>See sub-fields below</td>
        </tr>
        <tr>
          <td>principal.type</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>enum: ["human", "organisation"]</td>
        </tr>
        <tr>
          <td>principal.id</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>W3C DID; MUST NOT use did:aip method; MUST be byte-for-byte identical across all chain elements</td>
        </tr>
        <tr>
          <td>delegated_by</td>
          <td>string/null</td>
          <td>REQUIRED</td>
          <td>null when delegation_depth is 0; MUST be a did:aip AID when delegation_depth &gt; 0</td>
        </tr>
        <tr>
          <td>delegation_depth</td>
          <td>integer</td>
          <td>REQUIRED</td>
          <td>minimum: 0, maximum: 10; MUST equal the array index of this token in aip_chain</td>
        </tr>
        <tr>
          <td>max_delegation_depth</td>
          <td>integer</td>
          <td>REQUIRED</td>
          <td>minimum: 0, maximum: 10; default: 3 when absent; only the value from aip_chain[0] governs the chain</td>
        </tr>
        <tr>
          <td>issued_at</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>ISO 8601 UTC; format: date-time</td>
        </tr>
        <tr>
          <td>expires_at</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>ISO 8601 UTC; MUST be strictly after issued_at</td>
        </tr>
        <tr>
          <td>purpose</td>
          <td>string</td>
          <td>OPTIONAL</td>
          <td>maxLength: 128</td>
        </tr>
        <tr>
          <td>task_id</td>
          <td>string/null</td>
          <td>OPTIONAL; REQUIRED for ephemeral agents</td>
          <td>minLength: 1, maxLength: 256 when non-null</td>
        </tr>
        <tr>
          <td>scope</td>
          <td>array</td>
          <td>REQUIRED</td>
          <td>minItems: 1; uniqueItems: true</td>
        </tr>
        <tr>
          <td>acr</td>
          <td>string</td>
          <td>OPTIONAL; REQUIRED when grant ceremony is G3</td>
          <td>Authentication context class reference</td>
        </tr>
        <tr>
          <td>amr</td>
          <td>array</td>
          <td>OPTIONAL; REQUIRED when grant ceremony is G3</td>
          <td>Authentication method reference values per <xref target="RFC8176"/></td>
        </tr>
      </tbody>
    </table>

    <t>
      The principal.id field MUST be byte-for-byte identical across
      every Principal Token in the same aip_chain array. The principal.id
      MUST NOT begin with did:aip:.
    </t>
  </section>

  <section anchor="registration-envelope" numbered="true">
    <name>Registration Envelope (Section 4.2.5)</name>
    <t>
      The Registration Envelope is the request body submitted to
      POST /v1/agents to register a new AIP agent.
    </t>

    <table>
      <name>Registration Envelope Fields</name>
      <thead>
        <tr>
          <th align="left">Field</th>
          <th align="center">Type</th>
          <th align="center">Required</th>
          <th align="left">Constraints</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>identity</td>
          <td>object</td>
          <td>REQUIRED</td>
          <td>MUST conform to agent-identity schema; version MUST be 1; previous_key_signature MUST NOT be present</td>
        </tr>
        <tr>
          <td>capability_manifest</td>
          <td>object</td>
          <td>REQUIRED</td>
          <td>MUST conform to capability-manifest schema; version MUST be 1; aid MUST equal identity.aid</td>
        </tr>
        <tr>
          <td>principal_token</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>Compact JWT (header.payload.signature); delegation_depth MUST be 0; delegated_by MUST be null</td>
        </tr>
        <tr>
          <td>grant_tier</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>enum: ["G1", "G2", "G3"]; MUST be consistent with principal_token's delegation_depth and the scopes in capability_manifest</td>
        </tr>
      </tbody>
    </table>
  </section>

  <section anchor="resource-revocation-object" numbered="true">
    <name>Revocation Object (Section 4.2.6)</name>
    <t>
      Revocation is performed by submitting a signed Revocation Object to
      POST /v1/revocations. Canonical signing field order:
      revocation_id, target_aid, type, issued_by, reason, timestamp,
      propagate_to_children, scopes_revoked, signature.
    </t>

    <table>
      <name>Revocation Object Fields</name>
      <thead>
        <tr>
          <th align="left">Field</th>
          <th align="center">Type</th>
          <th align="center">Required</th>
          <th align="left">Constraints</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>revocation_id</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>pattern: ^rev:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$</td>
        </tr>
        <tr>
          <td>target_aid</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>MUST match did:aip ABNF</td>
        </tr>
        <tr>
          <td>type</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>enum: ["full_revoke", "scope_revoke", "delegation_revoke", "principal_revoke"]</td>
        </tr>
        <tr>
          <td>issued_by</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>W3C DID; in target's chain</td>
        </tr>
        <tr>
          <td>reason</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>enum of defined reasons</td>
        </tr>
        <tr>
          <td>timestamp</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>ISO 8601 UTC</td>
        </tr>
        <tr>
          <td>propagate_to_children</td>
          <td>boolean</td>
          <td>OPTIONAL</td>
          <td>default: false</td>
        </tr>
        <tr>
          <td>scopes_revoked</td>
          <td>array</td>
          <td>REQUIRED when scope_revoke; MUST NOT otherwise</td>
          <td>minItems: 1</td>
        </tr>
        <tr>
          <td>signature</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>base64url EdDSA</td>
        </tr>
      </tbody>
    </table>

    <t>
      <strong>Revocation Types:</strong>
    </t>
    <ul>
      <li><strong>full_revoke</strong> - Permanently revokes the AID.</li>
      <li><strong>scope_revoke</strong> - Removes specific scopes from the Capability Manifest. Does NOT invalidate outstanding Credential Tokens.</li>
      <li><strong>delegation_revoke</strong> - Invalidates delegation chains rooted at the target. Child agents lose authority; the target AID remains valid for direct principal interactions.</li>
      <li><strong>principal_revoke</strong> - Issued by the root principal to revoke their entire authorisation of the target agent. Equivalent to full_revoke plus child propagation.</li>
    </ul>
  </section>

  <section anchor="endorsement" numbered="true">
    <name>Endorsement Object (Section 4.2.7)</name>
    <t>
      Any Relying Party or agent MAY submit a signed Endorsement Object to
      the Registry after a completed interaction. Canonical signing field
      order: endorsement_id, from_aid, to_aid, task_id, outcome, notes,
      timestamp, signature.
    </t>

    <table>
      <name>Endorsement Object Fields</name>
      <thead>
        <tr>
          <th align="left">Field</th>
          <th align="center">Type</th>
          <th align="center">Required</th>
          <th align="left">Constraints</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>endorsement_id</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>pattern: ^end:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$</td>
        </tr>
        <tr>
          <td>from_aid</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>MUST NOT equal to_aid</td>
        </tr>
        <tr>
          <td>to_aid</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>MUST NOT equal from_aid</td>
        </tr>
        <tr>
          <td>task_id</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>minLength: 1, maxLength: 256</td>
        </tr>
        <tr>
          <td>outcome</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>enum: ["success", "partial", "failure"]</td>
        </tr>
        <tr>
          <td>notes</td>
          <td>string/null</td>
          <td>OPTIONAL</td>
          <td>maxLength: 512</td>
        </tr>
        <tr>
          <td>timestamp</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>ISO 8601 UTC</td>
        </tr>
        <tr>
          <td>signature</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>base64url EdDSA of from_aid</td>
        </tr>
      </tbody>
    </table>

    <t>
      The Registry MUST verify every submitted Endorsement Object signature.
      Only success and partial outcomes increment endorsement_count.
      failure increments incident_count.
    </t>
  </section>

  <section anchor="field-constraints" numbered="true">
    <name>Field Constraints and Defined Scope Identifiers (Section 4.3)</name>
    <t>
      Capability capabilities sub-fields are defined in full in
      schemas/v0.3/capability-manifest.schema.json.
    </t>

    <t>
      <strong>Defined Scope Identifiers:</strong>
    </t>

    <artwork>
    email.read email.write email.send
    email.delete calendar.read calendar.write
    calendar.delete filesystem.read filesystem.write
    filesystem.execute filesystem.delete web.browse
    web.forms_submit web.download transactions
    communicate.whatsapp communicate.telegram communicate.sms
    communicate.voice spawn_agents.create spawn_agents.manage
    </artwork>

    <t>
      NOTE: The bare scope spawn_agents is retired as of v0.3 and is NOT
      a defined scope identifier. Validators MUST reject tokens containing
      spawn_agents without a sub-scope qualifier with invalid_scope.
      spawn_agents.create and spawn_agents.manage are Tier 2 scopes with
      a maximum TTL of 300 seconds; DPoP is REQUIRED.
    </t>

    <t>
      Where this document references transactions.* or communicate.*,
      this means the bare capability key OR any scope beginning with that
      prefix OR capabilities.transactions.enabled: true /
      capabilities.communicate.enabled: true in the Capability Manifest.
    </t>

    <t>
      Empty arrays [] for filesystem.read or filesystem.write MUST
      be interpreted as deny-all. A require_confirmation_above value
      above max_single_transaction is vacuous and MUST be rejected.
      When communicate.enabled is true, at least one channel MUST be
      explicitly set to true.
    </t>
  </section>

  <section anchor="resource-delegation-rules" numbered="true">
    <name>Delegation Rules (Section 4.4)</name>
    <t>
      <strong>Rule D-1.</strong> A delegated agent MUST NOT grant scopes or looser
      constraint values than its own Capability Manifest contains.
    </t>

    <t>
      <strong>Rule D-2.</strong> A delegated agent MUST NOT issue a Principal Token with
      max_delegation_depth greater than its remaining depth
      (max_delegation_depth - delegation_depth).
    </t>

    <t>
      <strong>Rule D-3.</strong> Implementations MUST reject any Credential Token where
      the delegation_depth of any chain token exceeds the root token's
      max_delegation_depth.
    </t>

    <t>
      <strong>Rule D-4.</strong> The root Principal Token (index 0) MUST have
      delegation_depth = 0. Each subsequent token at index i MUST have
      delegation_depth = i. No gaps, skips, or repeated values are
      permitted.
    </t>

    <t>
      <strong>Rule D-5.</strong> Each delegation chain token MUST be signed by the
      private key of the delegated_by AID (or root principal for depth 0).
    </t>
  </section>

  <section anchor="capability-overlays" numbered="true">
    <name>Capability Overlays (Section 4.7)</name>
    <t>
      A Capability Overlay is a signed restriction document stored in the
      Registry. It narrows an agent's effective capability set for
      operations within a specific engagement or issuer context.
    </t>

    <t>
      <strong>Rule CO-1 (Attenuation Only):</strong> An overlay MUST NOT expand any
      constraint value beyond what the base Capability Manifest permits.
      The effective capability set is always the intersection of the base
      manifest and all active overlays scoped to the engagement or issuer.
    </t>

    <table>
      <name>Capability Overlay Fields</name>
      <thead>
        <tr>
          <th align="left">Field</th>
          <th align="center">Type</th>
          <th align="center">Required</th>
          <th align="left">Constraints</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>overlay_id</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>Pattern: ^co:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$</td>
        </tr>
        <tr>
          <td>aid</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>The target agent's AID</td>
        </tr>
        <tr>
          <td>engagement_id</td>
          <td>string</td>
          <td>OPTIONAL</td>
          <td>Pattern: ^eng:...$; links to Engagement Object (Section 4.8)</td>
        </tr>
        <tr>
          <td>issued_by</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>DID of the overlay issuer; MUST be did:web or did:aip (MUST NOT be did:key)</td>
        </tr>
        <tr>
          <td>overlay_type</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>MUST be "restrict"</td>
        </tr>
        <tr>
          <td>issued_at</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>ISO 8601 UTC timestamp</td>
        </tr>
        <tr>
          <td>expires_at</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>ISO 8601 UTC; MUST be strictly after issued_at</td>
        </tr>
        <tr>
          <td>version</td>
          <td>integer</td>
          <td>REQUIRED</td>
          <td>Positive integer; monotonically increasing per (aid, engagement_id, issued_by) tuple</td>
        </tr>
        <tr>
          <td>constraints</td>
          <td>object</td>
          <td>REQUIRED</td>
          <td>Uses the same schema as Capability Manifest capabilities sub-object</td>
        </tr>
        <tr>
          <td>signature</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>Base64url EdDSA signature by issued_by over JCS canonical JSON</td>
        </tr>
      </tbody>
    </table>

    <t>
      <strong>Overlay Rules:</strong>
    </t>
    <ol>
      <li><strong>CO-1 (Attenuation Only):</strong> For every field in constraints,
        the value MUST be equal to or more restrictive than the
        corresponding value in the base Capability Manifest.</li>
      <li><strong>CO-2 (Issuer DID Method):</strong> The issued_by DID MUST use
        did:web or did:aip. Overlays signed by did:key MUST be
        rejected with overlay_issuer_invalid.</li>
      <li><strong>CO-3 (Version Monotonicity):</strong> A new overlay for the same
        (aid, engagement_id, issued_by) tuple MUST have a strictly
        higher version than the current active overlay.</li>
      <li><strong>CO-4 (Expiry Handling):</strong> Expired overlays MUST be treated as
        absent.</li>
      <li><strong>CO-5 (Engagement Termination):</strong> If an Engagement Object
        associated with an overlay's engagement_id is
        terminated, all overlays for that engagement MUST be invalidated
        atomically by the Registry.</li>
      <li><strong>CO-6 (Multiple Overlays):</strong> When multiple overlays apply to
        the same agent, the effective capability set is the intersection
        of ALL applicable overlays with the base manifest.</li>
    </ol>

    <t>
      <strong>Effective Capability Computation:</strong>
    </t>
    <artwork>
    effective = base_manifest.capabilities
    for each active_overlay in applicable_overlays:
        effective = intersect(effective, active_overlay.constraints)
    </artwork>
    <t>
      Where intersect applies per-field: boolean fields use AND (false
      wins); numeric limits use min(base, overlay); path arrays use set
      intersection; scope set to false removes that scope.
    </t>
  </section>

  <section anchor="engagement-objects" numbered="true">
    <name>Engagement Objects (Section 4.8)</name>
    <t>
      An Engagement Object is a mutable Registry resource that models a
      multi-party engagement. It is the parent container for Capability
      Overlays (Section 4.7) scoped via engagement_id, Approval
      Envelopes (Section 4.6) scoped via engagement_id, and participant
      roster and approval gate state.
    </t>

    <table>
      <name>Engagement Object Fields</name>
      <thead>
        <tr>
          <th align="left">Field</th>
          <th align="center">Type</th>
          <th align="center">Required</th>
          <th align="left">Constraints</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>engagement_id</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>Pattern: ^eng:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$</td>
        </tr>
        <tr>
          <td>title</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>maxLength: 256</td>
        </tr>
        <tr>
          <td>hiring_operator</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>DID; MUST be did:web or did:aip</td>
        </tr>
        <tr>
          <td>deploying_principal</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>DID of the deploying principal</td>
        </tr>
        <tr>
          <td>created_at</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>ISO 8601 UTC</td>
        </tr>
        <tr>
          <td>expires_at</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>ISO 8601 UTC; MUST be after created_at</td>
        </tr>
        <tr>
          <td>status</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>One of: "proposed", "active", "suspended", "completed", "terminated"</td>
        </tr>
        <tr>
          <td>participants</td>
          <td>array</td>
          <td>REQUIRED</td>
          <td>Array of Participant objects</td>
        </tr>
        <tr>
          <td>approval_gates</td>
          <td>array</td>
          <td>OPTIONAL</td>
          <td>Array of Approval Gate objects</td>
        </tr>
        <tr>
          <td>change_log</td>
          <td>array</td>
          <td>REQUIRED</td>
          <td>Append-only array of Change Log Entry objects</td>
        </tr>
        <tr>
          <td>hiring_operator_signature</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>Base64url EdDSA signature by hiring_operator</td>
        </tr>
        <tr>
          <td>deploying_principal_signature</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>Countersignature by deploying_principal</td>
        </tr>
        <tr>
          <td>version</td>
          <td>integer</td>
          <td>REQUIRED</td>
          <td>Monotonically increasing</td>
        </tr>
      </tbody>
    </table>

    <t>
      <strong>Participant object fields:</strong> aid (REQUIRED), role
      (REQUIRED, maxLength: 64), capability_overlay_id (OPTIONAL),
      added_at (REQUIRED, ISO 8601), added_by (REQUIRED, DID),
      removed_at (OPTIONAL, ISO 8601).
    </t>

    <t>
      <strong>Approval Gate object fields:</strong> gate_id (REQUIRED,
      pattern: ^gate:[a-z0-9_-]+$), name (REQUIRED, maxLength: 128),
      required_approver (REQUIRED, DID), trigger (REQUIRED, string),
      status (REQUIRED, one of: "pending", "approved", "rejected"),
      approved_at (OPTIONAL, ISO 8601).
    </t>

    <t>
      <strong>Change Log Entry fields:</strong> seq (REQUIRED,
      monotonically increasing from 1), timestamp (REQUIRED, ISO 8601),
      action (REQUIRED), actor (REQUIRED, DID), payload (OPTIONAL,
      object), signature (REQUIRED, Base64url EdDSA by actor over JCS
      canonical JSON excluding signature).
    </t>

    <t>
      Defined change log actions: engagement_created,
      engagement_countersigned, participant_added, participant_removed,
      participant_role_changed, gate_added, gate_approved,
      gate_rejected, engagement_suspended, engagement_resumed,
      engagement_completed, engagement_terminated.
    </t>

    <t>
      The Registry MUST reject any request that modifies or deletes an
      existing change log entry. Only appends are permitted.
    </t>

    <t>
      <strong>Engagement Lifecycle:</strong>
    </t>

    <artwork>
    proposed --&gt; active --&gt; completed
                   |
                   +--&gt; suspended --&gt; active (resumed)
                   |
                   +--&gt; terminated
    </artwork>

    <t>
      <strong>Engagement Lifecycle Transitions:</strong>
    </t>
    <ul>
      <li>proposed to active: Requires both hiring_operator_signature
        and deploying_principal_signature.</li>
      <li>active to suspended / completed: Hiring operator only.</li>
      <li>active/suspended to terminated: Either party.</li>
    </ul>

    <t>
      <strong>Termination Cascade:</strong> When an Engagement is terminated:
    </t>
    <ol>
      <li>The Registry MUST set status: "terminated" atomically.</li>
      <li>All Capability Overlays linked to this engagement_id MUST be
        invalidated (per Rule CO-5).</li>
      <li>All Approval Envelopes in pending_approval, approved, or
        executing status scoped to this engagement_id MUST be
        transitioned to cancelled.</li>
      <li>Subsequent Credential Token validation for operations scoped to
        this engagement MUST fail with engagement_terminated.</li>
    </ol>
  </section>
</section>
    <section anchor="registration-protocol" xml:base="sections/05-registration.xml">
  <name>Registration Protocol</name>

  <section anchor="registration-envelope-submission">
    <name>Registration Envelope</name>
    <t>An agent is registered by submitting a Registration Envelope to
`POST /v1/agents`. The fields are defined in Section 4.2.5.</t>
    <t>The `principal_token` MUST have `delegation_depth` equal to 0 and
`delegated_by` equal to null. The `sub` field of the decoded principal
token payload MUST equal `identity.aid`.</t>
  </section>

  <section anchor="registration-validation">
    <name>Registration Validation</name>
    <t>The Registry MUST perform the following checks in order before
accepting a Registration Envelope. The Registry MUST either accept
all or reject all — partial registration MUST NOT be possible.</t>
    <ol>
      <li>Check 1. `identity` MUST be present and MUST be valid JSON conforming to the Agent Identity schema.</li>
      <li>Check 2. `identity.aid` MUST match the `did:aip` ABNF grammar defined in Section 4.1.</li>
      <li>Check 3. `identity.type` MUST equal the namespace component of `identity.aid`. A mismatch MUST be rejected.</li>
      <li>Check 4. `identity.aid` MUST NOT already exist in the Registry.</li>
      <li>Check 5. `identity.public_key` MUST be an Ed25519 JWK with `kty="OKP"`, `crv="Ed25519"`, and a 43-character base64url `x` value.</li>
      <li>Check 6. `capability_manifest.expires_at` MUST be in the future at the time of registration.</li>
      <li>Check 7. `capability_manifest.aid` MUST equal `identity.aid`.</li>
      <li>Check 8. The `principal_token` string MUST be decodable as a compact-serialised JWT and MUST conform to the Principal Token schema (Section 4.2.4).</li>
      <li>Check 9. The decoded `principal_token` payload `sub` MUST equal `identity.aid`.</li>
      <li>Check 10. The decoded `principal_token` payload `principal.id` MUST NOT begin with `did:aip:`.</li>
      <li>Check 11. If `identity.type` is `ephemeral`, the decoded `principal_token` payload `task_id` MUST be non-null and non-empty.</li>
      <li>Check 12. `capability_manifest.signature` MUST be verifiable against the `granted_by` DID's public key.</li>
      <li>Check 13. If `identity.version` is 2 or greater, `identity.previous_key_signature` MUST be present and MUST verify using the key at the previous version.</li>
      <li>Check 14. `grant_tier` MUST be present and MUST be one of `"G1"`, `"G2"`, or `"G3"`. If any scope in `capability_manifest.capabilities` is a Tier 2 scope (per Section 3), `grant_tier` MUST be `"G2"` or `"G3"`. If any scope is Tier 3, `grant_tier` MUST be `"G3"`. If `grant_tier` is absent, the Registry MUST reject with `registration_invalid`.</li>
    </ol>
    <t>The Registry MUST record the `delegated_by` field from the decoded `principal_token` payload (or the principal's DID if `delegated_by` is null) in the parent-child delegation index, for use in revocation propagation (Section 9.3).</t>
  </section>

  <section anchor="registration-errors">
    <name>Error Responses</name>
    <t>All AIP error responses MUST use the format defined in Section 13.1. Implementations MUST NOT return HTTP 200 for error conditions.</t>
  </section>
</section>
    <section anchor="agent-resolution" xml:base="sections/06-agent-resolution.xml">
  <name>Agent Resolution</name>
  <t>6. Agent Resolution</t>

  <!-- 6.1. DID Resolution -->
  <section anchor="did-resolution">
    <name>DID Resolution</name>
    <t>6.1. DID Resolution</t>
    <t>
      Every AID MUST have a corresponding W3C DID Document resolvable by any standard DID resolver.
      The DID Document is derived deterministically from the Agent Identity stored in the Registry.
    </t>
    <t>
      The Registry MUST generate and serve DID Documents from <tt>GET /v1/agents/{aid}</tt> when the
      <tt>Accept: application/did+ld+json</tt> or <tt>Accept: application/did+json</tt> header is present.
    </t>
    <t>
      AIP implementations MUST support DID resolution for at minimum <tt>did:key</tt> and <tt>did:web</tt>
      DID methods. Resolved DID Documents MAY be cached for a maximum of 300 seconds. If resolution fails or times
      out, implementations MUST treat the result as <tt>registry_unavailable</tt> and MUST reject the operation.
    </t>
    <t>
      If the AID has been revoked, the Registry MUST return <tt>deactivated: true</tt> in
      <em>didDocumentMetadata</em>, per W3C-DID Section 7.1.2.
    </t>
  </section>

  <!-- 6.2. DID Document Structure -->
  <section anchor="did-document-structure">
    <name>DID Document Structure</name>
    <t>6.2. DID Document Structure</t>
    <t>
      A conformant <tt>did:aip</tt> DID Document MUST include <tt>@context</tt>, <tt>id</tt>,
      <tt>verificationMethod</tt>, <tt>authentication</tt>, and <tt>controller</tt>. See the canonical example in the
      repository at <em>examples/latest/did-document.json</em>.
    </t>
    <t>
      A principal's DID Document that authorises agents for Tier 2 operations MUST include an <tt>AIPRegistry</tt>
      service entry in its <tt>service</tt> array:
    </t>

    <artwork type="json">
      {
        "id": "did:web:acme.com#aip-registry",
        "type": "AIPRegistry",
        "serviceEndpoint": "https://registry.acme.com"
      }
    </artwork>

    <t>
      The <tt>serviceEndpoint</tt> MUST be an HTTPS URI pointing to the base URL of the authoritative AIP Registry for
      agents delegated by this principal. The <tt>type</tt> MUST be exactly <tt>"AIPRegistry"</tt> (case-sensitive).
    </t>

    <t>
      For principals using <tt>did:key</tt>: an <tt>AIPRegistry</tt> service entry cannot be declared
      (DID Key documents have no service array). Per Section 4.5, <tt>did:key</tt> principals MUST NOT authorise
      Tier 2 operations.
    </t>

    <t>
      For principals using <tt>did:web</tt> or <tt>did:aip</tt>: the <tt>AIPRegistry</tt> service entry is REQUIRED when
      the principal authorises any agent with Tier 2 scopes. It is OPTIONAL for principals authorising only Tier 1 agents.
    </t>

    <t>The CRUD operations for <tt>did:aip</tt> are:</t>
    <table>
      <thead>
        <tr><td>Operation</td><td>Mechanism</td></tr>
      </thead>
      <tbody>
        <tr><td>Create</td><td><tt>POST /v1/agents</tt> with Registration Envelope</td></tr>
        <tr><td>Read</td><td><tt>GET /v1/agents/{aid}</tt> with DID Accept header</td></tr>
        <tr><td>Update</td><td><tt>PUT /v1/agents/{aid}</tt> for key rotation only</td></tr>
        <tr><td>Deactivate</td><td><tt>POST /v1/revocations</tt> with <em>full_revoke</em> object</td></tr>
      </tbody>
    </table>
  </section>

  <!-- 6.3. Registry Genesis -->
  <section anchor="registry-genesis">
    <name>Registry Genesis</name>
    <t>6.3. Registry Genesis</t>
    <t>
      Registry Genesis is the one-time initialisation procedure by which a new AIP Registry creates and persists its own
      <tt>did:aip</tt> Agent Identity (the Registry AID). It MUST be performed before the Registry serves any requests.
    </t>

    <!-- 6.3.1 Key Generation -->
    <section anchor="registry-genesis-key-generation">
      <name>6.3.1. Key Generation</name>
      <t>6.3.1. Key Generation</t>
      <t>
        The Registry MUST generate a fresh Ed25519 keypair at first boot. The private key MUST be stored encrypted at rest
        (AES-256-GCM or equivalent). The public key is used to construct the Registry's DID Document and to sign CRL
        documents (Section 9.2) and Step Execution Tokens (Section 4.6.8).
      </t>
    </section>

    <!-- 6.3.2 AID Construction -->
    <section anchor="registry-genesis-aid-construction">
      <name>6.3.2. AID Construction</name>
      <t>6.3.2. AID Construction</t>
      <t>
        The Registry MUST construct its AID using the <tt>registry</tt> namespace (Section 4.1):
      </t>

      <artwork type="text">
        did:aip:registry:&lt;32-hex-character unique-id&gt;
      </artwork>

      <t>
        The <tt>unique-id</tt> MUST be generated as a cryptographically random 32-character lowercase hexadecimal string.
        At most one Registry AID MUST be active per Registry deployment at any time.
      </t>
    </section>

    <!-- 6.3.3 Self-Registration Exemption -->
    <section anchor="registry-genesis-self-registration-exemption">
      <name>6.3.3. Self-Registration Exemption</name>
      <t>6.3.3. Self-Registration Exemption</t>
      <t>
        The Registry AID MUST NOT be created via <tt>POST /v1/agents</tt>. Instead, the Registry MUST persist its AID and
        keypair directly to its own data store during genesis. The following Registration Envelope checks (Section 5.2) are
        explicitly inapplicable to the Registry AID:
      </t>
      <ul>
        <li>
          <strong>Check 10</strong> (<tt>principal_token</tt> validation) — the Registry has no human principal and MUST
          NOT carry a principal delegation chain.
        </li>
        <li>
          <strong>Check 4</strong> (duplicate AID rejection) — genesis is idempotent; if a Registry AID already exists in the data store the existing record MUST be used and genesis MUST NOT overwrite it.
        </li>
      </ul>
    </section>

    <!-- 6.3.4 Well-Known Publication -->
    <section anchor="registry-genesis-well-known-publication">
      <name>6.3.4. Well-Known Publication</name>
      <t>6.3.4. Well-Known Publication</t>
      <t>
        The Registry MUST publish its DID Document and public key at:
      </t>
      <artwork type="text">
GET /.well-known/aip-registry
      </artwork>
      <t>
        The response MUST be a JSON object containing:
      </t>

      <table>
        <thead>
          <tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr>
        </thead>
        <tbody>
          <tr><td><tt>registry_aid</tt></td><td>string</td><td>REQUIRED</td><td>The Registry's <tt>did:aip</tt> AID</td></tr>
          <tr><td><tt>registry_name</tt></td><td>string</td><td>REQUIRED</td><td>Human-readable Registry name (maxLength: 128)</td></tr>
          <tr><td><tt>aip_version</tt></td><td>string</td><td>REQUIRED</td><td>The <tt>aip_version</tt> this Registry conforms to</td></tr>
          <tr><td><tt>public_key</tt></td><td>object</td><td>REQUIRED</td><td>JWK representation of the Registry's Ed25519 public key</td></tr>
          <tr><td><tt>endpoints</tt></td><td>object</td><td>REQUIRED</td><td>Map of service names to relative or absolute URI paths</td></tr>
          <tr><td><tt>signature</tt></td><td>string</td><td>REQUIRED</td><td>Base64url EdDSA signature of the canonical JSON (RFC 8785 JCS) of the document excluding the <tt>signature</tt> field</td></tr>
        </tbody>
      </table>

      <t>
        The <em>endpoints</em> object MUST include at minimum:
      </t>

      <table>
        <thead>
          <tr><th>Key</th><th>Description</th></tr>
        </thead>
        <tbody>
          <tr><td><tt>agents</tt></td><td>Base path for agent endpoints (e.g., <tt>/v1/agents</tt>)</td></tr>
          <tr><td><tt>crl</tt></td><td>CRL endpoint path (e.g., <tt>/v1/crl</tt>)</td></tr>
          <tr><td><tt>revocations</tt></td><td>Revocation submission path (e.g., <tt>/v1/revocations</tt>)</td></tr>
        </tbody>
      </table>

      <t>
        Self-signature verification: Relying Parties MUST verify the <tt>signature</tt> field against the <tt>public_key</tt>
        in the same document. This ensures the well-known document has not been tampered with in transit. The signature is
        computed over the RFC 8785 JCS canonical serialisation of the document with the <tt>signature</tt> field removed.
      </t>

      <t>
        First-contact bootstrapping: On first contact with a Registry, Relying Parties MUST:
      </t>
      <ul>
        <li>Fetch <tt>/.well-known/aip-registry</tt> over HTTPS.</li>
        <li>Verify the self-signature.</li>
        <li>Pin the <tt>public_key</tt> and <tt>registry_aid</tt> locally.</li>
        <li>On subsequent contacts, verify that the pinned key matches.</li>
      </ul>

      <t>
        Key rotation for Registry keys is an administrative operation that requires out-of-band communication to Relying
        Parties. A future AIP will define the Registry key rotation protocol.
      </t>

      <t>
        Validators bootstrapping trust in a Registry MUST fetch and pin the Registry's public key from this endpoint before
        processing any Step Execution Tokens or CRL documents signed by that Registry. Cached values MUST NOT be used beyond 300 seconds without revalidation.
      </t>
    </section>

    <!-- 6.3.5 Single-Instance Constraint -->
    <section anchor="registry-genesis-single-instance-constraint">
      <name>6.3.5. Single-Instance Constraint</name>
      <t>6.3.5. Single-Instance Constraint</t>
      <t>
        Each Registry deployment MUST have exactly one active Registry AID. Provisioning a second Registry AID within the same deployment
        MUST be treated as a fatal configuration error. Horizontal scaling and high-availability deployments MUST share a single Registry AID
        and keypair (stored in a shared secrets manager).
      </t>
    </section>

    <!-- 6.3.6 Registry Key Rotation (Interim Procedure) -->
    <section anchor="registry-genesis-registry-key-rotation">
      <name>6.3.6. Registry Key Rotation (Interim Procedure)</name>
      <t>6.3.6. Registry Key Rotation (Interim Procedure)</t>
      <t>
        Full in-band Registry key rotation is deferred to a future minor version. Until that protocol is defined, the following interim
        procedure applies to operators who must rotate the Registry key (e.g., in response to suspected key compromise).
      </t>

      <ol>
        <li>
          Registry key rotation MUST be treated as a Registry instance migration: the existing Registry AID is deprecated, a new Registry
          is initialised via Registry Genesis (Section 6.3) with a new AID and keypair, and agents are re-registered or migrated to the new
          Registry.
        </li>
        <li>
          Relying Parties that have pinned the existing Registry's public key (step 3 of Section 6.3.4 bootstrapping) and subsequently
          receive a <tt>/.well-known/aip-registry</tt> document whose <em>public_key</em> does not match the pinned key MUST treat this as a potential MITM attack. They MUST NOT use the new key automatically. They MUST halt Registry-dependent operations and require out-of-band verification that the key change is legitimate before re-pinning.
        </li>
        <li>
          Historical Step Execution Tokens and CRL documents signed by the previous Registry key remain valid for their stated <em>exp</em> period only. Relying Parties MUST verify historical tokens against the key that was pinned at issuance time; they MUST NOT re-verify them against a newly pinned key.
        </li>
        <li>
          A future minor version will define an in-band key succession protocol using a <em>previous_registry_key_signature</em> mechanism
          analogous to agent key rotation (Section 4.2.1), allowing Relying Parties to verify key transitions without out-of-band communication.
        </li>
      </ol>

      <t>
        Validators bootstrapping trust in a Registry MUST fetch and pin the Registry's public key from this endpoint before processing any
        Step Execution Tokens or CRL documents signed by that Registry. Cached values MUST NOT be used beyond 300 seconds without revalidation.
      </t>
    </section>
  </section>
</section>
    <section anchor="credential-tokens" numbered="true" xml:base="sections/06-credential-tokens.xml">
  <name>Credential Tokens</name>

  <section anchor="token-structure" numbered="true">
    <name>Token Structure</name>
    <t>
      An AIP Credential Token is transmitted as an HTTP Authorization
      header:
    </t>
    <t>EXAMPLE (informative):</t>
    <sourcecode>
 Authorization: AIP &lt;token&gt;
 X-AIP-Version: 0.3
</sourcecode>
    <t>
      For interactions requiring Proof-of-Possession:
    </t>
    <t>EXAMPLE (informative):</t>
    <sourcecode>
 Authorization: AIP &lt;token&gt;
 DPoP: &lt;dpop-proof&gt;
 X-AIP-Version: 0.3
</sourcecode>
    <t>
      The <tt>X-AIP-Version</tt> HTTP header MUST carry the full protocol
      version string, identical to the <tt>aip_version</tt> JWT claim
      (Section 4.2.3). For this specification, the value MUST be
      <tt>"0.3"</tt>. Implementations MUST reject requests where the
      <tt>X-AIP-Version</tt> header carries a version value that does not
      match a supported <tt>aip_version</tt>.
    </t>
  </section>

  <section anchor="token-issuance" numbered="true">
    <name>Token Issuance</name>
    <t>
      Implementations MUST enforce the following maximum token lifetimes:
    </t>
    <table>
      <thead>
        <tr><th>Scope Category</th><th>Maximum TTL</th></tr>
      </thead>
      <tbody>
        <tr><td>All standard scopes (<tt>email.*</tt>, <tt>calendar.*</tt>, <tt>web.*</tt>, etc.)</td><td>3600 seconds (1 hour)</td></tr>
        <tr><td><tt>transactions.*</tt> or <tt>communicate.*</tt></td><td>300 seconds (5 minutes)</td></tr>
        <tr><td><tt>filesystem.execute</tt></td><td>300 seconds (5 minutes)</td></tr>
        <tr><td><tt>spawn_agents.create</tt> or <tt>spawn_agents.manage</tt></td><td>300 seconds (5 minutes)</td></tr>
      </tbody>
    </table>
    <t>
      When a token contains scopes from multiple categories, the most
      restrictive TTL applies. Zero-duration and negative-duration tokens
      (where <tt>exp &lt;= iat</tt>) MUST be rejected.
    </t>
    <t>
      REMINDER: A token's Tier is determined by its highest-risk scope
      (Section 3). A token containing one Tier 2 scope and any number of
      Tier 1 scopes is a Tier 2 token in its entirety. Implementations
      MUST NOT derive Tier from the majority of scopes or from the first
      scope in the array.
    </t>
  </section>

  <section anchor="token-refresh" numbered="true">
    <name>Token Refresh and Long-Running Tasks</name>

    <section anchor="agent-self-refresh" numbered="true">
      <name>Agent Self-Refresh</name>
      <t>
        An AIP agent holds its own Ed25519 private key and MAY issue new
        Credential Tokens at any time, provided the Principal Token(s) in its
        delegation chain (<tt>aip_chain</tt>) remain valid. There is no refresh token
        in AIP - the agent's signing key IS the refresh credential.
      </t>
      <t>
        An agent self-refresh involves: issuing a new Credential Token with a
        new <tt>jti</tt>, a fresh <tt>iat</tt>, and a new <tt>exp</tt> within TTL limits.
        The <tt>aip_chain</tt> content remains unchanged until the Principal Tokens
        within it expire.
      </t>
      <t>
        Agents MUST NOT re-use the same <tt>jti</tt> when issuing a fresh token.
        Each issued Credential Token MUST have a unique <tt>jti</tt>.
      </t>
    </section>

    <section anchor="preemptive-refresh" numbered="true">
      <name>Pre-emptive Refresh Requirements</name>
      <t>
        Agents MUST implement pre-emptive refresh to avoid mid-task token
        expiry. An agent MUST begin issuing a replacement Credential Token
        before the current token expires:
      </t>
      <table>
        <thead>
          <tr><th>Token TTL Category</th><th>Begin refresh when remaining TTL &lt;=</th></tr>
        </thead>
        <tbody>
          <tr><td>Standard (3600s)</td><td>300 seconds (5 minutes)</td></tr>
          <tr><td>Sensitive (300s)</td><td>30 seconds</td></tr>
        </tbody>
      </table>
      <t>
        Implementations MUST NOT wait for a token rejection
        (<tt>token_expired</tt>) before refreshing. Waiting for rejection
        creates a gap in execution continuity and may leave in-progress Tier 2
        operations without valid authority.
      </t>
      <t>
        Relying Parties MUST NOT reject a token solely because a newer token
        exists for the same agent. Each token is independently valid for its
        own <tt>iat</tt> to <tt>exp</tt> window.
      </t>
      <t>
        For real-time streaming interactions (e.g., a long-running web
        socket), the agent SHOULD renegotiate the session with a fresh
        Credential Token before the current token's expiry rather than
        waiting for mid-stream rejection.
      </t>
    </section>

    <section anchor="delegation-chain-expiry" numbered="true">
      <name>Delegation Chain Expiry</name>
      <t>
        When a Principal Token in the <tt>aip_chain</tt> expires, the Credential
        Token becomes structurally invalid at validation Step 8h regardless
        of the Credential Token's own <tt>exp</tt>. This is because the delegation
        authority itself has lapsed.
      </t>
      <t>
        When a delegation chain expires:
      </t>
      <ol type="%d">
        <li>The agent MUST NOT issue new Credential Tokens referencing the expired <tt>aip_chain</tt> (even if the agent's own <tt>exp</tt> is still in the future).</li>
        <li>The agent MUST obtain a fresh delegation from its parent (or from the root principal for depth-0 agents) via the AIP-GRANT flow (Section 4.5) or sub-agent delegation flow (Section 4.5.7).</li>
        <li>Once a fresh delegation is established and registered, the agent may resume issuing Credential Tokens.</li>
      </ol>
      <t>
        Relying Parties that receive a token where Step 8h fails MUST return
        <tt>chain_token_expired</tt>. Agents receiving this error MUST treat it as
        <tt>delegation_chain_refresh_required</tt> - they must re-establish their
        delegation rather than merely refreshing their Credential Token.
      </t>
      <t>
        <strong>Anticipatory chain refresh:</strong> Agents SHOULD monitor the
        <tt>expires_at</tt> timestamps of all Principal Tokens in their
        <tt>aip_chain</tt>. When the nearest expiry is within 10% of the total
        delegation validity period (or 24 hours, whichever is smaller), the
        agent SHOULD proactively initiate a delegation renewal.
      </t>
    </section>

    <section anchor="approval-envelope-interaction" numbered="true">
      <name>Interaction with Approval Envelopes</name>
      <t>
        Approval Envelopes (Section 4.6) are specifically designed to decouple
        human approval timing from token TTL constraints. The following rules
        govern their interaction:
      </t>
      <ol type="%d">
        <li>An Approval Envelope's <tt>approval_window_expires_at</tt> is independent of any Credential Token TTL. Envelopes may remain in <tt>pending_approval</tt> status for hours while normal TTLs of 300s or 3600s apply only at execution time.</li>
        <li>When an agent claims an Approval Step (Section 4.6.8), it MUST present a Credential Token that is valid at the time of the claim. The token TTL for a step-claim follows the same rules as for any other interaction involving those scopes (300s for Tier 2 scopes, 3600s for Tier 1).</li>
        <li>Long-running workflows where steps are separated by hours or days require the agent to issue a fresh Credential Token for each step claim. This is intentional - the agent's authority must be re-verified at each step, not just at envelope creation time.</li>
        <li>If an agent's delegation chain expires between envelope approval and step execution, the agent MUST renew its delegation (Section 7.4.3) before claiming any remaining steps. The Approval Envelope itself remains valid - only the execution credential needs renewal.</li>
        <li>An agent MUST NOT pre-issue step-claim Credential Tokens for all steps at envelope approval time. Tokens MUST be issued at execution time so that revocation checks (Section 7.3 Step 7) are performed against the current Registry state.</li>
      </ol>
    </section>
  </section>

  <section anchor="token-exchange-mcp" numbered="true">
    <name>Token Exchange for MCP</name>

    <section anchor="token-exchange-overview" numbered="true">
      <name>Overview</name>
      <t>
        An AIP agent MAY exchange its Credential Token for a scoped access
        token targeting a specific MCP server or OAuth-protected resource.
        The exchange is performed at the Registry's token endpoint
        (Section 15.5).
      </t>
    </section>

    <section anchor="token-exchange-request" numbered="true">
      <name>Exchange Request</name>
      <t>
        The agent sends an RFC 8693 token exchange request:
      </t>
      <sourcecode>
 POST /v1/oauth/token HTTP/1.1
 Content-Type: application/x-www-form-urlencoded
 DPoP: &lt;DPoP proof JWT&gt;

 grant_type=urn:ietf:params:oauth:grant-type:token-exchange
 &amp;subject_token=&lt;AIP Credential Token&gt;
 &amp;subject_token_type=urn:ietf:params:oauth:token-type:jwt
 &amp;resource=https://mcp-server.example.com/
 &amp;scope=urn:aip:scope:email.read urn:aip:scope:calendar.read
</sourcecode>
      <t>Normative requirements:</t>
      <ol type="%d">
        <li><tt>grant_type</tt> MUST be <tt>urn:ietf:params:oauth:grant-type:token-exchange</tt>.</li>
        <li><tt>subject_token</tt> MUST be a valid AIP Credential Token.</li>
        <li><tt>subject_token_type</tt> MUST be <tt>urn:ietf:params:oauth:token-type:jwt</tt>.</li>
        <li><tt>resource</tt> MUST be present per RFC 8707 <xref target="RFC8707"/>, identifying the target resource server.</li>
        <li><tt>scope</tt> MUST use AIP scope URIs from the <tt>urn:aip:scope:</tt> namespace (Section 15.6). The requested scopes MUST be a subset of the Credential Token's <tt>aip_scope</tt>.</li>
        <li>DPoP proof <xref target="RFC9449"/> MUST be included, binding the exchange to the agent's key material.</li>
      </ol>
    </section>

    <section anchor="token-exchange-validation" numbered="true">
      <name>Exchange Validation</name>
      <t>
        The Registry (as OAuth AS) MUST:
      </t>
      <ol type="%d">
        <li>Validate the <tt>subject_token</tt> using the full validation algorithm (Section 7.3).</li>
        <li>Verify the DPoP proof binds to the agent's public key.</li>
        <li>Verify the requested <tt>scope</tt> is a subset of the <tt>subject_token</tt>'s <tt>aip_scope</tt> (attenuation only, never expansion).</li>
        <li>Verify the <tt>resource</tt> is a registered MCP server or resource. Fail: <tt>invalid_target</tt>.</li>
        <li>
          <t>Issue an access token with:</t>
          <ul>
            <li><t><tt>aud</tt> set to the <tt>resource</tt> URI</t></li>
            <li><t><tt>scope</tt> set to the granted scopes</t></li>
            <li><t><tt>sub</tt> set to the agent's AID</t></li>
            <li><t><tt>act.sub</tt> set to the root principal's DID (actor chain per <xref target="RFC8693"/> §4.1)</t></li>
            <li><t>TTL &lt;= the remaining TTL of the <tt>subject_token</tt></t></li>
          </ul>
        </li>
        <li>The access token MUST be a JWT per RFC 9068 <xref target="RFC9068"/>.</li>
      </ol>
    </section>

    <section anchor="token-exchange-response" numbered="true">
      <name>Exchange Response</name>
      <t>EXAMPLE (informative):</t>
      <sourcecode type="json">
 {
   "access_token": "&lt;JWT&gt;",
   "token_type": "DPoP",
   "expires_in": 300,
   "scope": "urn:aip:scope:email.read urn:aip:scope:calendar.read",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token"
 }
</sourcecode>
    </section>
  </section>
</section>
    <section anchor="credential-token-validation" numbered="true" xml:base="sections/07-validation.xml">
  <name>Credential Token Validation</name>
  <t>
    The Credential Token Validation Algorithm is the normative heart of
    AIP. A Relying Party MUST execute the following steps in the order
    presented. Validation is deterministic: two independent implementations
    executing the same steps on the same token with the same Registry state
    MUST reach the same outcome.
  </t>

  <t>
    The Relying Party MUST reject the token at the first step that fails.
    Steps marked 6a, 6b, 9e, and 10a are conditional sub-steps introduced
    in v0.3; they are part of the numbered step at which they appear and do
    not change the base step numbering.
  </t>

  <section anchor="validation-step-1" numbered="true">
  <name>Step 1: Parse</name>
    <t>
      Parse the Authorization header value as a JWT per <xref target="RFC7519"/>. If parsing fails or the token is malformed,
      reject with <tt>invalid_token</tt>.
    </t>
  </section>

  <section anchor="validation-step-2" numbered="true">
  <name>Step 2: Header Validation</name>
    <t>
      Verify the JWT header contains:
    </t>
    <ul>
      <li><tt>typ</tt>: MUST be <tt>"AIP+JWT"</tt></li>
      <li><tt>alg</tt>: MUST be one of the approved algorithms per Section
        17.2 (EdDSA REQUIRED; ES256 and RS256 optional per Section
        16.1)</li>
      <li><tt>kid</tt>: MUST be present and MUST be a DID URL in the form
        <tt>did:aip:&lt;namespace&gt;:&lt;32-hex&gt;#key-&lt;n&gt;</tt></li>
    </ul>
    <t>      If any check fails, reject with <tt>invalid_token</tt>.
    </t>
  </section>

  <section anchor="validation-step-3" numbered="true">
  <name>Step 3: Identify Agent and Retrieve Public Key</name>
    <t>
      Extract the <tt>kid</tt> header parameter. This is a full DID URL
      identifying the agent's public key. Contact the Registry to retrieve
      the historical public key matching this <tt>kid</tt>. The Registry
      MUST return the public key material along with its validity period
      (<tt>valid_from</tt> and <tt>valid_until</tt> timestamps).
    </t>
    <t>
      If the <tt>kid</tt> is not found or the key is not valid at the time
      of the JWT's <tt>iat</tt> claim, reject with <tt>unknown_aid</tt>.
    </t>
  </section>

  <section anchor="validation-step-4" numbered="true">
  <name>Step 4: Verify Signature</name>
    <t>       Verify the JWT signature using the public key retrieved in Step 3.       The signature verification MUST use constant-time comparison. If      signature verification fails, reject with <tt>invalid_token</tt>.
    </t>
  </section>

  <section anchor="validation-step-5" numbered="true">
  <name>Step 5: Validate Claims</name>
    <t>
      Validate the following claims from the decoded JWT payload:
    </t>
    <dl newline="false">
      <dt>5a. iat (Issued-At Time):</dt>
      <dd>
        <tt>iat</tt> MUST NOT be in the future. Allow a 30-second clock
        skew tolerance. If <tt>iat</tt> is in the future (beyond skew), reject
        with <tt>invalid_token</tt>.
      </dd>

      <dt>5b. exp (Expiration Time):</dt>
      <dd>
        <tt>exp</tt> MUST be strictly greater than <tt>iat</tt> (zero-duration
        tokens are not permitted). If <tt>exp &lt;= iat</tt>, reject with
        <tt>invalid_token</tt>.
      </dd>

      <dt>5c. Token Not Expired:</dt>
      <dd>
        <tt>exp</tt> MUST be in the future (current time must be &lt;
        <tt>exp</tt>). If <tt>exp</tt> is in the past, reject with
        <tt>token_expired</tt>.
      </dd>

      <dt>5d. aud (Audience):</dt>
      <dd>
        The <tt>aud</tt> claim MUST match the Relying Party's identifier.
        If <tt>aud</tt> is a string, it MUST match exactly. If <tt>aud</tt>
        is an array, the Relying Party's identifier MUST be present in the
        array. If no match, reject with <tt>invalid_token</tt>.
      </dd>

      <dt>5e. jti (JWT ID) Replay Check:</dt>
      <dd>
        <tt>jti</tt> MUST be a UUID v4 in canonical form (lowercase,
        hyphenated). The Relying Party MUST maintain a replay cache keyed by
        <tt>(iss, jti)</tt> for each token's validity window. If <tt>(iss,
        jti)</tt> has been seen before, reject with <tt>token_replayed</tt>.
        After the token expires, the replay cache entry MAY be discarded.
      </dd>

      <dt>5f. aip_version:</dt>
      <dd>
        <tt>aip_version</tt> MUST be present and MUST be <tt>"0.3"</tt> for
        tokens conforming to this specification. If <tt>aip_version</tt> is
        absent or unrecognised, reject with <tt>invalid_token</tt> and
        include the received <tt>aip_version</tt> in the error description
        to aid debugging.
      </dd>
    </dl>
  </section>

  <section anchor="validation-step-6" numbered="true">
  <name>Step 6: TTL Validation</name>
    <t>       Verify that the token's lifetime does not exceed the maximum permitted      by its scopes. Compute <tt>lifetime = exp - iat</tt> (in seconds).
      Compare against the TTL limits defined in Section 7.2:
    </t>
    <artwork>
Standard scopes (email.*, calendar.*, web.*):     max 3600 seconds
Sensitive scopes (transactions.*, communicate.*,
                  filesystem.execute,
                  spawn_agents.create/manage):    max 300 seconds

When a token contains scopes from multiple
categories, the most restrictive limit applies.
    </artwork>
    <t>
      If <tt>lifetime</tt> exceeds the limit, reject with <tt>invalid_token</tt>.
    </t>
    <t>
      REMINDER: A token's Tier is determined by its highest-risk scope
      (Section 3). A token containing one Tier 2 scope and nine Tier 1
      scopes is a Tier 2 token in its entirety. Do not derive Tier from the
      majority or first scope.
    </t>
  </section>

  <section anchor="validation-step-6a" numbered="true">
  <name>Step 6a: Registry Trust Anchoring (Conditional)</name>
    <t>
      This step is REQUIRED for Tier 2 operations and RECOMMENDED for Tier
      1. It verifies that the Registry from which the Relying Party has been
      fetching data matches the Registry declared by the principal's DID
      Document.
    </t>
    <ol>
      <li>Extract <tt>principal_id</tt> from <tt>aip_chain[0].iss</tt> (the
        root principal's DID). This is the authorising human or
        organisational principal.</li>
      <li>Resolve <tt>principal_id</tt> using the DID method's own resolution
        mechanism (e.g., did:web resolution per <xref target="W3C-DID"/>,
        did:key per <xref target="W3C-DID"/>). The resolution MUST be
        independent of any agent-provided data. Use the DID method's
        canonical resolver.</li>
      <li>Examine the resolved DID Document's <tt>service</tt> array. Locate
        an entry with <tt>type: "AIPRegistry"</tt>.</li>
      <li>Extract the <tt>serviceEndpoint</tt> URI from that service entry.
        This is the authoritative Registry URI for this principal.</li>
      <li>Verify that the Registry from which the Relying Party has been
        fetching revocation status, Capability Manifests, and step data
        matches the declared <tt>serviceEndpoint</tt> URI. Perform origin
        comparison per <xref target="RFC6454"/> (scheme + host + port must
        match).</li>
      <li>If <tt>aip_registry</tt> is present in the Credential Token,
        verify it matches the DID-Document-declared Registry endpoint. If
        mismatch, reject with <tt>registry_untrusted</tt>.</li>
      <li>If the DID Document does not contain an <tt>AIPRegistry</tt>
        service entry and the token contains Tier 2 scopes, reject with
        <tt>registry_untrusted</tt>.</li>
    </ol>
    <t>
      For Tier 1 operations: this step is RECOMMENDED. Registry trust
      anchoring prevents MitM Registry substitution but adds latency
      (additional DID resolution). Tier 1 operators MAY skip this step if
      they accept the risk that the Registry might be MitM'd with a loss of
      up to 15-minute revocation staleness.
    </t>
    <t>       If DID resolution fails and the token contains Tier 2 scopes, reject      with <tt>registry_unavailable</tt>.
    </t>
  </section>

  <section anchor="validation-step-6b" numbered="true">
  <name>Step 6b: Engagement Validation (Conditional)</name>
    <t>      This step applies only if <tt>aip_engagement_id</tt> is present in the
      Credential Token payload.
    </t>
    <ol>
      <li>Fetch the Engagement Object from the Registry via <tt>GET /v1/engagements/{aip_engagement_id}</tt>.</li>
      <li>Verify the Engagement's <tt>status</tt> field. If <tt>"active"</tt>, continue.
        If <tt>"completed"</tt> or <tt>"terminated"</tt>, reject with <tt>engagement_terminated</tt>.
        If <tt>"suspended"</tt>, reject with <tt>engagement_suspended</tt>.</li>
      <li>Verify that the token's <tt>sub</tt> (agent AID) appears in the
        Engagement's <tt>participants</tt> array as an active participant.
        If <tt>sub</tt> is not in the participants array or has been marked
        as removed, reject with <tt>engagement_participant_removed</tt>.</li>
      <li>If the Engagement defines <tt>approval_gates</tt> with pending
        gates that guard the current operation, verify the token's operation
        is not gated (or that the gate has been approved). If a required
        gate is still pending, reject with <tt>engagement_gate_pending</tt>.</li>
    </ol>
  </section>

  <section anchor="validation-step-7" numbered="true">
  <name>Step 7: Revocation Check</name>
    <t>
      Query the Registry for revocation status of the agent identified by
      <tt>iss</tt> (extracted from the <tt>kid</tt>). The revocation check
      method depends on the token's Tier:
    </t>
    <ul>
      <li><strong>Tier 1:</strong> Retrieve the CRL from the Registry cache.
        If the agent appears on the CRL (revocation type
        <tt>full_revoke</tt>, <tt>scope_revoke</tt>, or
        <tt>principal_revoke</tt>), reject with <tt>agent_revoked</tt>.</li>
      <li><strong>Tier 2:</strong> Perform a live Registry lookup. Do not use
        cached CRL data. Query <tt>GET /v1/agents/{iss}/revocation</tt>
        and verify no active revocation applies. If the agent is revoked,
        reject with <tt>agent_revoked</tt>. If the Registry is unreachable,
        reject with <tt>registry_unavailable</tt>.</li>
    </ul>
  </section>

  <section anchor="validation-step-8" numbered="true">
  <name>Step 8: Delegation Chain Validation</name>
    <t>      Validate the Principal Token delegation chain in <tt>aip_chain</tt>.
      This array contains one or more compact-serialised JWTs, each
      conforming to the Principal Token schema (Section 4.2.4).
    </t>
    <t>
      For each Principal Token at index <tt>i</tt> (from <tt>0</tt> to
      <tt>n-1</tt> where <tt>n</tt> is the length of <tt>aip_chain</tt>):
    </t>
    <dl newline="false">
      <dt>8a. Valid JWT:</dt>
      <dd>
        The token at index <tt>i</tt> MUST be a valid, well-formed JWT
        conforming to the Principal Token schema (Section 4.2.4). If
        parsing or schema validation fails, reject with
        <tt>delegation_chain_invalid</tt>.
      </dd>

      <dt>8b. delegation_depth Matches Index:</dt>
      <dd>
        The <tt>delegation_depth</tt> claim in token <tt>i</tt> MUST equal
        exactly <tt>i</tt>. (The array is 0-indexed; <tt>aip_chain[0]</tt>
        has <tt>delegation_depth: 0</tt>, etc.) If mismatch, reject with
        <tt>invalid_delegation_depth</tt>.
      </dd>

      <dt>8c. Delegation Depth Does Not Exceed Maximum:</dt>
      <dd>
        The <tt>delegation_depth</tt> of token <tt>i</tt> MUST NOT exceed
        the <tt>max_delegation_depth</tt> value declared in token 0 (the
        root token). If <tt>max_delegation_depth</tt> is absent from token
        0, the default is 3. If <tt>i</tt> exceeds this limit, reject with
        <tt>invalid_delegation_depth</tt>.
      </dd>

      <dt>8d. Signature Verification:</dt>
      <dd>For token at index <tt>i</tt>, extract the <tt>iss</tt> claim:
        For <tt>i = 0</tt>, <tt>iss</tt> MUST equal <tt>principal.id</tt>. Verify signature.
        For <tt>i &gt; 0</tt>, <tt>iss</tt> MUST equal <tt>delegated_by</tt>. Verify signature.
        If verification fails, reject with <tt>delegation_chain_invalid</tt>.</dd>

      <dt>8e. Delegation Chain Linkage:</dt>
      <dd>
        For token at index <tt>i &gt; 0</tt>, verify that
        <tt>delegated_by[i]</tt> equals <tt>sub[i-1]</tt> (the <tt>sub</tt>
        of the previous token). This ensures the chain is continuous: each
        agent is delegated by the previous agent in the chain. If mismatch,
        reject with <tt>delegation_chain_invalid</tt>.
      </dd>

      <dt>8f. Agent Revocation:</dt>
      <dd>
        For each <tt>sub</tt> AID in the chain (at all indices), verify it
        is not revoked using the same Tier-specific revocation check as Step
        7. If any agent in the chain is revoked, reject with
        <tt>agent_revoked</tt>.
      </dd>

      <dt>8g. No Duplicate AIDs:</dt>
      <dd>
        No AID may appear more than once in the chain (no cycles or
        duplicates). If an AID appears at indices <tt>i</tt> and <tt>j</tt>
        with <tt>i != j</tt>, reject with <tt>delegation_chain_invalid</tt>.
      </dd>

      <dt>8h. Token Validity:</dt>
      <dd>For token at index <tt>i</tt>, verify <tt>expires_at</tt> &gt; <tt>issued_at</tt>
        and <tt>expires_at</tt> is in the future. If either check fails, reject with
        <tt>chain_token_expired</tt>.</dd>

      <dt>8i. Consistent Principal:</dt>
      <dd>
        The <tt>principal.id</tt> field MUST be byte-for-byte identical
        across ALL elements in the chain (index 0 through n-1). This
        ensures the chain always traces to the same root principal. If any
        element has a different <tt>principal.id</tt>, reject with
        <tt>delegation_chain_invalid</tt>.
      </dd>

      <dt>8j. Principal is Not an Agent:</dt>
      <dd>
        The <tt>principal.id</tt> from <tt>aip_chain[0]</tt> MUST NOT begin
        with <tt>did:aip:</tt>. Principals are humans or organisations, never
        agents. If <tt>principal.id</tt> uses the <tt>did:aip</tt> method,
        reject with <tt>delegation_chain_invalid</tt>.
      </dd>
    </dl>

    <section numbered="true">
  <name>Step 8 Post-Check A</name>
      <t>
        After validating all elements in the chain, verify that <tt>iss</tt>
        (the issuer of the Credential Token, from the JWT header <tt>kid</tt>)
        MUST equal <tt>aip_chain[n-1].sub</tt> (the <tt>sub</tt> of the last
        element in the chain, i.e., the acting agent's AID). This confirms
        that the agent issuing the token is the leaf of the delegation chain.
        If mismatch, reject with <tt>delegation_chain_invalid</tt>.
      </t>
    </section>

    <section numbered="true">
  <name>Step 8 Post-Check B</name>
      <t>
        For single-element chains (n = 1), verify that <tt>iss</tt> MUST
        equal <tt>sub</tt>. This confirms a non-delegated token issued
        directly by the principal's agent. If mismatch, reject with
        <tt>delegation_chain_invalid</tt>.
      </t>
    </section>
  </section>

  <section anchor="validation-step-9" numbered="true">
  <name>Step 9: Capability Validation</name>
    <t>Verify that the agent's requested scopes are permitted by its Capability Manifest.</t>
    <ol>
      <li>Fetch the Capability Manifest for the agent identified by <tt>iss</tt> from the Registry.
        If unavailable, reject with <tt>manifest_invalid</tt>.</li>
      <li>Verify the manifest signature. If verification fails, reject with <tt>manifest_invalid</tt>.</li>
      <li>Verify <tt>expires_at</tt> is in the future. If expired, reject with <tt>manifest_expired</tt>.</li>
<li>For delegated agents: verify scope inheritance. For each scope, verify chain has permission.</li>
    </ol>
  </section>

  <section anchor="validation-step-9a" numbered="true">
    <name>Step 9a: Scope Verification</name>
    <t>For all agents: verify each scope in <tt>aip_scope</tt> is present in the Capability Manifest.</t>
  </section>

  <section anchor="validation-step-9b" numbered="true">
    <name>Step 9b: Capability Overlay (Conditional)</name>
    <t>If a Capability Overlay exists, verify scope constraints permit the requested operation.</t>
  </section>

  <section anchor="validation-step-10" numbered="true">
  <name>Step 10: DPoP Validation (Conditional)</name>
    <t>
      REMINDER: A token's Tier is determined by its highest-risk scope
      (Section 3). A token containing one Tier 2 scope is a Tier 2 token.
    </t>
    <t>      If <tt>aip_scope</tt> contains any of the following scopes, DPoP
      (Demonstration of Proof-of-Possession) MUST be verified:
    </t>
    <ul>
      <li><tt>transactions</tt> or any scope with prefix <tt>transactions.</tt></li>
      <li><tt>communicate.whatsapp</tt>, <tt>communicate.telegram</tt>,
        <tt>communicate.sms</tt>, <tt>communicate.voice</tt></li>
      <li><tt>filesystem.execute</tt></li>
      <li><tt>spawn_agents.create</tt>, <tt>spawn_agents.manage</tt></li>
    </ul>
    <t>
      If any of these scopes is present, the HTTP request MUST include a
      <tt>DPoP</tt> header containing a Demonstration of Proof-of-Possession
      proof JWT per <xref target="RFC9449"/>. Verify:
    </t>
    <ol>
      <li>The DPoP proof is a valid JWT.</li>
      <li>The <tt>htm</tt> (HTTP method) claim matches the HTTP method of
        the request.</li>
      <li>The <tt>htu</tt> (HTTP URI) claim matches the request URI.</li>
      <li>The <tt>jti</tt> has not been seen before (DPoP-specific replay
        cache, separate from Credential Token <tt>jti</tt> cache, keyed by
        <tt>(kid, jti)</tt>).</li>
      <li>The public key in the <tt>jwk</tt> claim matches the agent's key
        material (used to issue the Credential Token).</li>
    </ol>
    <t>
      If DPoP validation fails at any step, reject with
      <tt>dpop_proof_required</tt> (if proof is missing) or
      <tt>invalid_token</tt> (if proof is malformed or invalid).
    </t>
  </section>

  <section anchor="validation-step-10a" numbered="true">
  <name>Step 10a: Approval Envelope Step Verification (Conditional)</name>
    <t>
      This step applies only if both <tt>aip_approval_id</tt> and
      <tt>aip_approval_step</tt> are present in the Credential Token (the
      Step Execution Token case, Section 4.6.8).
    </t>
    <t>
      The Relying Party MUST call the Registry endpoint <tt>GET
      /v1/approvals/{aip_approval_id}/steps/{n}</tt> where <tt>{n}</tt> is
      the value of <tt>aip_approval_step</tt> verbatim (an integer &gt;= 1, per
      Section 4.6.4; MUST NOT be decremented or adjusted). Verify:
    </t>
    <ol>
      <li><strong>Step Status:</strong> The step's <tt>status</tt> field MUST
        be <tt>"claimed"</tt> (not <tt>"pending"</tt>, <tt>"completed"</tt>,
        etc.). If status is <tt>"pending"</tt>, the step has not been claimed
        and cannot execute. Reject with <tt>approval_step_invalid</tt>.</li>
      <li><strong>Actor Match:</strong> The step's <tt>actor</tt> field MUST
        equal the Credential Token's <tt>sub</tt> (the agent AID). Reject if
        mismatch with <tt>approval_step_invalid</tt>.</li>
      <li><strong>Relying Party Match:</strong> The step's
        <tt>relying_party_uri</tt> MUST match the host of the current HTTP
        request (origin comparison per <xref target="RFC6454"/>: scheme +
        host + port). Reject if mismatch with <tt>approval_step_invalid</tt>.</li>
      <li><strong>Action Hash:</strong> Compute the expected action hash for
        this step per Section 4.6.7. Compare against the step's
        <tt>action_hash</tt> field. If mismatch, reject with
        <tt>approval_step_invalid</tt>. This ensures the approving principal
        authorised the exact action being executed.</li>
    </ol>
  </section>

  <section anchor="validation-step-11" numbered="true">
  <name>Step 11: Tier 3 Enterprise Checks (Conditional)</name>
    <t>       This step applies only to Tier 3 enterprise deployments, which are      declared and documented in the Registry's <tt>/.well-known/aip-registry</tt>
      endpoint (Section 15.1).
    </t>
    <t>
      For Tier 3 operations:
    </t>
    <ol>
      <li><strong>mTLS Client Certificate:</strong> The HTTP connection MUST
        use mutual TLS. Verify that the client certificate's subject DN maps
        to the agent's AID (<tt>iss</tt>). If mismatch or certificate is
        absent, reject with <tt>invalid_token</tt>.</li>
      <li><strong>OCSP Revocation Check:</strong> Perform an OCSP check per
        <xref target="RFC6960"/> on the client certificate to verify it has
        not been revoked at the transport layer. If the certificate is
        revoked, reject with <tt>agent_revoked</tt>.</li>
    </ol>
    <t>
      If either check fails, reject with the appropriate error code
      (<tt>invalid_token</tt> or <tt>agent_revoked</tt>).
    </t>
  </section>

  <section anchor="validation-step-12" numbered="true">
  <name>Step 12: Accept</name>
    <t>
      If all preceding steps pass without rejection, the Relying Party MUST
      accept the token and grant the requested access.
    </t>
  </section>
</section>
    <section anchor="delegation-rules" numbered="true" xml:base="sections/08-delegation.xml">
  <name>Delegation</name>
  <t>     AIP enables hierarchical delegation where a principal authorizes a     primary agent, which may in turn authorize sub-agents under narrowing    scope constraints. Delegation is encoded in the <tt>aip_chain</tt> array
    of the Credential Token, with each Principal Token in the chain
    representing one delegation hop from principal to agent.
  </t>

  <section anchor="delegation-chain" numbered="true">
  <name>Delegation Chain</name>
    <t>
      Every Credential Token MUST include a verifiable principal chain
      linking the acting agent to its root principal via the
      <tt>aip_chain</tt> array. The root principal MUST be a human or
      organizational entity identified by a W3C Decentralized Identifier
      (DID) that does NOT use the <tt>did:aip</tt> method. For example:
      <tt>did:web</tt>, <tt>did:key</tt>, or proprietary DID methods are
      acceptable.
    </t>
    <t>
      The maximum delegation depth is a hard constraint of 10 levels. This
      means an agent may not delegate to a sub-agent if doing so would
      create a chain longer than 10 Principal Tokens (0-indexed from 0 to
      9, or 1-indexed as depths 1 to 10).
    </t>
    <t>
      The default value of <tt>max_delegation_depth</tt> MUST NOT exceed 3.
      When a Principal Token does not explicitly set
      <tt>max_delegation_depth</tt>, implementations MUST treat the default
      as 3. This conservative default prevents accidental authorization
      chains from becoming unmanageably deep; parties requiring deeper
      chains MUST explicitly opt in by setting
      <tt>max_delegation_depth</tt> to a value between 3 and 10.
    </t>
  </section>

  <section anchor="scope-inheritance-rules" numbered="true">
  <name>Capability Scope Rules</name>
    <t>
      Scope inheritance is the mechanism by which child agents are
      constrained to operate within the bounds of their parent's
      authorization. Five core rules (D-1 through D-5) govern this
      relationship; they are defined in Section 4.4 of the AIP
      specification.
    </t>
    <t>
      <strong>Scope Inheritance Rule:</strong> For each scope <tt>s</tt>
      granted to a child agent, <tt>s</tt> MUST be present in the parent's
      Capability Manifest AND all constraint values for <tt>s</tt> in the
      child's manifest MUST be no more permissive (&lt;=) than the corresponding
      values in the parent's manifest.
    </t>
    <t>
      For numeric limits (e.g., <tt>max_single_transaction</tt>,
      <tt>max_daily_value</tt>), the child value MUST be &lt;= the parent value.
      For boolean enables or enum values, the child constraint MUST NOT be
      more permissive than the parent (e.g., child cannot set a flag
      <tt>true</tt> if parent sets it <tt>false</tt>).
    </t>
    <t>
      Implementations MUST enforce this rule at delegation time -- when a
      child agent is registered, the Registry MUST validate that all scopes
      in its Capability Manifest satisfy the inheritance rule relative to
      its immediate parent's manifest. Implementations MAY reject delegation
      requests that violate this rule before they are registered.
    </t>
    <t>
      Relying Parties MUST independently verify this rule during validation
      at Step 9c of the Credential Token validation algorithm (see
      <xref target="validation-step-9"/>). This ensures that even if a
      Registry incorrectly permits a violating delegation, Relying Parties
      will catch it and reject the token.
    </t>
  </section>

  <section anchor="delegation-validation" numbered="true">
  <name>Delegation Validation</name>
    <t>
      When validating a delegated Credential Token, Relying Parties must
      fetch and verify the Capability Manifests of all agents in the
      delegation chain. This section specifies the performance and caching
      constraints for these operations.
    </t>
    <t>
      <strong>Ancestor Manifest Fetch Limits:</strong> Implementations MUST
      NOT fetch more ancestor manifests than the <tt>max_delegation_depth</tt>
      value of the chain's root token (which defaults to 3 when absent).
      This limit prevents accidental O(n^2) fetch patterns in deep delegation
      chains and bounds the performance cost of validation.
    </t>
    <t>
      <strong>Ancestor Manifest Caching for Tier 1:</strong> For Tier 1
      operations (low-risk scopes with bounded-staleness threat model),
      ancestor manifests MAY be cached for a maximum of 60 seconds. This
      cache is per-agent and per-manifest, and MUST respect the 60-second
      TTL. After 60 seconds, a fresh fetch is required.
    </t>
    <t>
      <strong>No-Cache Requirement for Tier 2:</strong> For Tier 2 operations
      (high-risk scopes with real-time threat model), the no-cache
      requirement in the Credential Token validation algorithm (see
      <xref target="validation-step-9"/>) applies to ALL manifests in the
      delegation chain -- including ancestor manifests -- not only the leaf
      agent's manifest. The 60-second ancestor cache MUST NOT be used for
      any manifest appearing in a Tier 2 validation. Every manifest must be
      fetched fresh from the Registry.
    </t>
    <t>
      <strong>Unavailable Manifests:</strong> If an ancestor manifest is
      unavailable or cannot be fetched (due to network failure, Registry
      downtime, or the manifest having been deleted), the Relying Party MUST
      reject the token by returning the error code <tt>manifest_invalid</tt>.
      Partial delegation chains are not acceptable; either all manifests in
      the chain are available and valid, or the token is rejected.
    </t>
  </section>

</section>
    <section anchor="principal-grant" numbered="true" xml:base="sections/09-grant.xml">
  <name>Principal Grant Ceremony (AIP-GRANT)</name>
  <t>
    The AIP-GRANT ceremony provides a standardised protocol for principals
    to authorise AI agents. AIP-GRANT is analogous to the OAuth 2.0
    Authorization Code Flow, adapted for the agent identity use case.
    Two independent implementations following this section MUST
    produce interoperable grant interactions.
  </t>

  <section anchor="grant-overview" numbered="true">
  <name>Overview and Roles</name>
    <t>
      The AIP-GRANT ceremony involves three actors:
    </t>

    <dl>
      <dt>Agent Deployer</dt>
      <dd>
        The party that constructs the GrantRequest and submits the
        Registration Envelope to the Registry. The deployer generates
        the agent's Ed25519 keypair before initiating the grant
        and may be the principal themselves or a service acting
        on the principal's behalf.
      </dd>

      <dt>Principal Wallet</dt>
      <dd>
        Software that holds the principal's DID private key and executes
        the signing ceremony. The wallet MUST verify the deployer's
        signature and MUST obtain explicit human approval before signing.
      </dd>

      <dt>AIP Registry</dt>
      <dd>
        The service that accepts the Registration Envelope. The Registry's
        role is defined in <xref target="registry"/>; it is not modified
        by AIP-GRANT.
      </dd>
    </dl>

    <t>
      The basic flow:
    </t>

    <artwork type="example">
Agent Deployer      Principal Wallet    AIP Registry
     |                      |                 |
     |-- GrantRequest -----&gt; |                 |
     | (capabilities,      |                 |
     |  purpose, nonce)     |                 |
     | [Display consent UI]                 |
     | [Human reviews &amp; signs]              |
     |                      |                 |
     |&lt;-- GrantResponse --- |              |
     | (signed PrincipalToken)            |
     |                      |                 |
     |-- Registration Envelope ---------------------&gt; |
     | (identity + manifest +                 |
     |  principal_token)                      |
     |&lt;-- AID --------------------------------------- |
</artwork>
  </section>

  <section anchor="grant-request" numbered="true">
  <name>GrantRequest Object</name>
    <t>
      The GrantRequest is a JSON object constructed by the Agent Deployer.
      It MUST be signed by the deployer's Ed25519 key when transmitted
      over the Web Redirect or QR Code bindings. The nonce MUST be
      cryptographically random and MUST contain at least 128 bits of
      entropy.
    </t>

                <table>
          <name>GrantRequest Fields</name>
          <thead>
            <tr>
              <th align="left">Field</th>
              <th align="left">Type</th>
              <th align="center">Required</th>
              <th align="left">Constraints</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>grant_request_id</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>pattern: gr:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}</td>
            </tr>
            <tr>
              <td>aip_version</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>MUST be "0.3"</td>
            </tr>
            <tr>
              <td>agent_name</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>maxLength: 64; displayed in consent UI</td>
            </tr>
            <tr>
              <td>agent_type</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>registered namespace value</td>
            </tr>
            <tr>
              <td>model.provider</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>displayed in consent UI</td>
            </tr>
            <tr>
              <td>model.model_id</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>displayed in consent UI</td>
            </tr>
            <tr>
              <td>requested_capabilities</td>
              <td>object</td>
              <td>REQUIRED</td>
              <td>Capability Manifest capabilities sub-schema</td>
            </tr>
            <tr>
              <td>purpose</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>minLength: 1; maxLength: 512</td>
            </tr>
            <tr>
              <td>delegation_valid_for_seconds</td>
              <td>integer</td>
              <td>REQUIRED</td>
              <td>min: 300; max: 31536000</td>
            </tr>
            <tr>
              <td>nonce</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>minLength: 22; cryptographically random</td>
            </tr>
            <tr>
              <td>request_expires_at</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>ISO 8601 UTC</td>
            </tr>
            <tr>
              <td>callback_uri</td>
              <td>string</td>
              <td>CONDITIONAL</td>
              <td>REQUIRED for Web Redirect; MUST use HTTPS</td>
            </tr>
          </tbody>
        </table>

    <t>
      The deployer MUST generate the agent's AID before constructing
      the GrantRequest so that the resulting Principal Token correctly
      names the agent's AID in its sub field.
    </t>
  </section>

  <section anchor="grant-consent" numbered="true">
  <name>Wallet Consent Requirements</name>
    <t>
      The Principal Wallet MUST implement the following requirements
      before signing any grant.
    </t>

    <t>
      <strong>Nonce and expiry checks:</strong> The Principal Wallet
      MUST check request_expires_at against the current clock. If expired,
      it MUST reject with grant_request_expired and MUST NOT show the
      consent UI. The Principal Wallet MUST maintain a record of seen
      grant_request_id values for at least 30 days and MUST reject
      replays with grant_request_replayed.
    </t>

    <t>
      <strong>Mandatory display elements:</strong> The consent UI
      MUST display ALL of the following before presenting the
      sign/decline choice:
    </t>

    <ol>
      <li>Agent name (agent_name) and type (agent_type)</li>
      <li>AI model - provider and model_id</li>
      <li>Purpose - the purpose field verbatim</li>
      <li>Deployer identity - deployer_name and deployer_did</li>
      <li>All requested capabilities in canonical human-readable strings</li>
      <li>Delegation validity - expiry computed from delegation_valid_for_seconds</li>
    </ol>

    <t>
      <strong>Canonical human-readable capability strings:</strong>
    </t>

                <table>
          <name>Canonical Capability Display Strings</name>
          <thead>
            <tr>
              <th align="left">Scope</th>
              <th align="left">Display String</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>email.read</td>
              <td>Read your email messages and metadata</td>
            </tr>
            <tr>
              <td>email.send</td>
              <td>Send email on your behalf</td>
            </tr>
            <tr>
              <td>email.delete</td>
              <td>Permanently delete your email messages - this cannot be undone</td>
            </tr>
            <tr>
              <td>calendar.read</td>
              <td>Read your calendar events</td>
            </tr>
            <tr>
              <td>filesystem.execute</td>
              <td>Execute scripts and commands on your system - HIGH RISK</td>
            </tr>
            <tr>
              <td>transactions</td>
              <td>Make financial transactions up to specified limits</td>
            </tr>
            <tr>
              <td>spawn_agents.create</td>
              <td>Create child AI agents on your behalf</td>
            </tr>
          </tbody>
        </table>
  </section>

  <section anchor="grant-response" numbered="true">
  <name>GrantResponse Object</name>
    <t>
      The GrantResponse is constructed and signed by the Principal Wallet
      after the principal completes the signing ceremony.
    </t>

                <table>
          <name>GrantResponse Fields</name>
          <thead>
            <tr>
              <th align="left">Field</th>
              <th align="center">Required</th>
              <th align="left">Constraints</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>grant_request_id</td>
              <td>REQUIRED</td>
              <td>MUST match original request</td>
            </tr>
            <tr>
              <td>nonce</td>
              <td>REQUIRED</td>
              <td>MUST match original nonce</td>
            </tr>
            <tr>
              <td>status</td>
              <td>REQUIRED</td>
              <td>enum: "approved", "rejected", "partial"</td>
            </tr>
            <tr>
              <td>principal_id</td>
              <td>REQUIRED</td>
              <td>W3C DID of signing principal</td>
            </tr>
            <tr>
              <td>principal_token</td>
              <td>CONDITIONAL</td>
              <td>REQUIRED when status is "approved" or "partial"</td>
            </tr>
          </tbody>
        </table>

    <t>
      <strong>Deployer validation of GrantResponse:</strong> Upon receipt the deployer
      MUST:
    </t>

    <ol>
      <li>Verify grant_request_id matches the original request</li>
      <li>Verify nonce matches - mismatch MUST be treated as forgery</li>
      <li>If status is "rejected", halt - no agent is registered</li>
      <li>Decode and verify the principal_token JWT signature</li>
      <li>Verify the principal_token payload sub matches the agent's AID</li>
    </ol>
  </section>

  <section anchor="grant-transport" numbered="true">
  <name>Transport Bindings</name>
    <t>
      Implementations MUST support the Web Redirect Flow.
    </t>

    <t>
      <strong>Web Redirect Flow:</strong>
    </t>

    <artwork type="example">
https://wallet.example.com/aip-grant
  ?request=&lt;base64url-signed-GrantRequest&gt;
  &amp;aip_version=0.3
</artwork>

    <t>
      After the principal signs, the Principal Wallet delivers the GrantResponse:
    </t>

    <artwork type="example">
POST &lt;callback_uri&gt;
Content-Type: application/json

{GrantResponse JSON}
</artwork>

    <t>
      The callback_uri MUST be pre-registered by the deployer.
      Principal Wallets MUST NOT deliver GrantResponses to unregistered URIs.
    </t>
  </section>

  <section anchor="sub-agent-delegation" numbered="true">
  <name>Sub-Agent Delegation Flow</name>
    <t>
      When a parent agent delegates to a child agent, the parent acts as
      the Principal Wallet. No human consent UI is required.
    </t>

    <t>
      The parent agent MUST:
    </t>

    <ol>
      <li>Verify requested child capabilities are a strict subset of its own
        Capability Manifest (Rule D-1)</li>
      <li>Generate a fresh Ed25519 keypair for the child agent</li>
      <li>Construct and sign the child's Principal Token</li>
      <li>Construct the Registration Envelope for the child</li>
      <li>Submit the Registration Envelope to the Registry</li>
      <li>Provision the child's private key through a secure channel</li>
    </ol>

    <t>
      The parent MUST NOT retain the child's private key after
      successful provisioning. Retaining the child's private key enables
      the parent to forge Credential Tokens in the child's name and
      constitutes a violation of the principle of least privilege.
    </t>
  </section>

  <section anchor="grant-errors" numbered="true">
  <name>AIP-GRANT Error Codes</name>
                <table>
          <name>AIP-GRANT Error Codes</name>
          <thead>
            <tr>
              <th align="left">Code</th>
              <th align="left">Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>grant_request_expired</td>
              <td>request_expires_at has passed</td>
            </tr>
            <tr>
              <td>grant_request_replayed</td>
              <td>grant_request_id seen before</td>
            </tr>
            <tr>
              <td>grant_request_invalid</td>
              <td>GrantRequest malformed or signature failed</td>
            </tr>
            <tr>
              <td>grant_rejected_by_principal</td>
              <td>Principal declined the grant</td>
            </tr>
            <tr>
              <td>grant_nonce_mismatch</td>
              <td>GrantResponse nonce does not match</td>
            </tr>
          </tbody>
        </table>
  </section>

  <section anchor="g1-grant" numbered="true">
  <name>G1: Registry-Mediated Grant Flow</name>
    <t>
      The G1 (Registry-Mediated) grant profile is designed for consumer
      deployments where the agent deployer does not operate its own
      Principal Wallet integration. The AIP Registry brokers the
      consent ceremony on the deployer's behalf.
    </t>

    <t>
      <strong>Actors:</strong> Agent Deployer, AIP Registry, Principal
      (via Registry-hosted or redirected wallet consent UI).
    </t>

    <artwork type="example">
Deployer              Registry             Principal
   |                     |                     |
   |-- POST /v1/grants -&gt;|                     |
   |  (GrantRequest +    |                     |
   |   callback_uri)     |                     |
   |&lt;-- 201 grant_id + --|                     |
   |   wallet_redirect   |                     |
   |                     |                     |
   | [redirect principal to wallet URI]        |
   |                     |&lt;-- authenticates ---|
   |                     |&lt;-- approve/decline -|
   |                     |                     |
   | [Registry signs Principal Token]          |
   |                     |                     |
   |&lt;-- POST callback ---|                     |
   |   (GrantResponse)   |                     |
   |                     |                     |
   |-- GET /v1/grants/id&gt;|                     |
   |&lt;-- GrantResponse ---|                     |
</artwork>

    <t>
      <strong>Step-by-step definition:</strong>
    </t>

    <ol>
      <li>
        The deployer generates the agent's Ed25519 keypair and constructs
        a GrantRequest. For G1, callback_uri is REQUIRED.
      </li>
      <li>The deployer calls POST /v1/grants. See example response.</li>
      <li>
        The deployer redirects the principal to wallet_redirect_uri.
        The Registry presents a consent UI.
      </li>
      <li>
        On approval, the Registry MUST construct the Principal Token
        and store the GrantResponse.
      </li>
      <li>
        The deployer receives the GrantResponse and submits the
        Registration Envelope to POST /v1/agents with grant_tier: "G1".
      </li>
    </ol>

    <t>
      <strong>GET /v1/grants/{grant_id} authorisation:</strong> Only the
      deployer whose deployer_did appears in the original GrantRequest
      MUST be permitted to retrieve the GrantResponse.
    </t>
  </section>
</section>
    <section anchor="approval-envelopes" numbered="true" xml:base="sections/10-approval-envelopes.xml">
  <name>Approval Envelopes</name>
  <t>
    Approval Envelopes enable a single human approval to authorise a
    pre-declared sequence of dependent agent actions. The principal
    approves the complete workflow graph upfront, and each Relying
    Party independently verifies its specific step against the
    Registry without requiring additional human interaction.
  </t>

  <section anchor="approval-motivation" numbered="true">
  <name>Motivation</name>
    <t>
      <strong>The Cascading Approval Problem:</strong> Without Approval
      Envelopes, multi-step agent workflows require the human to
      approve each step independently, eliminating the benefit
      of autonomous agents. For example, an agent places an order
      with an e-commerce platform (step 1), which triggers a
      payment processor (step 2). Without Approval Envelopes,
      each step would require independent approval.
    </t>

    <t>
      <strong>The Token-Expiry-While-Pending Problem:</strong> A
      Credential Token (TTL = 300s for Tier 2) may expire
      while approval is pending. Approval Envelopes decouple
      the approval phase from execution: the envelope waits
      in pending_approval state without requiring a valid
      token, and step-claim tokens are issued at execution
      time.
    </t>
  </section>

  <section anchor="approval-schema" numbered="true">
  <name>Approval Envelope Schema</name>
    <t>
      An Approval Envelope is submitted by the orchestrating
      agent to POST /v1/approvals and approved by the principal
      through their Principal Wallet.
    </t>

                <table>
          <name>Approval Envelope Fields</name>
          <thead>
            <tr>
              <th align="left">Field</th>
              <th align="center">Required</th>
              <th align="left">Constraints</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>approval_id</td>
              <td>REQUIRED</td>
              <td>pattern: apr:[uuid]</td>
            </tr>
            <tr>
              <td>created_by</td>
              <td>REQUIRED</td>
              <td>AID of orchestrating agent</td>
            </tr>
            <tr>
              <td>principal_id</td>
              <td>REQUIRED</td>
              <td>W3C DID; MUST NOT be did:aip</td>
            </tr>
            <tr>
              <td>description</td>
              <td>REQUIRED</td>
              <td>minLength: 1; maxLength: 512</td>
            </tr>
            <tr>
              <td>approval_window_expires_at</td>
              <td>REQUIRED</td>
              <td>ISO 8601 UTC; RECOMMENDED max: 72 hours</td>
            </tr>
            <tr>
              <td>steps</td>
              <td>REQUIRED</td>
              <td>minItems: 1; maxItems: 20</td>
            </tr>
            <tr>
              <td>compensation_steps</td>
              <td>OPTIONAL</td>
              <td>Compensation steps for SAGA rollback</td>
            </tr>
            <tr>
              <td>total_value</td>
              <td>OPTIONAL</td>
              <td>Total financial value; MUST equal sum of step values</td>
            </tr>
            <tr>
              <td>currency</td>
              <td>OPTIONAL</td>
              <td>ISO 4217; pattern: three uppercase ASCII letters</td>
            </tr>
            <tr>
              <td>creator_signature</td>
              <td>REQUIRED</td>
              <td>base64url EdDSA signature</td>
            </tr>
            <tr>
              <td>principal_signature</td>
              <td>CONDITIONAL</td>
              <td>REQUIRED once status is approved</td>
            </tr>
          </tbody>
        </table>

    <t>
      total_value and currency MUST both be present or both absent.
      When present, total_value MUST equal the sum of value fields
      across ALL steps.
    </t>
  </section>

  <section anchor="step-schema" numbered="true">
  <name>Step Schema</name>
    <t>
      Each element in the steps array represents one atomic action
      at one Relying Party.
    </t>

                <table>
          <name>Step Fields</name>
          <thead>
            <tr>
              <th align="left">Field</th>
              <th align="center">Required</th>
              <th align="left">Constraints</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>step_index</td>
              <td>REQUIRED</td>
              <td>1-based; unique within envelope</td>
            </tr>
            <tr>
              <td>actor</td>
              <td>REQUIRED</td>
              <td>AID executing this step</td>
            </tr>
            <tr>
              <td>relying_party_uri</td>
              <td>REQUIRED</td>
              <td>URI of the Relying Party</td>
            </tr>
            <tr>
              <td>action_type</td>
              <td>REQUIRED</td>
              <td>Application-defined; maxLength: 128</td>
            </tr>
            <tr>
              <td>action_hash</td>
              <td>REQUIRED</td>
              <td>sha256:64-hex; see Section 4.6.7</td>
            </tr>
            <tr>
              <td>description</td>
              <td>REQUIRED</td>
              <td>minLength: 1; maxLength: 256</td>
            </tr>
            <tr>
              <td>required</td>
              <td>REQUIRED</td>
              <td>boolean; false = optional step</td>
            </tr>
            <tr>
              <td>triggered_by</td>
              <td>REQUIRED</td>
              <td>null or step_index; 0 forbidden</td>
            </tr>
            <tr>
              <td>value</td>
              <td>OPTIONAL</td>
              <td>Financial value; minimum: 0</td>
            </tr>
            <tr>
              <td>status</td>
              <td>READ-ONLY</td>
              <td>pending | claimed | completed | failed | compensated | skipped</td>
            </tr>
          </tbody>
        </table>

    <t>
      The triggered_by field implements the SAGA DAG. A step
      with triggered_by: null is a root step. A step with
      triggered_by: N may only be claimed after step N reaches
      completed status. Circular dependencies MUST be rejected.
    </t>

    <t>
      Multiple steps MAY share the same triggered_by value,
      enabling parallel execution paths.
    </t>
  </section>

  <section anchor="compensation-schema" numbered="true">
  <name>Compensation Step Schema</name>
    <t>
      Compensation steps define SAGA rollback actions pre-authorised
      by the principal. If a forward step fails, compensation
      actions execute without requiring new human approval.
    </t>

                <table>
          <name>Compensation Step Fields</name>
          <thead>
            <tr>
              <th align="left">Field</th>
              <th align="center">Required</th>
              <th align="left">Constraints</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>compensation_index</td>
              <td>REQUIRED</td>
              <td>0-based index</td>
            </tr>
            <tr>
              <td>actor</td>
              <td>REQUIRED</td>
              <td>AID executing compensation</td>
            </tr>
            <tr>
              <td>relying_party_uri</td>
              <td>REQUIRED</td>
              <td>URI of the Relying Party</td>
            </tr>
            <tr>
              <td>action_type</td>
              <td>REQUIRED</td>
              <td>Application-defined rollback</td>
            </tr>
            <tr>
              <td>action_hash</td>
              <td>REQUIRED</td>
              <td>sha256:64-hex</td>
            </tr>
            <tr>
              <td>description</td>
              <td>REQUIRED</td>
              <td>Plain-language rollback description</td>
            </tr>
          </tbody>
        </table>
  </section>

  <section anchor="approval-lifecycle" numbered="true">
  <name>Approval Envelope Lifecycle</name>
    <t>
      The Registry maintains lifecycle state with valid transitions:
    </t>

    <artwork type="example">
pending_approval --&gt; approved --&gt; executing --&gt; completed
    |              |              |
    v              v              v
rejected    expired     compensating --&gt; compensated
    |
    v
    failed
</artwork>

    <t>
      <strong>Transition rules:</strong>
    </t>

    <dl newline="true">
      <dt>pending_approval to approved</dt>
      <dd>Principal signs via Principal Wallet. Registry stores principal_signature.</dd>

      <dt>pending_approval to rejected</dt>
      <dd>Principal declines.</dd>

      <dt>pending_approval to expired</dt>
      <dd>approval_window_expires_at passes without approval.</dd>

      <dt>approved to executing</dt>
      <dd>First step is claimed. Automatic transition.</dd>

      <dt>executing to completed</dt>
      <dd>All required: true steps reach completed status.</dd>

      <dt>executing to compensating</dt>
      <dd>Any required: true step fails with compensation_index present.</dd>

      <dt>compensating to compensated</dt>
      <dd>All compensation steps complete successfully.</dd>

      <dt>compensating to failed</dt>
      <dd>One or more compensation steps fail. Terminal state.</dd>
    </dl>

    <t>
      Terminal states (no further transitions): completed, compensated,
      failed, rejected, expired.
    </t>
  </section>

  <section anchor="action-hash" numbered="true">
  <name>Action Hash Computation</name>
    <t>
      The action_hash binds the principal's approval to the
      specific content of the action. An agent MUST NOT execute
      a different action than the principal approved.
    </t>

    <t>
      The action_hash is computed as:
    </t>

    <artwork type="example">
action_hash = "sha256:"
  + LCHEX( SHA-256( JCS( action_parameters ) ) )
</artwork>

    <t>
      Where action_parameters contains:
    </t>

    <ul>
      <li>approval_id - The approval_id of the envelope</li>
      <li>step_index - The step_index of this step</li>
      <li>actor - The actor AID</li>
      <li>relying_party_uri - The Relying Party URI</li>
      <li>action_type - The action type</li>
      <li>parameters - Application-defined JSON object</li>
    </ul>

    <t>
      Implementations MUST use an RFC8785-conformant library.
      Financial amounts MUST be represented as JSON numbers (not strings).
    </t>
  </section>

  <section anchor="step-claim" numbered="true">
  <name>Step Claim and Execution Protocol</name>
    <t>
      To execute a step, an agent follows this protocol:
    </t>

    <t>
      <strong>Step 1 - Claim:</strong> The agent calls
      POST /v1/approvals/{id}/steps/{n}/claim with a valid
      Credential Token and action_parameters.
    </t>

    <t>
      The Registry MUST atomically verify all of the following:
    </t>

    <ol>
      <li>Envelope status is approved or executing</li>
      <li>approval_window_expires_at has not passed</li>
      <li>Step status is pending</li>
      <li>All triggered_by steps are completed</li>
      <li>Token iss matches step actor</li>
      <li>Credential Token passes validation</li>
      <li>action_hash matches stored value</li>
    </ol>

    <t>
      If all checks pass, the Registry returns a Step Execution Token:
    </t>

    <artwork type="example">
{
  "iss": "&lt;Registry AID&gt;",
  "sub": "&lt;actor AID&gt;",
  "aud": "&lt;relying_party_uri&gt;",
  "iat": &lt;now&gt;,
  "exp": &lt;now + 300&gt;,
  "jti": "&lt;UUID&gt;",
  "aip_version": "0.3",
  "aip_scope": "&amp;lt;scope array&amp;gt;",
  "aip_chain": "&amp;lt;principal chain array&amp;gt;",
  "aip_approval_id": "&lt;approval_id&gt;",
  "aip_approval_step": &lt;step_index&gt;
}
</artwork>

    <t>
      <strong>Step 2 - Execute:</strong> The agent presents the
      Step Execution Token to the Relying Party.
    </t>

    <t>
      <strong>Step 3 - Complete or Fail:</strong> After execution,
      the agent MUST call /complete or /fail within
      step_claim_timeout_seconds (RECOMMENDED: 600 seconds).
    </t>
  </section>

  <section anchor="saga-compensation" numbered="true">
  <name>SAGA Compensation Semantics</name>
    <t>
      When any required: true step fails after one or more
      earlier steps completed, the Registry MUST:
    </t>

    <ol>
      <li>Transition envelope status to compensating</li>
      <li>Trigger compensation steps in reverse step_index order</li>
      <li>Notify the orchestrating agent</li>
    </ol>

    <t>
      Because the principal pre-approved all compensation
      steps, no additional human interaction is required.
      This is the core SAGA property.
    </t>

    <t>
      If a compensation step itself fails, the envelope
      transitions to failed (terminal). This requires manual
      intervention outside the AIP protocol.
    </t>
  </section>

  <section anchor="approval-validation" numbered="true">
  <name>Approval Envelope Validation Rules</name>
    <t>
      The Registry MUST reject an Approval Envelope at
      submission time if any of the following are true:
    </t>

    <ol>
      <li>principal_id begins with did:aip:</li>
      <li>steps contains circular dependencies in triggered_by</li>
      <li>steps contains duplicate step_index values</li>
      <li>total_value != sum of step values</li>
      <li>step currency != envelope currency</li>
      <li>approval_window_expires_at is in the past</li>
      <li>compensation_index references non-existent index</li>
      <li>creator_signature does not verify</li>
      <li>created_by agent is revoked</li>
      <li>steps contains more than 20 elements</li>
      <li>triggered_by is 0 or non-existent</li>
    </ol>

    <t>
      The Registry MUST also reject envelopes from agents whose
      Capability Manifest does not include spawn_agents.create
      or minimum transactions capability.
    </t>
  </section>
</section>
    <section anchor="principal-chain" xml:base="sections/10-principal-chain.xml">
  <name>Principal Chain</name>
  <t>
    The <tt>principal.id</tt> field MUST be byte-for-byte identical in
    every Principal Token in the <tt>aip_chain</tt> array. Relying
    Parties MUST verify this. An intermediate agent MUST NOT change it,
    substitute its own AID, or modify the original principal DID in any
    way.
  </t>
  <t>
    Every element in <tt>aip_chain</tt> is a compact-serialised JWT whose
    payload conforms to the Principal Token schema (Section 4.2.4).
    Elements are ordered root-to-leaf. The maximum <tt>aip_chain</tt>
    length is 11. Implementations MUST reject tokens with
    <tt>aip_chain</tt> length exceeding 11.
  </t>
  <t>
    Cryptographic non-repudiation: Every Credential Token carries a
    delegation chain in which each link is signed by the delegating
    party's private key. Because <tt>principal.id</tt> is byte-identical
    across all chain elements and is bound to the signing key, every
    agent action is cryptographically attributable to the human or
    organisational principal that authorised it. This satisfies the
    non-repudiation requirement of <xref target="SP-800-63-4"/> Section
    11.
  </t>
</section>
    <section anchor="reputation" xml:base="sections/11-reputation.xml">
  <name>Reputation and Endorsements</name>
  <section anchor="endorsement-object">
    <name>Endorsement Object</name>
    <t>Any Relying Party or agent MAY submit a signed Endorsement Object to the Registry after a completed interaction. The schema is in Section 4.2.7.</t>
    <t>The Registry MUST verify every submitted Endorsement Object signature. Only <tt>success</tt> and <tt>partial</tt> outcomes increment <tt>endorsement_count</tt>. <tt>failure</tt> increments <tt>incident_count</tt>.</t>
    <t>Completed Approval Envelope steps SHOULD generate Endorsement Objects. When an Approval Envelope reaches <tt>completed</tt> status, the orchestrator SHOULD submit an Endorsement Object for each agent that executed a step successfully.</t>
  </section>
  <section anchor="reputation-scoring">
    <name>Reputation Scoring</name>
    <t>The Registry MUST expose reputation data for every registered AID at <tt>GET /v1/agents/{aid}/reputation</tt>. Required fields: <tt>registration_date</tt>, <tt>task_count</tt>, <tt>successful_task_count</tt>, <tt>endorsement_count</tt>, <tt>incident_count</tt>, <tt>revocation_history</tt>, <tt>last_active</tt>.</t>
    <t>The Registry MAY expose a reference advisory score labelled <tt>advisory_only: true</tt>. Relying Parties MUST NOT treat it as normative. AIP standardises reputation inputs, not the scoring formula.</t>
    <t>Reputation Non-Transferability: Reputation is bound to a specific AID. Implementations MUST NOT transfer reputation from revoked to new AIDs. Endorsements from AIDs with current <tt>full_revoke</tt> or <tt>principal_revoke</tt> status MUST NOT be weighted.</t>
  </section>
</section>
    <section anchor="revocation" numbered="true" xml:base="sections/11-revocation.xml">
  <name>Revocation Management</name>
  <t>
    Revocation provides the kill switch for agent identity. When
    an agent is revoked, all its Credential Tokens become
    invalid and the agent can no longer act on behalf of
    any principal.
  </t>

  <section anchor="revocation-object" numbered="true">
  <name>Revocation Object</name>
    <t>
      Revocation is performed by submitting a signed Revocation Object
      to POST /v1/revocations.
    </t>

                <table>
          <name>Revocation Types</name>
          <thead>
            <tr>
              <th align="left">Type</th>
              <th align="left">Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>full_revoke</td>
              <td>Permanently revokes the AID.</td>
            </tr>
            <tr>
              <td>scope_revoke</td>
              <td>Removes specific scopes from Capability Manifest. Does NOT invalidate outstanding tokens.</td>
            </tr>
            <tr>
              <td>delegation_revoke</td>
              <td>Invalidates delegation chains rooted at target. Child agents lose authority.</td>
            </tr>
            <tr>
              <td>principal_revoke</td>
              <td>Issued by root principal. Equivalent to full_revoke plus child propagation.</td>
            </tr>
          </tbody>
        </table>
  </section>

  <section anchor="crl" numbered="true">
  <name>Certificate Revocation List (CRL)</name>
    <t>
      The Registry MUST expose a CRL at GET /v1/crl. The CRL
      MUST be updated within 15 minutes of a new Revocation
      Object being accepted. The CRL endpoint MUST be served
      from a CDN or distributed infrastructure.
    </t>
  </section>

  <section anchor="revocation-checking" numbered="true">
  <name>Revocation Checking</name>
    <t>
      A token's Tier is determined by its highest-risk
      scope. A token with one Tier 2 scope and any
      number of Tier 1 scopes is a Tier 2 token.
    </t>

    <t>
      <strong>Tier 1 - Bounded-staleness:</strong> TTL is 3600 seconds.
      Validate against CRL at issuance time. CRL MUST be refreshed
      every 15 minutes.
    </t>

    <t>
      <strong>Tier 2 - Real-time revocation:</strong> For scopes:
      transactions.*, communicate.*, filesystem.execute,
      spawn_agents.create, spawn_agents.manage. TTL is 300 seconds.
      Real-time Registry check on EVERY request. MUST NOT cache
      revocation status. DPoP MUST be verified. If Registry
      unreachable: MUST deny and return registry_unavailable.
    </t>

    <t>
      <strong>Tier 3 - Enterprise:</strong> MUST use mTLS. MUST support
      OCSP per RFC 6960. Tier 3 supplements, not replaces,
      Tier 2.
    </t>

    <t>
      <strong>Child Agent Propagation:</strong> When the
      Registry processes a Revocation Object with
      propagate_to_children: true, the Registry MUST
      recursively revoke descendants within 15 seconds.
    </t>

    <t>
      Replica Registries MUST synchronise within 45 seconds.
      Combined end-to-end propagation MUST NOT exceed
      60 seconds.
    </t>

    <t>
      <strong>Approval Envelope interaction:</strong> When an
      agent AID is revoked, the Registry MUST transition
      all Approval Envelopes in pending_approval, approved,
      or executing status to failed. Unclaimed steps
      for that actor MUST be marked failed. In-progress
      claims MUST be treated as failed; the Registry
      MUST initiate compensation if applicable.
    </t>
  </section>

  <section anchor="rpnp" numbered="true">
  <name>Registry Push Notification Protocol (RPNP)</name>
    <t>
      RPNP is an OPTIONAL Registry capability for real-time
      revocation event delivery.
    </t>

    <section anchor="rpnp-overview" numbered="true">
  <name>Overview</name>
      <t>
        When RPNP is implemented:
      </t>

      <ul>
        <li>Registry MUST deliver push events within 5 seconds</li>
        <li>Push payloads MUST be signed by the Registry's administrative key</li>
        <li>Subscriber authentication MUST be verified at subscription time</li>
      </ul>

      <t>
        For subscribing Relying Parties, the effective revocation
        window is the RPNP delivery latency (at most 5 seconds)
        rather than the CRL refresh interval. RPNP does not
        replace CRL; it supplements it.
      </t>
    </section>

    <section anchor="rpnp-subscription" numbered="true">
  <name>Subscription</name>
      <t>
        A Relying Party subscribes by calling POST /v1/subscriptions:
      </t>

                        <table>
              <name>Subscription Fields</name>
              <thead>
                <tr>
                  <th align="left">Field</th>
                  <th align="center">Required</th>
                  <th align="left">Constraints</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>subscriber_did</td>
                  <td>REQUIRED</td>
                  <td>MUST be did:web or did:aip</td>
                </tr>
                <tr>
                  <td>event_types</td>
                  <td>REQUIRED</td>
                  <td>full_revoke, scope_revoke, delegation_revoke, etc.</td>
                </tr>
                <tr>
                  <td>scope_filter</td>
                  <td>REQUIRED</td>
                  <td>aid, principal, or all</td>
                </tr>
                <tr>
                  <td>targets</td>
                  <td>CONDITIONAL</td>
                  <td>REQUIRED when scope_filter is aid or principal</td>
                </tr>
                <tr>
                  <td>webhook_uri</td>
                  <td>REQUIRED</td>
                  <td>HTTPS URI</td>
                </tr>
                <tr>
                  <td>hmac_secret_hash</td>
                  <td>REQUIRED</td>
                  <td>SHA-256 hash of shared secret</td>
                </tr>
                <tr>
                  <td>subscription_expires_at</td>
                  <td>REQUIRED</td>
                  <td>ISO 8601 UTC; max 90 days</td>
                </tr>
              </tbody>
            </table>

      <t>
        The subscription request MUST be authenticated via
        DPoP proof. Un-signed requests MUST be rejected
        with subscription_auth_required.
      </t>
    </section>

    <section anchor="rpnp-payload" numbered="true">
  <name>Push Event Payload</name>
      <t>
        Push events are delivered as HTTP POST to the subscriber's
        webhook_uri. The body is a compact-serialised JWT signed
        by the Registry's administrative key.
      </t>

                        <table>
              <name>RPNP JWT Fields</name>
              <thead>
                <tr>
                  <th align="left">Header Field</th>
                  <th align="left">Value</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>typ</td>
                  <td>AIP-RPNP+JWT</td>
                </tr>
                <tr>
                  <td>alg</td>
                  <td>EdDSA</td>
                </tr>
                <tr>
                  <td>kid</td>
                  <td>Registry AID key ID</td>
                </tr>
              </tbody>
            </table>

                        <table>
              <name>RPNP Payload Fields</name>
              <thead>
                <tr>
                  <th align="left">Payload Field</th>
                  <th align="left">Description</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>iss</td>
                  <td>Registry AID</td>
                </tr>
                <tr>
                  <td>sub</td>
                  <td>Affected AID or engagement ID</td>
                </tr>
                <tr>
                  <td>iat</td>
                  <td>Unix timestamp</td>
                </tr>
                <tr>
                  <td>jti</td>
                  <td>UUID v4; unique event ID</td>
                </tr>
                <tr>
                  <td>event_type</td>
                  <td>One of subscribed types</td>
                </tr>
                <tr>
                  <td>event_data</td>
                  <td>Event-specific data</td>
                </tr>
              </tbody>
            </table>

      <t>
        The request MUST include an X-AIP-Signature header
        containing HMAC-SHA256(shared_secret, body). The
        subscriber MUST verify both the JWT signature
        and the HMAC.
      </t>
    </section>

    <section anchor="rpnp-delivery" numbered="true">
  <name>Delivery Guarantees</name>
      <ol>
        <li>Registry MUST deliver push events within 5 seconds</li>
        <li>
          On delivery failure: retry with exponential backoff
          (1s, 2s, 4s - 3 attempts minimum)
        </li>
        <li>
          After 3 consecutive failures: mark subscription as
          degraded. Subscriber falls back to CRL.
        </li>
        <li>Subscribers MUST reject duplicate jti within 60 seconds</li>
      </ol>
    </section>
  </section>
</section>
    <section anchor="lifecycle-states" xml:base="sections/12-lifecycle-states.xml">
  <name>Lifecycle States</name>
  <t>
    An AID has exactly two lifecycle states: <tt>active</tt> and
    <tt>revoked</tt>. AIP does not define an <tt>inactive</tt> status;
    the Dead Man's Switch mechanism (Section 17.8) uses
    <tt>full_revoke</tt>.
  </t>
  <t>
    An AID MUST remain valid until explicitly revoked. Revoked AIDs MUST
    NOT be reused. Key rotation preserves the AID but changes the active
    key. Outstanding tokens signed under the previous key remain valid
    until their <tt>exp</tt>.
  </t>
  <t>
    Ephemeral agents MUST have a non-null <tt>task_id</tt>. They MUST be
    explicitly revoked on task completion. The Registry SHOULD
    auto-revoke ephemeral agents when their stored <tt>expires_at</tt>
    passes.
  </t>
</section>
    <section anchor="registry" numbered="true" xml:base="sections/12-registry.xml">
  <name>Registry Interface</name>
  <t>
    A conformant AIP Registry MUST implement the HTTP endpoints
    defined in this section.
  </t>

  <section anchor="required-endpoints" numbered="true">
  <name>Required Endpoints</name>
                <table>
          <thead>
            <tr>
              <th align="left">Method</th>
              <th align="left">Path</th>
              <th align="left">Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>POST</td>
              <td>/v1/agents</td>
              <td>Register a new AID (Registration Envelope)</td>
            </tr>
            <tr>
              <td>GET</td>
              <td>/v1/agents/{aid}</td>
              <td>Retrieve Agent Identity or DID Document</td>
            </tr>
            <tr>
              <td>PUT</td>
              <td>/v1/agents/{aid}</td>
              <td>Key rotation only</td>
            </tr>
            <tr>
              <td>POST</td>
              <td>/v1/revocations</td>
              <td>Submit a Revocation Object</td>
            </tr>
            <tr>
              <td>GET</td>
              <td>/v1/crl</td>
              <td>Retrieve Certificate Revocation List</td>
            </tr>
            <tr>
              <td>GET</td>
              <td>/.well-known/aip-registry</td>
              <td>Retrieve Registry configuration</td>
            </tr>
          </tbody>
        </table>
  </section>

  <section anchor="aid-url-encoding" numbered="true">
  <name>AID URL Encoding</name>
    <t>
      The AID MUST be URL-safe. The did:aip: prefix and all
      colons MUST be percent-encoded in URL paths:
    </t>

    <artwork type="example">
GET /v1/agents/did:aip%3Anamespace%3A32hexcharacters
</artwork>
  </section>

  <section anchor="response-format" numbered="true">
  <name>Response Format</name>
    <t>
      All responses MUST use Content-Type: application/json.
      Timestamps MUST be in ISO 8601 format.
    </t>
  </section>

  <section anchor="approval-endpoints" numbered="true">
  <name>Approval Envelope Endpoints</name>
                <table>
          <thead>
            <tr>
              <th align="left">Method</th>
              <th align="left">Path</th>
              <th align="left">Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>POST</td>
              <td>/v1/approvals</td>
              <td>Submit Approval Envelope</td>
            </tr>
            <tr>
              <td>GET</td>
              <td>/v1/approvals/{id}</td>
              <td>Retrieve envelope</td>
            </tr>
            <tr>
              <td>POST</td>
              <td>/v1/approvals/{id}/steps/{n}/claim</td>
              <td>Claim a step</td>
            </tr>
            <tr>
              <td>POST</td>
              <td>/v1/approvals/{id}/steps/{n}/complete</td>
              <td>Mark step complete</td>
            </tr>
            <tr>
              <td>POST</td>
              <td>/v1/approvals/{id}/steps/{n}/fail</td>
              <td>Mark step failed</td>
            </tr>
          </tbody>
        </table>
  </section>

  <section anchor="oauth-endpoints" numbered="true">
  <name>OAuth 2.1 Authorization Server</name>
    <t>
      The Registry MAY implement an OAuth 2.1 Authorization Server
      for token exchange (Section 7.5). The token endpoint:
    </t>

    <artwork type="example">
POST /v1/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&amp;subject_token=&lt;AIP Credential Token&gt;
&amp;subject_token_type=urn:ietf:params:oauth:token-type:jwt
&amp;resource=&lt;target URI&gt;
&amp;scope=urn:aip:scope:email.read
</artwork>
  </section>

  <section anchor="scope-map" numbered="true">
  <name>Scope Map</name>
    <t>
      Scope URIs follow the URN namespace urn:aip:scope:*:
    </t>

    <artwork type="example">
urn:aip:scope:email.read
urn:aip:scope:email.write
urn:aip:scope:email.send
urn:aip:scope:calendar.read
urn:aip:scope:transactions
urn:aip:scope:filesystem.execute
urn:aip:scope:spawn_agents.create
</artwork>
  </section>

  <section anchor="rpnp-subscriptions" numbered="true">
  <name>RPNP Subscription Endpoints</name>
                <table>
          <thead>
            <tr>
              <th align="left">Method</th>
              <th align="left">Path</th>
              <th align="left">Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>POST</td>
              <td>/v1/subscriptions</td>
              <td>Create subscription</td>
            </tr>
            <tr>
              <td>GET</td>
              <td>/v1/subscriptions/{id}</td>
              <td>Get subscription</td>
            </tr>
            <tr>
              <td>DELETE</td>
              <td>/v1/subscriptions/{id}</td>
              <td>Delete subscription</td>
            </tr>
          </tbody>
        </table>
  </section>
</section>
    <section anchor="error-handling" xml:base="sections/13-error-handling.xml">
  <name>Error Handling</name>

  <section anchor="error-response-format">
    <name>Error Response Format</name>
    <t>EXAMPLE (informative):</t>
    <artwork type="json">
{
  "error": "&lt;error_code&gt;",
  "error_description": "&lt;human-readable description&gt;",
  "error_uri": "https://provai.dev/errors/&lt;error_code&gt;"
}
    </artwork>
    <t>Implementations MUST use exact string values for `error`.</t>
    <t>Implementations MUST NOT return HTTP 200 for error conditions.</t>
  </section>

  <section anchor="error-codes">
    <name>Standard Error Codes</name>
    <dl newline="false">
      <dt><tt>invalid_token</tt></dt><dd><t>HTTP 401. Token malformed, invalid signature, or invalid claims.</t></dd>
      <dt><tt>token_expired</tt></dt><dd><t>HTTP 401. Token <tt>exp</tt> is in the past.</t></dd>
      <dt><tt>token_replayed</tt></dt><dd><t>HTTP 401. Token <tt>jti</tt> seen before within validity window.</t></dd>
      <dt><tt>dpop_proof_required</tt></dt><dd><t>HTTP 401. DPoP proof absent or invalid.</t></dd>
      <dt><tt>delegation_chain_refresh_required</tt></dt><dd><t>HTTP 401. Principal Token in chain has expired; agent must re-establish delegation.</t></dd>
      <dt><tt>agent_revoked</tt></dt><dd><t>HTTP 403. The AID has been revoked.</t></dd>
      <dt><tt>insufficient_scope</tt></dt><dd><t>HTTP 403. Operation not within granted scopes.</t></dd>
      <dt><tt>invalid_delegation_depth</tt></dt><dd><t>HTTP 403. <tt>delegation_depth</tt> mismatch or exceeds <tt>max_delegation_depth</tt>.</t></dd>
      <dt><tt>chain_token_expired</tt></dt><dd><t>HTTP 403. Principal Token in <tt>aip_chain</tt> expired.</t></dd>
      <dt><tt>delegation_chain_invalid</tt></dt><dd><t>HTTP 403. Structural error in delegation chain.</t></dd>
      <dt><tt>manifest_invalid</tt></dt><dd><t>HTTP 403. Capability Manifest signature failed or unavailable.</t></dd>
      <dt><tt>manifest_expired</tt></dt><dd><t>HTTP 403. Capability Manifest <tt>expires_at</tt> passed.</t></dd>
      <dt><tt>approval_envelope_invalid</tt></dt><dd><t>HTTP 400. Approval Envelope malformed, circular dependencies, or hash mismatch.</t></dd>
      <dt><tt>approval_envelope_expired</tt></dt><dd><t>HTTP 403. <tt>approval_window_expires_at</tt> has passed.</t></dd>
      <dt><tt>approval_not_found</tt></dt><dd><t>HTTP 404. Approval Envelope ID not found.</t></dd>
      <dt><tt>approval_step_prerequisites_unmet</tt></dt><dd><t>HTTP 403. <tt>triggered_by</tt> step not yet completed.</t></dd>
      <dt><tt>approval_step_already_claimed</tt></dt><dd><t>HTTP 409. Step already claimed by another actor.</t></dd>
      <dt><tt>approval_step_action_mismatch</tt></dt><dd><t>HTTP 403. Presented <tt>action_parameters</tt> hash does not match stored <tt>action_hash</tt>.</t></dd>
      <dt><tt>approval_step_invalid</tt></dt><dd><t>HTTP 403. Step Execution Token verification against Registry failed.</t></dd>
      <dt><tt>grant_request_expired</tt></dt><dd><t>HTTP 400. AIP-GRANT <tt>request_expires_at</tt> passed.</t></dd>
      <dt><tt>grant_request_replayed</tt></dt><dd><t>HTTP 400. AIP-GRANT <tt>grant_request_id</tt> seen before.</t></dd>
      <dt><tt>grant_request_invalid</tt></dt><dd><t>HTTP 400. GrantRequest malformed or signature failed.</t></dd>
      <dt><tt>grant_rejected_by_principal</tt></dt><dd><t>HTTP 403. Principal declined the grant.</t></dd>
      <dt><tt>grant_nonce_mismatch</tt></dt><dd><t>HTTP 400. GrantResponse nonce does not match.</t></dd>
      <dt><tt>unknown_aid</tt></dt><dd><t>HTTP 404. AID not registered in any accessible Registry.</t></dd>
      <dt><tt>registry_unavailable</tt></dt><dd><t>HTTP 503. Registry could not be reached.</t></dd>
      <dt><tt>rate_limit_exceeded</tt></dt><dd><t>HTTP 429. Rate limit for this operation exceeded; see Section 14.</t></dd>
      <dt><tt>revocation_stale</tt></dt><dd><t>HTTP 403. Tier 2 operation with cached revocation status.</t></dd>
      <dt><tt>dpop_required</tt></dt><dd><t>HTTP 401. Tier 2 operation without DPoP proof.</t></dd>
      <dt><tt>mtls_required</tt></dt><dd><t>HTTP 403. Tier 3 operation without mTLS.</t></dd>
      <dt><tt>invalid_scope</tt></dt><dd><t>HTTP 400. Token contains retired bare <tt>spawn_agents</tt> scope.</t></dd>
      <dt><tt>principal_did_method_forbidden</tt></dt><dd><t>HTTP 403. Principal uses <tt>did:key</tt> for Tier 2 scope.</t></dd>
      <dt><tt>identity_proofing_insufficient</tt></dt><dd><t>HTTP 403. G3 identity proofing below requested <tt>acr_values</tt>.</t></dd>
      <dt><tt>grant_not_found</tt></dt><dd><t>HTTP 404. G1 <tt>grant_id</tt> not found or expired.</t></dd>
      <dt><tt>grant_deployer_mismatch</tt></dt><dd><t>HTTP 403. G1 <tt>grant_id</tt> does not match deployer.</t></dd>
      <dt><tt>pkce_required</tt></dt><dd><t>HTTP 400. G3 authorization request missing PKCE.</t></dd>
      <dt><tt>registry_untrusted</tt></dt><dd><t>HTTP 403. Registry does not match principal DID-Document-declared Registry.</t></dd>
      <dt><tt>overlay_exceeds_manifest</tt></dt><dd><t>HTTP 400. Overlay violates CO-1 attenuation rule.</t></dd>
      <dt><tt>overlay_issuer_invalid</tt></dt><dd><t>HTTP 400. Overlay issuer uses <tt>did:key</tt>.</t></dd>
      <dt><tt>overlay_version_conflict</tt></dt><dd><t>HTTP 409. Overlay version not strictly increasing.</t></dd>
      <dt><tt>overlay_signature_invalid</tt></dt><dd><t>HTTP 400. Overlay signature verification failure.</t></dd>
      <dt><tt>engagement_terminated</tt></dt><dd><t>HTTP 403. Engagement has been terminated or completed.</t></dd>
      <dt><tt>engagement_suspended</tt></dt><dd><t>HTTP 403. Engagement is currently suspended.</t></dd>
      <dt><tt>engagement_participant_removed</tt></dt><dd><t>HTTP 403. Agent removed from engagement.</t></dd>
      <dt><tt>engagement_gate_pending</tt></dt><dd><t>HTTP 403. Required approval gate not yet approved.</t></dd>
      <dt><tt>engagement_not_found</tt></dt><dd><t>HTTP 404. Engagement ID not found.</t></dd>
      <dt><tt>engagement_countersign_required</tt></dt><dd><t>HTTP 400. Missing required countersignature.</t></dd>
      <dt><tt>change_log_immutable</tt></dt><dd><t>HTTP 400. Attempt to modify change log entry.</t></dd>
      <dt><tt>change_log_sequence_invalid</tt></dt><dd><t>HTTP 400. Out-of-sequence change log append.</t></dd>
      <dt><tt>subscription_auth_required</tt></dt><dd><t>HTTP 401. RPNP subscription without DPoP.</t></dd>
      <dt><tt>subscription_scope_forbidden</tt></dt><dd><t>HTTP 403. <tt>scope_filter: "all"</tt> rejected by Registry policy.</t></dd>
      <dt><tt>invalid_webhook_uri</tt></dt><dd><t>HTTP 400. Webhook URI not HTTPS.</t></dd>
      <dt><tt>subscription_limit_exceeded</tt></dt><dd><t>HTTP 429. RPNP subscription limit reached.</t></dd>
      <dt><tt>invalid_target</tt></dt><dd><t>HTTP 400. Token exchange resource not registered.</t></dd>
    </dl>
  </section>

  <section anchor="error-detail-types">
    <name>Error Detail Types</name>
    <t>For `registry_unavailable` (503): SHOULD include `Retry-After` per <xref target="RFC9110"/>.</t>
    <t>For `rate_limit_exceeded` (429): MUST include `Retry-After` per <xref target="RFC6585"/> Section 4 and rate limit headers per Section 14.1.</t>
    <t>For `delegation_chain_refresh_required` (401): the response SHOULD include an `error_description` indicating which delegation depth's Principal Token has expired, to help the agent identify which delegation level to renew.</t>
  </section>
</section>
    <section anchor="security" numbered="true" xml:base="sections/13-security.xml">
  <name>Security Considerations</name>
  <t>
    This section describes the security considerations for
    AIP implementations, including the threat model, cryptographic
    requirements, and recommended mitigations.
  </t>

  <section anchor="threat-model" numbered="true">
  <name>Threat Model</name>
    <t>
      AIP identifies the following threat categories:
    </t>

    <dl newline="true">
      <dt>TS-1: Token Replay</dt>
      <dd>An adversary reuses a captured Credential Token. Mitigation: JTI replay cache.</dd>

      <dt>TS-2: Key Compromise</dt>
      <dd>An adversary steals an agent's private key. Mitigation: Key rotation, HSM storage.</dd>

      <dt>TS-3: Delegation Escalation</dt>
      <dd>A child agent exceeds granted scope. Mitigation: Rule D-1 (Scope Inheritance), Step 9c validation.</dd>

      <dt>TS-4: Registry Impersonation</dt>
      <dd>A malicious Registry serves fake revocation status. Mitigation: Well-known configuration, key pinning.</dd>

      <dt>TS-5: Principal Impersonation</dt>
      <dd>An adversary forges a Principal Token. Mitigation: DID resolution verification.</dd>

      <dt>TS-6: Revocation Delay</dt>
      <dd>The gap between revocation issue and propagation. Mitigation: Real-time RPNP for Tier 2.</dd>

      <dt>TS-7: Token Theft</dt>
      <dd>An adversary intercepts a token in transit. Mitigation: TLS 1.2+, DPoP.</dd>

      <dt>TS-8: Child Agent Self-Replication</dt>
      <dd>An agent with spawn_agents.create creates children during TTL window. Mitigation: Tier 2 with propagate_to_children.</dd>

      <dt>TS-9: Action Hash Manipulation</dt>
      <dd>An agent executes different action than approved. Mitigation: Action hash verification at claim time.</dd>
    </dl>
  </section>

  <section anchor="crypto-requirements" numbered="true">
  <name>Cryptographic Requirements</name>
                <table>
          <name>Mandatory-to-Implement Cryptography</name>
          <thead>
            <tr>
              <th align="left">Operation</th>
              <th align="left">Algorithm</th>
              <th align="left">Specification</th>
              <th align="center">Status</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Signing / Verification</td>
              <td>Ed25519 (EdDSA)</td>
              <td><xref target="RFC8037"/></td>
              <td>MUST</td>
            </tr>
            <tr>
              <td>Hashing</td>
              <td>SHA-256</td>
              <td><xref target="FIPS-180-4"/></td>
              <td>MUST</td>
            </tr>
            <tr>
              <td>Key representation</td>
              <td>JWK</td>
              <td><xref target="RFC7517"/></td>
              <td>MUST</td>
            </tr>
            <tr>
              <td>Key exchange (future)</td>
              <td>X25519</td>
              <td><xref target="RFC7748"/></td>
              <td>MUST</td>
            </tr>
          </tbody>
        </table>

    <t>
      Optional suites: ES256 (ECDSA P-256) per <xref target="RFC7518"/>
      for WebAuthn compatibility; RS256 (RSA-PKCS1) per
      <xref target="RFC7518"/> for legacy enterprise only - MUST NOT be
      the sole supported algorithm; RSA keys MUST be at least 2048 bits.
    </t>

    <t>
      Prohibited: none, HS256/384/512, RS512, MD5, SHA-1.
      The alg header MUST be explicitly specified.
    </t>
  </section>

  <section anchor="dpop" numbered="true">
  <name>Proof-of-Possession (DPoP)</name>
    <t>
      DPoP is REQUIRED for transactions.*, communicate.*, and
      filesystem.execute scopes. DPoP proofs MUST use EdDSA.
    </t>

    <t>
      <strong>DPoP Proof Header:</strong>
    </t>

    <artwork type="example">
{
  "typ": "dpop+jwt",
  "alg": "EdDSA",
  "jwk": {
    "kty": "OKP",
    "crv": "Ed25519",
    "x": "&lt;base64url public key&gt;",
    "kid": "&lt;DID URL&gt;"
  }
}
</artwork>

    <t>
      <strong>DPoP Proof Payload:</strong>
    </t>

    <artwork type="example">
{
  "jti": "&lt;UUID v4&gt;",
  "htm": "&lt;HTTP method, uppercase&gt;",
  "htu": "&lt;scheme + host + path&gt;",
  "iat": "&lt;Unix timestamp&gt;",
  "ath": "&lt;BASE64URL(SHA-256(token)))&gt;"
}
</artwork>

    <t>
      Relying Party validation: Verify alg is EdDSA, verify jwk.kid
      matches token kid, verify htm/htu/iat, check jti replay,
      verify ath, verify signature.
    </t>
  </section>

  <section anchor="key-management" numbered="true">
  <name>Key Management</name>
    <t>
      Private keys MUST NOT be stored in plaintext on disk,
      transmitted in protocol messages, or included in logs.
      Private keys SHOULD be stored in HSM, secure enclave,
      or OS-level keychain.
    </t>

    <t>
      <strong>Key rotation:</strong> Generate new keypair, increment version,
      include previous_key_signature signed by retiring key,
      submit to Registry, retain retiring key until outstanding
      tokens expire.
    </t>

    <t>
      <strong>Key compromise response:</strong> Immediately revoke with
      reason: key_compromised, register new AID with new
      keypair, re-establish delegations.
    </t>
  </section>

  <section anchor="token-security" numbered="true">
  <name>Token Security</name>
    <t>
      All tokens MUST be transmitted over TLS 1.2 or higher
      (TLS 1.3 RECOMMENDED). MUST NOT transmit over
      unencrypted HTTP.
    </t>

    <t>
      <strong>JTI replay cache:</strong> Keyed by (iss, jti);
      window at least max TTL for served scopes;
      shared cache for distributed deployments.
    </t>

    <t>
      <strong>Audience validation:</strong> MUST validate aud claim.
      Mismatch returns invalid_token.
    </t>
  </section>

  <section anchor="delegation-security" numbered="true">
  <name>Delegation Chain Security</name>
    <t>
      Default max_delegation_depth MUST NOT exceed 3.
      Hard cap is 10. Circular delegation MUST be detected
      and rejected.
    </t>

    <t>
      Ephemeral agents MUST have non-null task_id. Registry
      SHOULD auto-revoke when expires_at passes.
    </t>
  </section>

  <section anchor="registry-security" numbered="true">
  <name>Registry Security</name>
    <t>
      All write operations MUST be authenticated.
      Revocation issued_by MUST be verified as in the target's
      delegation chain.
    </t>

    <t>
      Registry read endpoints MUST target 99.9% uptime.
      CRL MUST be served from CDN.
    </t>

    <t>
      Relying Parties MUST NOT trust the aip_registry
      claim in a Credential Token as sole indicator.
      For Tier 2, the authoritative Registry MUST be
      verified via the root principal's DID Document.
    </t>
  </section>

  <section anchor="revocation-security" numbered="true">
  <name>Revocation Security</name>
    <t>
      Every Revocation Object MUST be signed. Unsigned
      or invalidly signed objects MUST be rejected.
    </t>

    <t>
      <strong>Dead Man's Switch (Optional):</strong> Registry
      MAY issue full_revoke for agents that fail to submit
      a signed heartbeat within configured window
      (RECOMMENDED: 24 hours).
    </t>

    <t>
      <strong>Timing attack mitigation:</strong> Short sensitive-scope
      TTLs (max 300s) limit exploitation window.
    </t>
  </section>

  <section anchor="envelope-security" numbered="true">
  <name>Approval Envelope Security</name>
    <t>
      <strong>Action hash integrity:</strong> The action_hash binds
      principal approval to specific action parameters.
      Registry MUST reject any claim where recomputed
      hash does not match stored action_hash.
    </t>

    <t>
      <strong>Double-spend prevention:</strong> Atomic step-claim
      ensures each step claimed by exactly one actor.
    </t>

    <t>
      <strong>Envelope replay prevention:</strong> approval_id is
      UUID v4 unique per envelope. Registries MUST reject
      duplicate approval_id values.
    </t>
  </section>

  <section anchor="privacy" numbered="true">
  <name>Privacy Considerations</name>
    <t>
      AIP is designed for zero-trust environments.
      No personally identifiable information (PII) is
      transmitted in Credential Tokens beyond the AID and
      delegation chain.
    </t>

    <t>
      Registries MUST NOT log or retain Credential Token
      payloads beyond validation. Relying Parties MUST NOT
      store tokens after validation.
    </t>

    <t>
      The principal_id in Principal Tokens enables
      attribution for compliance but does not inherently
      reveal principal identity to Relying Parties.
    </t>
  </section>
</section>
    <section anchor="rate-limiting" xml:base="sections/14-rate-limiting.xml">
  <name>Rate Limiting and Abuse Prevention</name>
  <t>
    Rate limiting protects the Registry from denial-of-service attacks,
    registration floods, and validation-driven key lookup storms. A
    public Registry that permits unrestricted write operations or
    validation-driven lookups is exploitable in ways that undermine the
    security guarantees of the entire ecosystem.
  </t>

  <section anchor="rate-limit-response-format">
    <name>Rate Limit Response Format</name>
    <t>
      When rate limiting is applied, the Registry MUST return HTTP 429 with
      the following headers:
    </t>
    <table>
      <thead>
        <tr><th>Header</th><th>Required</th><th>Description</th></tr>
      </thead>
      <tbody>
        <tr><td>`Retry-After`</td><td>MUST</td><td>Seconds until the client may retry, or a HTTP-date per [RFC9110]</td></tr>
        <tr><td>`X-RateLimit-Limit`</td><td>SHOULD</td><td>The request limit for this window</td></tr>
        <tr><td>`X-RateLimit-Remaining`</td><td>SHOULD</td><td>Remaining requests in this window</td></tr>
        <tr><td>`X-RateLimit-Reset`</td><td>SHOULD</td><td>Unix timestamp when the window resets</td></tr>
        <tr><td>`X-RateLimit-Policy`</td><td>MAY</td><td>Human-readable description of the applicable policy</td></tr>
      </tbody>
    </table>
    <t>
      The response body MUST conform to the error response format (Section 13.1)
      with `error: "rate_limit_exceeded"` and a human-readable
      `error_description` identifying the rate-limited operation and the
      applicable window.
    </t>
  </section>

  <section anchor="rate-limit-categories">
    <name>Per-Endpoint Rate Limit Categories</name>
    <t>
      The Registry MUST implement separate rate limit buckets for each of
      the following operation categories. The limits below are RECOMMENDED
      minimums; Registry operators MAY enforce stricter limits based on
      observed traffic patterns and threat models.
    </t>

    <section anchor="rate-limit-category-r1">
      <name>Category R1 - Registration writes</name>
      <t>
        (`POST /v1/agents`):
      </t>
      <ul>
        <li>
          Per-principal-DID: RECOMMENDED limit of 20 agent registrations per
          hour. This prevents a single compromised principal key from flooding
          the Registry with rogue agent registrations.
        </li>
        <li>
          Per-source-IP: RECOMMENDED limit of 50 registrations per hour
          across all principals. This prevents registration floods from a
          single network origin, regardless of the principal DID presented.
        </li>
        <li>
          Global: Registries SHOULD implement a global registration rate limit
          appropriate to their infrastructure capacity.
        </li>
      </ul>
    </section>

    <section anchor="rate-limit-category-r2">
      <name>Category R2 - Key rotation writes</name>
      <t>
        (`PUT /v1/agents/{aid}`):
      </t>
      <ul>
        <li>
          Per-AID: RECOMMENDED limit of 10 key rotations per 24-hour window.
          Legitimate key rotation is infrequent; high frequency suggests
          automated abuse or a compromised orchestrator.
        </li>
      </ul>
    </section>

    <section anchor="rate-limit-category-r3">
      <name>Category R3 - Revocation writes</name>
      <t>
        (`POST /v1/revocations`):
      </t>
      <ul>
        <li>
          Per-issuer-DID: RECOMMENDED limit of 100 revocations per hour.
          Higher limits are legitimate for enterprise orchestrators managing
          large ephemeral agent fleets. Registries MAY issue higher limits to
          verified principals.
        </li>
        <li>
          Registries MUST apply special throttling to `propagate_to_children:
          true` revocations that would cascade to more than 100 descendants,
          as these trigger recursive Registry writes. A revocation that would
          cascade to more than 100 descendants SHOULD be queued and processed
          asynchronously, with the Registry returning HTTP 202 (Accepted) and
          a status URI rather than blocking on the full cascade.
        </li>
      </ul>
    </section>

    <section anchor="rate-limit-category-r4">
      <name>Category R4 - Validation-driven key reads</name>
      <t>
        (`GET /v1/agents/{aid}/public-key/{key-id}`,
        `GET /v1/agents/{aid}/revocation`):
      </t>
      <ul>
        <li>
          Per-requesting-IP: RECOMMENDED limit of 1,000 requests per minute
          across all AIDs. Validation-driven reads are triggered by token
          verification; legitimate Relying Parties have bounded lookup rates.
        </li>
        <li>
          Per-AID: RECOMMENDED limit of 200 reads per minute. A single AID
          being looked up 200 times per minute from varied IPs is likely the
          subject of a coordinated replay attack; rate limiting per AID
          allows the Registry to throttle targeted abuse.
        </li>
        <li>
          Registries SHOULD offer API key authentication for Relying Parties
          whose legitimate validation rates exceed these limits (e.g.,
          high-traffic APIs that verify thousands of agent tokens per minute).
        </li>
      </ul>
    </section>

    <section anchor="rate-limit-category-r5">
      <name>Category R5 - CRL reads</name>
      <t>
        (`GET /v1/crl`):
      </t>
      <ul>
        <li>
          The CRL endpoint MUST be served from a CDN or distributed
          infrastructure (Section 9.2). Direct-origin CRL reads SHOULD be rate
          limited per IP to 100 requests per minute to protect against CDN
          bypass attacks. CDN-served responses have no normative rate limit
          constraint.
        </li>
      </ul>
    </section>

    <section anchor="rate-limit-category-r6">
      <name>Category R6 - Endorsement writes</name>
      <t>
        (`POST /v1/endorsements`):
      </t>
      <ul>
        <li>
          Per-from-AID: RECOMMENDED limit of 500 endorsements per hour. This
          prevents an AID from artificially inflating another AID's
          `endorsement_count` through automated submission.
        </li>
        <li>
          Self-endorsement (`from_aid` == `to_aid`) MUST be rejected at the
          application layer before rate limits are checked.
        </li>
      </ul>
    </section>

    <section anchor="rate-limit-category-r7">
      <name>Category R7 - Approval Envelope writes and step claims</name>
      <t>
        (`POST /v1/approvals`, `POST /v1/approvals/{id}/steps/{n}/claim`):
      </t>
      <ul>
        <li>
          Per-principal-DID: RECOMMENDED limit of 100 Approval Envelopes per
          hour. Approval Envelopes represent human-authorised workflows;
          high frequency is anomalous.
        </li>
        <li>
          Per-actor-AID per envelope: step claims are naturally rate-limited
          by the sequential structure of the workflow. No additional rate
          limit is required for step claims within a single envelope.
        </li>
      </ul>
    </section>
  </section>

  <section anchor="registration-abuse-prevention">
    <name>Registration Abuse Prevention</name>
    <t>
      Beyond rate limiting, the Registry MUST implement the following
      structural checks to prevent registration abuse:
    </t>

    <section anchor="aid-uniqueness-enforcement">
      <name>AID uniqueness enforcement</name>
      <t>
        The Registry MUST check AID uniqueness under a distributed lock or
        equivalent atomic mechanism. Two simultaneous registration requests
        for the same AID MUST result in exactly one succeeding and one
        receiving an appropriate error (Section 5.2 Check 4).
      </t>
    </section>

    <section anchor="principal-delegation-chain-verification">
      <name>Principal delegation chain verification at registration</name>
      <t>
        The Registry MUST verify that the `principal_token` in the Registration
        Envelope was issued by a DID that is resolvable and has not been
        subjected to a `full_revoke` or `principal_revoke` RevocationObject
        in the Registry. A revoked principal MUST NOT be permitted to
        register new agents.
      </t>
      <t>
        For the purposes of this check, a **revoked principal** is defined as
        a principal DID (`principal_token.principal.id`) against which a
        `principal_revoke` Revocation Object has been submitted to this
        Registry. The Registry MUST maintain an index of principal DIDs
        associated with `principal_revoke` revocations and MUST reject new
        Registration Envelopes where `principal_token.principal.id` matches a
        DID in this index, unless the `principal_revoke` object explicitly
        scopes the revocation to a specific agent AID other than the one being
        registered.
      </t>
    </section>

    <section anchor="registration-flood-from-shared-principals">
      <name>Registration flood from shared principals</name>
      <t>
        If a single principal DID registers more than 1,000 agents (across
        all time), the Registry SHOULD require the deployer to present proof
        of legitimate use (out-of-band, implementation-specific). This is a
        SHOULD, not a MUST, because legitimate orchestrator-heavy deployments
        may reach this threshold.
      </t>
    </section>

    <section anchor="public-registry-challenge">
      <name>Public Registry challenge for unauthenticated deployers</name>
      <t>
        Registries that permit registration without deployer authentication
        (open Registries) SHOULD implement a lightweight proof-of-work or
        CAPTCHA mechanism for registrations where `deployer_did` is absent
        from the principal_token context.
      </t>
    </section>
  </section>

  <section anchor="validation-driven-lookup-limits">
    <name>Validation-Driven Lookup Limits</name>
    <t>
      Key lookup amplification occurs when an adversary presents many tokens
      with distinct `kid` values, forcing the Registry to perform a lookup
      for each. Mitigations:
    </t>

    <section anchor="key-version-caching">
      <name>Key version caching</name>
      <t>
        Relying Parties MUST cache resolved public keys for a given `kid`
        for up to 300 seconds (Section 6.1). Repeated validation of tokens
        with the same `kid` SHOULD NOT trigger repeated Registry lookups
        within the cache window.
      </t>
    </section>

    <section anchor="historical-key-depth-limit">
      <name>Historical key depth limit</name>
      <t>
        Registries MAY reject requests for key versions older than a
        configurable retention window (RECOMMENDED: 90 days past the key
        rotation date, since all tokens issued with that key must have
        expired within 3600s of rotation). This prevents adversaries from
        constructing tokens with ancient, never-rotated keys to force deep
        history lookups.
      </t>
    </section>

    <section anchor="kid-validation-at-relying-party">
      <name>`kid` validation at the Relying Party</name>
      <t>
        Relying Parties MUST validate that the `kid` in the token header
        matches the pattern:
      </t>
      <artwork type="inline">did:aip:&lt;lowercase-namespace&gt;:&lt;32-lowercase-hex&gt;#key-&lt;positive-integer&gt;</artwork>
      <t>
        before performing any Registry lookup (Validation Step 3).
        Malformed `kid` values MUST be rejected with `invalid_token`
        without making a Registry call.
      </t>
    </section>
  </section>

  <section anchor="approval-envelope-rate-limits">
    <name>Approval Envelope Rate Limits</name>
    <t>
      Approval Envelope operations require specific abuse prevention because
      they involve asynchronous principal interactions and potential cascade
      effects.
    </t>

    <section anchor="envelope-submission-rate">
      <name>Envelope submission rate</name>
      <t>
        Per Section 14.2, Category R7.
      </t>
    </section>

    <section anchor="pending-envelope-limit">
      <name>Pending envelope limit</name>
      <t>
        A Registry SHOULD enforce a maximum of 1,000 `pending_approval`
        envelopes per principal DID at any time. Envelopes that expire
        transition to `expired` and free this quota.
      </t>
    </section>

    <section anchor="step-claim-timeout">
      <name>Step claim timeout</name>
      <t>
        Step claims that are not completed or failed within 600 seconds
        MUST be automatically failed by the Registry (Section 4.6.8).
        This prevents a claimed step from blocking the workflow indefinitely
        due to a crashed or unresponsive agent.
      </t>
    </section>

    <section anchor="compensation-cascade-depth">
      <name>Compensation cascade depth</name>
      <t>
        Compensation step execution is not rate-limited separately - it is
        a recovery mechanism whose scope is bounded by the number of forward
        steps (maximum 20). No additional rate limit is required for
        compensation.
      </t>
    </section>
  </section>

  <section anchor="graduated-backoff-requirements">
    <name>Graduated Backoff Requirements</name>
    <t>
      Clients that receive HTTP 429 responses MUST implement exponential
      backoff with jitter. The minimum retry interval is the value in the
      `Retry-After` header. Clients MUST NOT retry before `Retry-After`
      expires.
    </t>
    <t>
      Implementations SHOULD use the following backoff formula:
    </t>
    <artwork type="pseudocode">
BACKOFF_BASE = 1         ; fixed exponential base, in seconds
MAX_DELAY    = 3600      ; hard ceiling, in seconds

computed_delay = min(BACKOFF_BASE * 2^attempt, MAX_DELAY)
jitter         = random(0, BACKOFF_BASE)
retry_delay    = max(computed_delay + jitter, Retry-After)
    </artwork>
    <t>
      The `Retry-After` value from the 429 response acts as a mandatory
      minimum: clients MUST NOT retry before `Retry-After` seconds have
      elapsed, even if `computed_delay + jitter` is smaller. When no
      `Retry-After` header is present, clients MUST treat it as 0 and
      rely solely on the exponential formula.
    </t>
    <t>
      Clients that continue to receive HTTP 429 after 5 exponential backoff
      attempts MUST cease retrying for a minimum of 1 hour and SHOULD alert
      an operator. Persistent rate limiting at this scale indicates either
      a misconfigured client or a sustained attack pattern.
    </t>
    <t>
      Registries MUST track clients that consistently exceed rate limits and
      MAY temporarily block their source IPs or API keys after sustained
      abuse. Blocking decisions are implementation-specific and are not
      normatively constrained by this specification.
    </t>
  </section>
</section>
    <section anchor="iana" numbered="true" xml:base="sections/14-iana.xml">
  <name>IANA Considerations</name>
  <t>
    This section describes the IANA considerations for
    the AIP specification.
  </t>

  <section anchor="iana-did-method" numbered="true">
  <name>DID Method Registration</name>
    <t>
      The did:aip DID method is requested for registration
      in the W3C DID Method Registry per W3C DID Specification.
    </t>

                <table>
          <name>did:aip Method Registration</name>
          <thead>
            <tr>
              <th align="left">Field</th>
              <th align="left">Value</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Method Name</td>
              <td>did:aip</td>
            </tr>
            <tr>
              <td>Status</td>
              <td>Draft</td>
            </tr>
            <tr>
              <td>Canonical ID</td>
              <td>did:aip:namespace:32-hex-characters</td>
            </tr>
          </tbody>
        </table>
  </section>

  <section anchor="iana-scope-namespace" numbered="true">
  <name>Scope URI Namespace</name>
    <t>
      AIP defines a URN namespace for scope identifiers:
      urn:aip:scope:*. This namespace is used in
      OAuth 2.0 token exchange to identify capability scopes.
    </t>

    <artwork type="example">
urn:aip:scope:email.read
urn:aip:scope:email.write
urn:aip:scope:transactions
urn:aip:scope:spawn_agents.create
</artwork>
  </section>

  <section anchor="iana-grant-tiers" numbered="true">
  <name>Grant Tier Registry</name>
                <table>
          <name>Grant Tier Values</name>
          <thead>
            <tr>
              <th align="left">Value</th>
              <th align="left">Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>G1</td>
              <td>Registry-Mediated Grant Flow</td>
            </tr>
            <tr>
              <td>G2</td>
              <td>Direct Deployer Grant Flow</td>
            </tr>
            <tr>
              <td>G3</td>
              <td>Full Ceremony Grant Flow</td>
            </tr>
          </tbody>
        </table>
  </section>

  <section anchor="iana-error-codes" numbered="true">
  <name>Error Code Registry</name>
    <t>
      AIP defines a registry of error codes.
    </t>

                <table>
          <name>AIP Error Codes</name>
          <thead>
            <tr>
              <th align="left">Code</th>
              <th align="center">HTTP</th>
              <th align="left">Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>invalid_token</td>
              <td>401</td>
              <td>Token malformed or signature invalid</td>
            </tr>
            <tr>
              <td>token_expired</td>
              <td>401</td>
              <td>Token exp has passed</td>
            </tr>
            <tr>
              <td>agent_revoked</td>
              <td>403</td>
              <td>AID has been revoked</td>
            </tr>
            <tr>
              <td>insufficient_scope</td>
              <td>403</td>
              <td>Operation not in granted scopes</td>
            </tr>
            <tr>
              <td>registry_unavailable</td>
              <td>503</td>
              <td>Registry could not be reached</td>
            </tr>
          </tbody>
        </table>
  </section>

  <section anchor="iana-media-types" numbered="true">
  <name>Media Types</name>
                <table>
          <name>AIP Media Types</name>
          <thead>
            <tr>
              <th align="left">Type</th>
              <th align="left">Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>application/aip+jwt</td>
              <td>AIP Credential Token (JWT format)</td>
            </tr>
          </tbody>
        </table>

    <t>
      The typ header value for AIP Credential Tokens
      is AIP+JWT per RFC 7515.
    </t>
  </section>
</section>
    <section anchor="registry-interface" xml:base="sections/15-registry.xml">
  <name>Registry Interface</name>

  <section anchor="registry-endpoints">
    <name>Required Endpoints</name>
    <t>A conformant AIP Registry MUST implement the following HTTP endpoints:</t>
    <table>
      <thead>
        <tr>
          <th>Method</th>
          <th>Path</th>
          <th>Description</th>
        </tr>
      </thead>
      <tbody>
        <tr><td>POST</td><td>/v1/agents</td><td>Register a new AID (Registration Envelope)</td></tr>
        <tr><td>GET</td><td>/v1/agents/{aid}</td><td>Retrieve Agent Identity or DID Document</td></tr>
        <tr><td>PUT</td><td>/v1/agents/{aid}</td><td>Key rotation only</td></tr>
        <tr><td>GET</td><td>/v1/agents/{aid}/public-key</td><td>Current public key (JWK)</td></tr>
        <tr><td>GET</td><td>/v1/agents/{aid}/public-key/{key-id}</td><td>Historical key version</td></tr>
        <tr><td>GET</td><td>/v1/agents/{aid}/capabilities</td><td>Current Capability Manifest</td></tr>
        <tr><td>PUT</td><td>/v1/agents/{aid}/capabilities</td><td>Replace Capability Manifest</td></tr>
        <tr><td>GET</td><td>/v1/agents/{aid}/revocation</td><td>Revocation status</td></tr>
        <tr><td>POST</td><td>/v1/revocations</td><td>Submit RevocationObject</td></tr>
        <tr><td>GET</td><td>/v1/crl</td><td>Certificate Revocation List</td></tr>
        <tr><td>GET</td><td>/v1/agents/{aid}/reputation</td><td>Reputation data</td></tr>
        <tr><td>POST</td><td>/v1/endorsements</td><td>Submit Endorsement Object</td></tr>
        <tr><td>POST</td><td>/v1/grants</td><td>Submit G1 grant (Section 4.5)</td></tr>
        <tr><td>GET</td><td>/v1/grants/{grant_id}</td><td>Retrieve grant status</td></tr>
        <tr><td>PUT</td><td>/v1/agents/{aid}/overlays</td><td>Submit Capability Overlay (Section 4.7)</td></tr>
        <tr><td>GET</td><td>/v1/agents/{aid}/overlays</td><td>Retrieve current overlay</td></tr>
        <tr><td>POST</td><td>/v1/engagements</td><td>Create Engagement Object (Section 4.8)</td></tr>
        <tr><td>GET</td><td>/v1/engagements/{id}</td><td>Retrieve Engagement Object</td></tr>
        <tr><td>PUT</td><td>/v1/engagements/{id}</td><td>Update Engagement (append change log)</td></tr>
        <tr><td>POST</td><td>/v1/subscriptions</td><td>Create RPNP subscription (Section 9.4)</td></tr>
        <tr><td>DELETE</td><td>/v1/subscriptions/{id}</td><td>Cancel RPNP subscription</td></tr>
        <tr><td>GET</td><td>/v1/scopes</td><td>Scope Map registry (Section 15.6)</td></tr>
        <tr><td>POST</td><td>/v1/oauth/authorize</td><td>G3 authorization endpoint (Section 15.5)</td></tr>
        <tr><td>POST</td><td>/v1/oauth/token</td><td>G3 token endpoint / token exchange (Section 15.5)</td></tr>
        <tr><td>GET</td><td>/.well-known/oauth-authorization-server</td><td>AS Metadata <xref target="RFC8414"/></td></tr>
      </tbody>
    </table>
  </section>

  <section anchor="aid-encoding">
    <name>AID URL Encoding</name>
    <t>In all path parameters, the `did:aip:` prefix and colons MUST be percent-encoded per <xref target="RFC3986"/>:</t>

    <artwork anchor="aid-encoding-example" type="example">
did:aip:personal:9f3a1c82b4e6d7f0a2b5c8e1d4f7a0b3
→ /v1/agents/did%3Aaip%3Apersonal%3A9f3a1c82b4e6d7f0a2b5c8e1d4f7a0b3
    </artwork>
  </section>

  <section anchor="response-format-registry">
    <name>Response Format</name>
    <t>All Registry responses MUST use `Content-Type: application/json`.</t>
    <t>All timestamps MUST be in ISO 8601 UTC format.</t>
  </section>

  <section anchor="approval-endpoints-registry">
    <name>Approval Envelope Endpoints</name>
    <t>A conformant AIP Registry MUST implement the following additional endpoints for Approval Envelopes:</t>
    <table>
      <thead>
        <tr><th>Method</th><th>Path</th><th>Description</th></tr>
      </thead>
      <tbody>
        <tr><td>POST</td><td>/v1/approvals</td><td>Submit Approval Envelope for approval</td></tr>
        <tr><td>GET</td><td>/v1/approvals/{id}</td><td>Retrieve Approval Envelope with step statuses</td></tr>
        <tr><td>POST</td><td>/v1/approvals/{id}/approve</td><td>Principal approves (wallet call; sets `principal_signature`)</td></tr>
        <tr><td>POST</td><td>/v1/approvals/{id}/reject</td><td>Principal rejects</td></tr>
        <tr><td>POST</td><td>/v1/approvals/{id}/steps/{n}/claim</td><td>Claim step for execution; returns Step Execution Token</td></tr>
        <tr><td>POST</td><td>/v1/approvals/{id}/steps/{n}/complete</td><td>Mark step completed</td></tr>
        <tr><td>POST</td><td>/v1/approvals/{id}/steps/{n}/fail</td><td>Mark step failed; triggers compensation</td></tr>
        <tr><td>GET</td><td>/v1/approvals/{id}/steps/{n}</td><td>Get step status (used by Relying Parties for Step Execution Token verification)</td></tr>
        <tr><td>POST</td><td>/v1/approvals/{id}/compensation-steps/{n}/claim</td><td>Claim compensation step</td></tr>
        <tr><td>POST</td><td>/v1/approvals/{id}/compensation-steps/{n}/complete</td><td>Mark compensation step completed</td></tr>
        <tr><td>POST</td><td>/v1/approvals/{id}/compensation-steps/{n}/fail</td><td>Mark compensation step failed</td></tr>
      </tbody>
    </table>

    <t>**`POST /v1/approvals` validation:** The Registry MUST perform all checks defined in Section 4.6.10 before accepting an Approval Envelope. The Registry MUST return HTTP 201 with the stored envelope (including the Registry-assigned `status: "pending_approval"`) on success.</t>
    <t>**`POST /v1/approvals/{id}/approve` flow:** This endpoint is called by the Principal Wallet after the principal completes the signing ceremony. The request body MUST contain the `principal_signature` field: a base64url EdDSA signature computed by the principal's wallet over the JCS-canonical serialisation of the full Approval Envelope with `principal_signature` set to the empty string `""` before serialisation — identical to the pattern used for `creator_signature` (Section 4.6.3). The signing key MUST be the private key of `principal_id`. The Registry MUST verify this signature against the `principal_id`'s resolved public key before transitioning the envelope to `approved`.</t>
    <t>**Atomicity requirement for step claim:** The Registry MUST implement step-claim operations atomically (e.g., using optimistic locking or a distributed lock) to prevent two actors from claiming the same step simultaneously. Only one claim MUST succeed; the other MUST receive `approval_step_already_claimed`.</t>
    <t>**Step Execution Token format:** The Step Execution Token returned by `POST .../steps/{n}/claim` is a JWT signed by the Registry's administrative key with the claims described in Section 4.6.8. Its TTL MUST comply with normal TTL rules (Section 7.2) for the scopes involved.</t>
  </section>

  <section anchor="oauth-registry">
    <name>OAuth 2.1 Authorization Server</name>
    <t>A conformant AIP Registry supporting G3 grants MUST implement an OAuth 2.1 Authorization Server <xref target="RFC6749"/> with the following requirements:</t>
    <ol>
      <li>The authorization endpoint MUST be published in the Registry's well-known configuration at `/.well-known/oauth-authorization-server` per <xref target="RFC8414"/>.</li>
      <li>PKCE <xref target="RFC7636"/> with `code_challenge_method: "S256"` is REQUIRED for all authorization requests.</li>
      <li>The `scope` parameter MUST use AIP scope URIs from the `urn:aip:scope:` namespace (Section 15.6).</li>
      <li>The token endpoint MUST return a signed Principal Token (not a standard OAuth access token) as the `access_token` value, with `token_type: "AIP+JWT"`.</li>
      <li>The token response MUST include `acr` and `amr` claims reflecting the authentication performed.</li>
      <li>The authorization server MUST support the `acr_values` parameter to declare minimum identity-proofing requirements.</li>
      <li>DPoP [RFC9449] MUST be supported on the token endpoint.</li>
    </ol>

    <t>The token endpoint also serves RFC 8693 <xref target="RFC8693"/> token exchange (Section 7.5).</t>

    <section anchor="well-known-metadata-registry">
      <name>Well-Known Metadata Additions</name>
      <t>The `/.well-known/aip-registry` response MUST also include:</t>
      <table>
        <thead>
          <tr><th>Field</th><th>Type</th><th>Description</th></tr>
        </thead>
        <tbody>
          <tr><td>`grant_tiers_supported`</td><td>array</td><td>Supported grant tiers: `["G1", "G2", "G3"]`</td></tr>
          <tr><td>`acr_values_supported`</td><td>array</td><td>Supported `acr` values</td></tr>
          <tr><td>`amr_values_supported`</td><td>array</td><td>Supported `amr` values</td></tr>
          <tr><td>`oauth_authorization_server`</td><td>string</td><td>URI of the OAuth AS metadata document (present only if G3 supported)</td></tr>
          <tr><td>`identity_proofing_required_for_tier2`</td><td>boolean</td><td>Whether this Registry requires G3 for Tier 2 operations</td></tr>
        </tbody>
      </table>
    </section>
  </section>

  <section anchor="scope-map-registry">
    <name>Scope Map</name>
    <t>All AIP capability scopes MUST be representable as URIs in the
      `urn:aip:scope:` namespace:</t>
    <artwork>
      scope_string → urn:aip:scope:&lt;scope_string&gt;
    </artwork>
    <t>Examples: `email.read` → `urn:aip:scope:email.read`,
`spawn_agents.create` → `urn:aip:scope:spawn_agents.create`.</t>
    <t>When used in OAuth `scope` parameters, the URI form MUST be used.
Within AIP Credential Tokens (`aip_scope` array), the short string
form remains canonical.</t>
    <t>A conformant AIP Registry MUST implement `GET /v1/scopes` returning
a JSON object mapping scope strings to metadata:</t>
    <table>
      <thead>
        <tr><th>Field</th><th>Type</th><th>Description</th></tr>
      </thead>
      <tbody>
        <tr><td>`uri`</td><td>string</td><td>Full `urn:aip:scope:` URI</td></tr>
        <tr><td>`description`</td><td>string</td><td>Human-readable description</td></tr>
        <tr><td>`tier`</td><td>integer</td><td>Tier classification (1, 2, or 3)</td></tr>
        <tr><td>`destructive`</td><td>boolean</td><td>Whether the scope requires additional confirmation per Section 4.5.3</td></tr>
        <tr><td>`constraint_schema`</td><td>object/null</td><td>JSON Schema fragment for scope-specific constraints</td></tr>
      </tbody>
    </table>
  </section>

  <section anchor="engagement-endpoints">
    <name>Engagement Endpoints</name>
    <t>A conformant AIP Registry supporting Engagement Objects (Section 4.8) MUST implement:</t>
    <table>
      <thead>
        <tr><th>Method</th><th>Path</th><th>Description</th></tr>
      </thead>
      <tbody>
        <tr><td>POST</td><td>/v1/engagements</td><td>Create Engagement Object</td></tr>
        <tr><td>GET</td><td>/v1/engagements/{id}</td><td>Retrieve Engagement Object</td></tr>
        <tr><td>PUT</td><td>/v1/engagements/{id}</td><td>Update Engagement (append change log entry)</td></tr>
      </tbody>
    </table>

    <t>Create validation: The Registry MUST verify the initiator's
DID signature, validate that all referenced participant AIDs exist
and are not revoked, and assign `status: "active"` with
`seq_number: 1`.</t>

    <t>Update validation: The Registry MUST verify the submitter is an
active participant, the change log entry's `seq_number` is exactly
`current_max + 1`, and the entry signature is valid. The Registry
MUST reject modifications to existing entries with
`change_log_immutable`.</t>
  </section>

  <section anchor="rpnp-endpoints">
    <name>RPNP Subscription Endpoints</name>
    <t>A conformant AIP Registry supporting RPNP (Section 9.4) MUST
implement:</t>
    <table>
      <thead>
        <tr><th>Method</th><th>Path</th><th>Description</th></tr>
      </thead>
      <tbody>
        <tr><td>POST</td><td>/v1/subscriptions</td><td>Create RPNP subscription</td></tr>
        <tr><td>GET</td><td>/v1/subscriptions/{id}</td><td>Retrieve subscription status</td></tr>
        <tr><td>DELETE</td><td>/v1/subscriptions/{id}</td><td>Cancel subscription</td></tr>
      </tbody>
    </table>

    <t>Subscription creation MUST be authenticated via DPoP. The
`webhook_uri` MUST use HTTPS. The Registry MUST reject non-HTTPS
URIs with `invalid_webhook_uri`.</t>
  </section>
</section>
    <section anchor="versioning" xml:base="sections/16-versioning.xml">
  <name>Versioning and Compatibility</name>
  <t>
    AIP follows Semantic Versioning. Before v1.0, MINOR versions MAY
    include breaking changes.
  </t>
  <t>
    Breaking changes from v0.2:
  </t>
  <ul>
    <li><tt>aip_version</tt> is now <tt>"0.3"</tt> for conforming implementations.</li>
    <li><tt>X-AIP-Version: 0.3</tt> replaces <tt>X-AIP-Version: 0.2</tt>.</li>
    <li>The bare <tt>spawn_agents</tt> scope is retired; use <tt>spawn_agents.create</tt> and <tt>spawn_agents.manage</tt> (Section 3).</li>
    <li>Registration Envelopes MUST include <tt>grant_tier</tt> (Section 4.5).</li>
    <li>Principal DID Documents for Tier 2 agents MUST include an <tt>AIPRegistry</tt> service entry (Section 6.2).</li>
    <li>The <tt>/.well-known/aip-registry</tt> response MUST include <tt>signature</tt>, <tt>registry_name</tt>, and <tt>endpoints</tt> (Section 6.3.4).</li>
    <li>§14.6 backoff formula corrected (Section 14.6).</li>
  </ul>
  <t>
    Implementations MUST NOT silently accept tokens from unsupported
    versions without logging a version warning.
  </t>

  <section anchor="tier-conformance">
    <name>Tier Conformance</name>
    <table>
      <thead>
        <tr><th>Tier</th><th>Revocation</th><th>DPoP</th><th>mTLS</th><th><tt>grant_tier</tt></th><th>Principal DID Method</th></tr>
      </thead>
      <tbody>
        <tr><td>1</td><td>CRL (15 min)</td><td>NOT REQUIRED</td><td>NOT REQUIRED</td><td>G1 or G2</td><td>Any (note 1)</td></tr>
        <tr><td>2</td><td>Real-time</td><td>REQUIRED</td><td>NOT REQUIRED</td><td>G2 or G3</td><td><tt>did:web</tt> (note 2)</td></tr>
        <tr><td>3</td><td>Real-time + OCSP</td><td>REQUIRED</td><td>REQUIRED</td><td>G3</td><td><tt>did:web</tt> (note 2)</td></tr>
      </tbody>
    </table>
    <dl newline="false">
      <dt>Note 1</dt>
      <dd><t>For Tier 1, the principal MAY use any W3C DID method. Principals using <tt>did:key</tt> are permitted for Tier 1 only; see Note 2.</t></dd>
      <dt>Note 2</dt>
      <dd><t><tt>did:aip</tt> is NEVER a valid Principal DID method for Tier 2 or Tier 3. The <tt>principal.id</tt> field MUST NOT use the <tt>did:aip</tt> method (Section 4.2.4). Principals using <tt>did:key</tt> for Tier 2 agents MUST be rejected with <tt>principal_did_method_forbidden</tt>; the <tt>did:key</tt> method does not support a DID Document with the <tt>AIPRegistry</tt> service entry required by Section 6.2.</t></dd>
    </dl>
  </section>
</section>
  </middle>

  <back>
    <references xml:base="references/normative.xml">
  <name>Normative References</name>

  <reference anchor="RFC2119">
    <front><title>Key words for use in RFCs</title><author><organization>Bradner, S.</organization></author><date month="March" year="1997"/></front>
    <seriesInfo name="BCP" value="14"/>
  </reference>
  <reference anchor="RFC3986">
    <front><title>URI Generic Syntax</title><author><organization>Berners-Lee et al.</organization></author><date month="January" year="2005"/></front>
    <seriesInfo name="STD" value="66"/>
  </reference>
  <reference anchor="RFC5234">
    <front><title>ABNF</title><author><organization>Crocker &amp; Overell</organization></author><date month="January" year="2008"/></front>
    <seriesInfo name="STD" value="68"/>
  </reference>
  <reference anchor="RFC5280">
    <front><title>X.509 PKI Certificate Profile</title><author><organization>Cooper et al.</organization></author><date month="May" year="2008"/></front>
  </reference>
  <reference anchor="RFC6585">
    <front><title>Additional HTTP Status Codes</title><author><organization>Nottingham &amp; Fielding</organization></author><date month="April" year="2012"/></front>
  </reference>
  <reference anchor="RFC7515">
    <front><title>JSON Web Signature (JWS)</title><author><organization>Jones, M., Bradley, J., and N. Sakimura</organization></author><date month="May" year="2015"/></front>
    <seriesInfo name="RFC" value="7515"/>
    <seriesInfo name="DOI" value="10.17487/RFC7515"/>
  </reference>
  <reference anchor="RFC7517">
    <front><title>JSON Web Key (JWK)</title><author><organization>Jones, M.</organization></author><date month="May" year="2015"/></front>
  </reference>
  <reference anchor="RFC7518">
    <front><title>JSON Web Algorithms (JWA)</title><author><organization>Jones, M.</organization></author><date month="May" year="2015"/></front>
  </reference>
  <reference anchor="RFC7519">
    <front><title>JSON Web Token (JWT)</title><author><organization>Jones, M., Bradley, J., and N. Sakimura</organization></author><date month="May" year="2015"/></front>
    <seriesInfo name="RFC" value="7519"/>
    <seriesInfo name="DOI" value="10.17487/RFC7519"/>
  </reference>
  <reference anchor="RFC7748">
    <front><title>Elliptic Curves for Security</title><author><organization>Langley et al.</organization></author><date month="January" year="2016"/></front>
  </reference>
  <reference anchor="RFC8032">
    <front><title>Edwards-Curve Digital Signature Algorithm (EdDSA)</title><author><organization>Josefsson, S. and I. Liusvaara</organization></author><date month="January" year="2017"/></front>
    <seriesInfo name="RFC" value="8032"/>
    <seriesInfo name="DOI" value="10.17487/RFC8032"/>
  </reference>
  <reference anchor="RFC8037">
    <front><title>CFRG Elliptic Curves for JOSE</title><author><organization>Liusvaara, I.</organization></author><date month="January" year="2017"/></front>
  </reference>
  <reference anchor="RFC8174">
    <front><title>Ambiguity of Uppercase in RFC 2119</title><author><organization>Leiba, B.</organization></author><date month="May" year="2017"/></front>
    <seriesInfo name="BCP" value="14"/>
  </reference>
  <reference anchor="RFC8259">
    <front><title>JSON Data Interchange Format</title><author><organization>Bray, T.</organization></author><date month="December" year="2017"/></front>
    <seriesInfo name="STD" value="90"/>
  </reference>
  <reference anchor="RFC8785">
    <front><title>JSON Canonicalization Scheme (JCS)</title><author><organization>Rundgren, A., Jordan, B., and S. Erdtman</organization></author><date month="June" year="2020"/></front>
    <seriesInfo name="RFC" value="8785"/>
    <seriesInfo name="DOI" value="10.17487/RFC8785"/>
  </reference>
  <reference anchor="RFC9110">
    <front><title>HTTP Semantics</title><author><organization>Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed.</organization></author><date month="June" year="2022"/></front>
    <seriesInfo name="STD" value="97"/>
    <seriesInfo name="RFC" value="9110"/>
    <seriesInfo name="DOI" value="10.17487/RFC9110"/>
  </reference>
  <reference anchor="RFC9449">
    <front><title>OAuth 2.0 Demonstrating Proof of Possession (DPoP)</title><author><organization>Fett, D., Campbell, B., Bradley, J., Lodderstedt, T., Jones, M., and D. Waite</organization></author><date month="September" year="2023"/></front>
    <seriesInfo name="RFC" value="9449"/>
    <seriesInfo name="DOI" value="10.17487/RFC9449"/>
  </reference>
  <reference anchor="W3C-DID">
    <front><title>Decentralized Identifiers (DIDs) v1.0</title><author><organization>Sporny, M., Longley, D., Sabadello, M., Reed, D., Steele, O., and C. Allen</organization></author><date month="July" year="2022"/></front>
  </reference>
  <reference anchor="RFC6454">
    <front><title>The Web Origin Concept</title><author><organization>Barth, A.</organization></author><date month="December" year="2011"/></front>
    <seriesInfo name="RFC" value="6454"/>
  </reference>
  <reference anchor="RFC6960">
    <front><title>X.509 Internet Public Key Infrastructure Online Certificate Status Protocol - OCSP</title><author><organization>Santesson, S., Myers, M., Ankney, R., Malpani, A., Galperin, S., and C. Adams</organization></author><date month="June" year="2013"/></front>
    <seriesInfo name="RFC" value="6960"/>
  </reference>
  <reference anchor="RFC7636">
    <front><title>Proof Key for Code Exchange by OAuth Public Clients</title><author><organization>Sakimura, N., Ed., Bradley, J., and N. Agarwal</organization></author><date month="September" year="2015"/></front>
    <seriesInfo name="RFC" value="7636"/>
  </reference>
  <reference anchor="RFC8176">
    <front><title>Authentication Method Reference Values</title><author><organization>Jones, M., Hunt, P., and A. Nadalin</organization></author><date month="June" year="2017"/></front>
    <seriesInfo name="RFC" value="8176"/>
  </reference>
  <reference anchor="RFC8414">
    <front><title>OAuth 2.0 Authorization Server Metadata</title><author><organization>Jones, M., Sakimura, N., and J. Bradley</organization></author><date month="June" year="2018"/></front>
    <seriesInfo name="RFC" value="8414"/>
  </reference>
  <reference anchor="RFC8693">
    <front><title>OAuth 2.0 Token Exchange</title><author><organization>Jones, M., Nadalin, A., Campbell, B., Bradley, J., and C. Mortimore</organization></author><date month="January" year="2020"/></front>
    <seriesInfo name="RFC" value="8693"/>
  </reference>
  <reference anchor="RFC8707">
    <front><title>Resource Indicators for OAuth 2.0</title><author><organization>Campbell, B., Bradley, J., and H. Tschofenig</organization></author><date month="February" year="2020"/></front>
    <seriesInfo name="RFC" value="8707"/>
  </reference>
  <reference anchor="RFC9068">
    <front><title>JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens</title><author><organization>Bertocci, V.</organization></author><date month="October" year="2021"/></front>
    <seriesInfo name="RFC" value="9068"/>
  </reference>
  <reference anchor="FIPS-180-4">
    <front><title>Secure Hash Standard (SHS)</title><author><organization>National Institute of Standards and Technology</organization></author><date month="August" year="2015"/></front>
    <seriesInfo name="FIPS PUB" value="180-4"/>
    <seriesInfo name="DOI" value="10.6028/NIST.FIPS.180-4"/>
  </reference>
</references>
    <references xml:base="references/informative.xml">
  <name>Informative References</name>

  <reference anchor="RFC6749">
    <front><title>The OAuth 2.0 Authorization Framework</title><author><organization>Hardt, D., Ed.</organization></author><date month="October" year="2012"/></front>
    <seriesInfo name="RFC" value="6749"/>
    <seriesInfo name="DOI" value="10.17487/RFC6749"/>
  </reference>
  <reference anchor="MCP">
    <front><title>Model Context Protocol Specification</title><author><organization>Anthropic</organization></author><date year="2024"/></front>
  </reference>
  <reference anchor="SP-800-207">
    <front><title>Zero Trust Architecture</title><author><organization>Rose, S., Borchert, O., Mitchell, S., and S. Connelly</organization></author><date month="August" year="2020"/></front>
    <seriesInfo name="NIST Special Publication" value="800-207"/>
    <seriesInfo name="DOI" value="10.6028/NIST.SP.800-207"/>
  </reference>
  <reference anchor="SP-800-63-4">
    <front><title>Digital Identity Guidelines</title><author><organization>Temoshok, D., Fenton, J., Choong, Y., Lefkovitz, N., Regenscheid, A., and J. Richer</organization></author><date month="July" year="2025"/></front>
    <seriesInfo name="NIST Special Publication" value="800-63-4"/>
  </reference>
</references>
    <section anchor="acknowledgements" numbered="false">
      <name>Acknowledgements</name>
      <t>
        AIP builds directly on the work of the W3C DID Working Group,
        IETF OAuth Working Group, and NIST NCCoE AI Agent Identity and
        Authorization Concept Paper (2026).
      </t>
    </section>
    <section anchor="appendix-a" numbered="false">
      <name>Appendix A: Changes from Version 0.1</name>
      <t>This appendix is informative.</t>
      <t>
        This version addresses four operational and security additions relative
        to v0.1:
      </t>
      <ul>
        <li><strong>Section 4.5 - AIP-GRANT Principal Authorization Protocol.</strong> Defines the standardised ceremony by which a human principal reviews, consents to, and cryptographically authorises an agent delegation. Analogous to the OAuth 2.0 Authorization Code Flow.</li>
        <li><strong>Section 4.6 - Chained Approval Envelopes.</strong> Defines a workflow-level authorisation primitive. A single human approval covers a pre-declared sequence of dependent agent actions, including SAGA compensation steps. Addresses the cascading-approval problem and the token-expiry-while-pending problem for Tier 2 workflows.</li>
        <li><strong>Section 7.4 - Token Refresh and Long-Running Tasks.</strong> Specifies when and how agents re-issue Credential Tokens, pre-emptive refresh requirements, and handling of delegation chain expiry.</li>
        <li><strong>Section 14 (revised) - Rate Limiting and Abuse Prevention.</strong> Defines per-endpoint rate limit categories, required response headers, anti-abuse rules for registration and validation-driven lookups, and graduated backoff requirements.</li>
      </ul>
    </section>
    <section anchor="appendix-b" numbered="false">
      <name>Appendix B: Changes from Version 0.2</name>
      <t>This appendix is informative.</t>
      <t>
        v0.3 introduces the following changes relative to v0.2. Items marked
        <strong>(breaking)</strong> are not backward compatible.
      </t>
      <t><strong>Breaking changes:</strong></t>
      <ul>
        <li><strong>(breaking) §3 - Tiers reframed as threat-model declarations.</strong> Tier 1 (bounded-staleness), Tier 2 (real-time revocation), Tier 3 (enterprise/regulated). Each Tier now enumerates its security property declarations rather than just listing scopes.</li>
        <li><strong>(breaking) §3, §4.5.3, §7.2 - <tt>spawn_agents</tt> scope retired.</strong> Replaced by <tt>spawn_agents.create</tt> and <tt>spawn_agents.manage</tt>, both classified as Tier 2 with 300s max TTL and DPoP requirement.</li>
        <li><strong>(breaking) §4.5 - Three-tier grant model.</strong> AIP-GRANT restructured into G1 (Registry-Mediated), G2 (Direct Deployer), and G3 (Full Ceremony with OAuth 2.1). Registration Envelopes MUST include <tt>grant_tier</tt>. <tt>did:key</tt> principals forbidden for Tier 2.</li>
        <li><strong>(breaking) §6.2 - AIPRegistry service entry.</strong> Principal DID Documents authorising Tier 2 agents MUST include an <tt>AIPRegistry</tt> service entry.</li>
        <li><strong>(breaking) §6.3.4 - Well-known schema extended.</strong> The <tt>/.well-known/aip-registry</tt> response MUST include <tt>signature</tt>, <tt>registry_name</tt>, and <tt>endpoints</tt> fields.</li>
        <li><strong>(breaking) §7.1 - Version header.</strong> <tt>X-AIP-Version</tt> updated from <tt>"0.2"</tt> to <tt>"0.3"</tt>.</li>
      </ul>
      <t><strong>Additive changes:</strong></t>
      <ul>
        <li><strong>§1.2 - MCP integration note.</strong> Informative note clarifying AIP's relationship with Model Context Protocol.</li>
        <li><strong>§4.2.3 - New claims.</strong> <tt>aip_approval_step</tt> (integer) and <tt>aip_engagement_id</tt> (string) added to Credential Token schema.</li>
        <li><strong>§4.2.4 - Principal Token claims.</strong> <tt>acr</tt> and <tt>amr</tt> claims added for G3 identity proofing. <tt>iss</tt> field added.</li>
        <li><strong>§4.7 - Capability Overlays.</strong> New section defining context-specific capability attenuation with Rules CO-1 through CO-6.</li>
        <li><strong>§4.8 - Engagement Objects.</strong> New section defining multi-party engagement lifecycle, append-only change log, and approval gates.</li>
        <li><strong>§7.3 - Validation steps.</strong> Step 6a (Registry Trust Anchoring), Step 6b (Engagement validation), and Step 9e (Overlay application) added.</li>
        <li><strong>§7.5 - Token Exchange.</strong> New section for RFC 8693 token exchange with DPoP binding, enabling AIP-to-OAuth bridging for MCP.</li>
        <li><strong>§9.4 - RPNP.</strong> Registry Push Notification Protocol for near-real-time event delivery (5s SLA).</li>
        <li><strong>§13.2 - Error codes.</strong> 26 new error codes for all v0.3 features.</li>
        <li><strong>§14.6 - Backoff formula corrected.</strong> <tt>BACKOFF_BASE</tt> separated from <tt>Retry-After</tt>; jitter bounded by <tt>BACKOFF_BASE</tt>.</li>
        <li><strong>§15.5 - OAuth 2.1 AS.</strong> Authorization server interface for G3.</li>
        <li><strong>§15.6 - Scope Map.</strong> <tt>urn:aip:scope:</tt> URI namespace and <tt>GET /v1/scopes</tt> endpoint.</li>
        <li><strong>§15.7 - Engagement Endpoints.</strong> CRUD for Engagement Objects.</li>
        <li><strong>§15.8 - RPNP Endpoints.</strong> Subscription management.</li>
        <li><strong>§16.1 - Tier Conformance table.</strong> Maps Tiers to required security properties.</li>
        <li><strong>§17.1 - Threat scenarios.</strong> TS-12 (orchestrator cascade), TS-13 (overlay injection), TS-14 (engagement tampering).</li>
        <li><strong>§19 - References.</strong> 10 new normative references (RFC 6454, 6960, 7636, 8176, 8414, 8693, 8707, 9068, 9728; RFC 8785 already present). 1 new informative reference (OWASP LLM Top 10).</li>
      </ul>
    </section>
  </back>

</rfc>
