<?xml version='1.0' encoding='UTF-8'?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" version="3" docName="draft-singla-agent-identity-protocol-02" 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-02"/>

    <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="May" day="14"/>

    <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>
    </abstract>
  </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 the following design principles and relationships:
    </t>
    <t>
      Neutral and open. This document is submitted under BCP 78, BCP 79,
      and the IETF Trust Legal Provisions. Code Components extracted from
      this document are licensed under the Revised BSD License as described
      in those Legal Provisions. This document defines the <tt>did:aip</tt>
      DID method and requests its registration with the W3C DID Method
      Registry.
    </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 (Section 9) 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 is designed to support the zero trust
      principles defined in <xref target="SP-800-207"/> and to provide
      protocol controls that can be used by deployments seeking alignment
      with NIST SP 800-63-4 <xref target="SP-800-63-4"/> AAL2 for
      agent-to-service interactions. This specification does not by itself
      assert SP 800-63-4 conformance, certification, or audit sufficiency.
      Any implementation claiming SP 800-63-4 or AAL2 conformance MUST
      maintain a separate conformance mapping covering its concrete
      authenticators, identity proofing process, verifier behavior, key
      management, logging, and operational controls. No agent is implicitly
      trusted by virtue of its origin, network location, or prior successful
      interaction. Every interaction MUST execute the validation algorithm in
      <xref target="credential-token-validation"/>; Registry-dependent checks
      are performed according to the token's Tier, revocation-checking mode,
      and cache rules. Short-lived Credential Tokens bound by a mandatory TTL
      limit the blast radius of any credential compromise. Revocation is
      checked in real time for Tier 2 sensitive operations as specified in
      <xref target="revocation-checking"/>. DPoP proof-of-possession
      (Section 21.3) ensures that
      possession of a valid Credential Token is insufficient for impersonation
      without the corresponding private key material, supporting the
      anti-replay objectives of <xref target="SP-800-207"/> Section 3.
    </t>
    <t>
      Least Privilege. Agents are granted only the specific capabilities
      required for their declared purpose, expressed as signed Capability
      Manifests (Section 5.3). Capability grants are always additive from
      principal to agent and never exceed the principal-approved capability
      set or, for delegated agents, the delegating parent's effective
      Capability Manifest. A child agent must not be granted capabilities that
      the delegating parent does not itself hold (Rule D-1, Section 5.10).
      The max_delegation_depth field (Section 10) 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. An
      AIP-authenticated agent can obtain scoped access tokens for MCP servers
      via the token exchange mechanism defined in
      <xref target="token-exchange-mcp"/> and the OAuth 2.1 Authorization
      Server profile in <xref target="oauth-registry"/>.
    </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 revoked via <tt>full_revoke</tt>
      (Section 11.1). Tier 2 and Tier 3 revocation checks fail closed against
      live Registry state; Tier 1 rejection is bounded by the CRL freshness
      SLA, while child materialisation and replica convergence are bounded by
      the revocation propagation requirements in Section 11. 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>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</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, and the other rules 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, AIP first constructs the object-specific
      signing input described below, then serializes that signing input with
      the JSON Canonicalization Scheme (JCS) defined in
      <xref target="RFC8785"/>. The following procedure MUST be used when
      computing or verifying signatures, and when computing Action Hashes:
    </t>
    <ol type="%d">
      <li>Represent the object as a JSON value per <xref target="RFC8259"/>.</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>
      <li>Serialize the resulting signing-input JSON value using JCS
      <xref target="RFC8785"/>. Array element order MUST be preserved; arrays
      MUST NOT be sorted.</li>
      <li>Encode the JCS result as a UTF-8 byte sequence with no BOM.</li>
    </ol>
    <t>
      Every object type signed with this procedure MUST define a top-level
      signature member in its schema or in the object-specific signing profile.
      If an unsigned object instance is being prepared for signing and the
      top-level signature member is absent, the signer MUST inject that member
      with value <tt>""</tt> before canonicalization. Verifiers MUST reconstruct
      the same signing input by replacing the received signature value with
      <tt>""</tt>. Object types that do not define a signature member MUST NOT
      be signed with this procedure unless a later profile explicitly defines
      the signature member name and signing input.
    </t>
    <t>
      The serialization step is JCS. The preceding signature-field
      replacement or injection is AIP-specific signing-input normalization and
      is not part of RFC 8785 itself. Implementations SHOULD use an
      RFC8785-conformant library to ensure serialization correctness. When
      this procedure is used to compute Action Hashes, 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>
      This canonical serialization procedure applies to Capability Manifests,
      Agent Identity Objects, Revocation Objects, Endorsement Objects,
      GrantRequest Objects when signed, and Approval Envelopes. It MUST NOT
      be used for JWT-format objects, including Credential Tokens, Principal
      Tokens, Step Execution Tokens, and RPNP push notifications. JWT-format
      objects MUST use standard JWS compact serialization per
      <xref target="RFC7515"/>.
    </t>
  </section>
</section>
    <section anchor="terminology" numbered="true" xml:base="sections/03-architecture.xml">
  <name>Definitions and 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 cryptographically derived, persistent identifier for an agent
      conforming to the did:aip DID method defined in Section 4.1. An AID is
      a W3C DID. A standalone <tt>did:aip</tt> string does not encode its
      authoritative Registry; resolution requires Registry context as defined
      in Section 7.</t></dd>

    <dt>Decentralized Identifier (DID):</dt>
    <dd>A W3C Decentralized Identifier. DID Documents, DID URLs, and DID
      resolution are used as defined by W3C DID Core.</dd>

    <dt>Production Deployment:</dt>
    <dd>Any AIP Registry or Relying Party deployment where more than one
      process, container, or server instance may validate tokens for the same
      AID simultaneously, or where the deployment serves external principals
      not under the operator's direct control. Deployments that are explicitly
      single-instance and serve only internal development or testing purposes
      are not production deployments for the purposes of
      <xref target="token-cache-requirements-stateless"/>.</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.
      Throughout this document, Principal Wallet references use this capitalized
      term unless the context explicitly describes a generic wallet ecosystem.</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 5.3.</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 8.1.</dd>

    <dt>Step Execution Token (SET):</dt>
    <dd>A short-lived Registry-issued JWT, distinct from an agent-issued
      Credential Token, attesting that a specific Approval Envelope step has
      been claimed by its designated actor. Defined in Section 13.8 and
      validated using the Step Execution Token profile in Section 9.</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 or Step Execution
      Token.
      Defined in Section 5.5.</t></dd>

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

    <dt>Authorization Server (AS):</dt>
    <dd>The OAuth 2.1 authorization server role. In G3 grant flows, the
      Registry commonly acts as the AS for authorization-code and token
      endpoints.</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>An operation security classification derived from the highest-risk
      requested scope in the synced AIP Scope Catalog. Tier determines the
      applicable revocation-checking mode, maximum Credential Token lifetime,
      and mandatory security mechanisms. Three Tiers are defined in
      <xref target="architecture-tiers"/>. Tier selection on performance or
      availability grounds alone, without using the synced catalog metadata,
      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 12. Grant Tier is a ceremony-assurance axis; it is
      distinct from the security Tier derived from an operation's requested
      scopes.</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 5.11.</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 5.12.</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 13.</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 13.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 (<xref target="registry"/>) and the Validation
      Algorithm (<xref target="credential-token-validation"/>). 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
      12.</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 5.7.</dd>

    <dt>Certificate Revocation List (CRL):</dt>
    <dd>An AIP signed JSON revocation artifact published by the Registry for
      Tier 1 bounded-staleness validation. Tier 2 and Tier 3 validation use
      live Registry revocation status lookups; they do not satisfy their
      real-time revocation requirement by fetching an on-demand CRL. Defined
      in <xref target="crl"/>.</dd>

    <dt>DPoP Proof:</dt>
    <dd>A Demonstration of Proof-of-Possession as defined in <xref target="RFC9449"/>. Mandatory for Tier 2 and Tier 3 operations, and
      also mandatory for any Tier 1 scope or endpoint that explicitly
      requires DPoP. Binds a Credential Token to a specific HTTP request and
      the agent's key material.</dd>

    <dt>Authentication Assurance Level 2 (AAL2):</dt>
    <dd>A NIST SP 800-63 assurance level for authentication strength. When
      this specification refers to AAL2, it means AAL2 as defined by the
      cited NIST SP 800-63 document.</dd>

    <dt>JWK, JWS, and JWT:</dt>
    <dd>JSON Web Key, JSON Web Signature, and JSON Web Token, respectively.
      AIP uses JWK for public key representation, JWS compact serialization
      for signed JWTs, and JWT payloads for Credential Tokens, Principal
      Tokens, Step Execution Tokens, and RPNP push notifications.</dd>

    <dt>Lowercase Hexadecimal Encoding (LCHEX):</dt>
    <dd>The lowercase hexadecimal representation of a byte string, using
      characters <tt>0-9</tt> and <tt>a-f</tt> only.</dd>

    <dt>Registry Push Notification Protocol (RPNP):</dt>
    <dd>The optional Registry push protocol for revocation notifications,
      defined in <xref target="rpnp"/>.</dd>

    <dt>Enterprise Identity Provider (Enterprise IdP):</dt>
    <dd>An OAuth 2.0/2.1 or OIDC-compliant authorization server operated by
      an enterprise organisation, such as Microsoft Entra ID, Okta, Ping
      Identity, or an equivalent system, that manages human principal
      identities, enforces enterprise access policies including Conditional
      Access and ABAC, and issues access tokens scoped to enterprise
      resources. In AIP, an Enterprise IdP is the authorization server that a
      Relying Party trusts for resource access tokens, distinct from the AIP
      Registry acting as an OAuth AS for AIP-scope token exchange. Registries
      identify an Enterprise IdP by the presence of the
      <tt>enterprise_idp_required</tt> flag on a registered resource record
      (see <xref target="enterprise-idp-federation-profile"/>).</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 14.1.</dd>
  </dl>

  <section anchor="abbreviations" numbered="true">
    <name>Abbreviations</name>
    <t>
      The following common abbreviations are used with their ordinary industry
      meanings or with the referenced external specifications:
    </t>
    <dl newline="false">
      <dt>ABAC</dt><dd>Attribute-Based Access Control.</dd>
      <dt>BOM</dt><dd>Byte Order Mark. AIP canonical JSON signing input uses
        UTF-8 without a leading BOM.</dd>
      <dt>CDN</dt><dd>Content Delivery Network.</dd>
      <dt>DAG</dt><dd>Directed Acyclic Graph.</dd>
      <dt>FIDO2/WebAuthn</dt><dd>FIDO2 and Web Authentication.</dd>
      <dt>HSM</dt><dd>Hardware Security Module.</dd>
      <dt>HMAC</dt><dd>Hash-based Message Authentication Code.</dd>
      <dt>JCS</dt><dd>JSON Canonicalization Scheme, defined by <xref target="RFC8785"/>.</dd>
      <dt>JWKS</dt><dd>JSON Web Key Set, the JWK container format used by
        OAuth and OIDC providers to publish signing keys.</dd>
      <dt>MITM</dt><dd>Man-in-the-Middle.</dd>
      <dt>mTLS</dt><dd>Mutual TLS.</dd>
      <dt>OCSP</dt><dd>Online Certificate Status Protocol, defined by <xref target="RFC6960"/>.</dd>
      <dt>PII</dt><dd>Personally Identifiable Information.</dd>
      <dt>PKCE</dt><dd>Proof Key for Code Exchange, defined by <xref target="RFC7636"/>.</dd>
      <dt>SAGA</dt><dd>Distributed-workflow compensation pattern.</dd>
      <dt>SLA</dt><dd>Service-Level Agreement.</dd>
      <dt>TTL</dt><dd>Time To Live.</dd>
    </dl>
  </section>

  <section anchor="architecture-tiers" numbered="true">
    <name>Architecture Tiers</name>
    <t>
      AIP defines three security Tiers that map synced AIP Scope Catalog
      metadata to mandatory protocol behaviors. Tiers are NOT performance
      classes and are not locally chosen labels; they are derived from the
      highest-risk requested scope.
    </t>
    <ul>
      <li><strong>Tier 1 (Bounded-staleness):</strong> Optimized for high availability
        and low latency. Revocation is checked against a Registry-published CRL
        with a mandatory 15-minute update SLA. Principals MAY use <tt>did:key</tt>
        for Tier 1 operations.</li>
      <li><strong>Tier 2 (Real-time):</strong> The default Tier for sensitive operations.
        Revocation status MUST be checked in real-time against the Registry on
        every interaction. DPoP proof-of-possession (RFC 9449) is REQUIRED.
        Principals MUST use <tt>did:web</tt> to enable Registry trust anchoring.</li>
      <li><strong>Tier 3 (Regulated/Enterprise):</strong> The highest security level
        for regulated or high-value environments. Tier 3 supplements Tier 2 with
        mandatory mutual TLS (mTLS) and certificate revocation checking using
        OCSP or an equivalent deployment-profile mechanism defined in
        Section 21.6.</li>
    </ul>
    <t>
      The complete mapping of Tiers to normative protocol requirements is defined in
      the Tier Conformance Table in Section 20.1.
    </t>
  </section>
</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 cryptographically derived, persistent identifier conforming     to the W3C Decentralized Identifier (DID) standard [W3C-DID]. This
    document defines the AIP DID method <tt>did:aip</tt> and provides
    non-IANA W3C DID Method Registry registration information in
    <xref target="iana-did-method"/>.
  </t>

  <section anchor="aid-syntax" numbered="true">
  <name>AID Syntax</name>
    <t>
      An Agent Identity Descriptor (AID) conforms to the following ABNF:
    </t>
    <artwork type="abnf">
	AID        = "did:aip:" namespace ":" agent-id
	namespace  = LOALPHA *( LOALPHA / DIGIT )
	             *( "-" 1*( LOALPHA / DIGIT ) )
	agent-id   = 32LOHEXDIG
	LOALPHA    = %x61-7A        ; a-z only, lowercase
	DIGIT      = %x30-39
	LOHEXDIG   = DIGIT / "a" / "b" / "c" / "d" / "e" / "f"
	                            ; lowercase hex digit only
    </artwork>
    <t>
      Example: <tt>did:aip:personal:9f3a1c82b4e6d7f0a2b5c8e1d4f7a0b3</tt>
    </t>
    <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>
    <ul>
      <li><tt>namespace</tt>: A lowercase alphanumeric string with optional
        single-hyphen-separated segments identifying the agent type. Each
        hyphen MUST be followed by one or more lowercase alphanumeric
        characters. Concrete namespace registrations are maintained in the AIP
        Namespace Catalog (<xref target="scope-map-registry"/>).</li>
      <li><tt>agent-id</tt>: A 32-character hexadecimal string (lowercase)
        computed as the leftmost 128 bits (first 16 octets) of the SHA-256
        hash of the agent's public Ed25519 key material (the JWK <tt>x</tt>
        value base64url-decoded).</li>
    </ul>
  </section>

  <section anchor="aid-derivation" 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
        digest.</li>
      <li>Take the leftmost 16 octets of that digest and hex-encode them
        (lowercase) to form the 32-character <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 corresponding to the registered public key
      is both necessary and sufficient to prove control of the AID.
    </t>
  </section>

  <section anchor="registered-namespaces" numbered="true">
  <name>AIP Namespace Catalog</name>
    <t>
      Standard and community AID namespace registrations are defined by the
      immutable AIP Catalog Snapshot identified in
      <xref target="scope-map-registry"/> and exposed by conformant
      Registries through the Namespace Catalog endpoint defined there. The
      Draft-02 Catalog Snapshot contains the standard namespace entries for
      personal, enterprise, service, orchestrator, ephemeral, and registry
      agents.
    </t>
    <t>
      Namespace registration metadata is normative for namespace-level
      lifecycle rules. If a namespace entry sets
      <tt>requires_task_id: true</tt>, Principal Tokens for agents in that
      namespace MUST contain a non-empty <tt>task_id</tt>. If a namespace
      entry sets <tt>reserved: true</tt>, the namespace MUST NOT be registered
      through the normal <tt>POST /v1/agents</tt> flow.
    </t>
    <t>
      Namespaces are immutable once registered. Custom or community namespaces
      MUST be registered in the AIP Namespace Catalog before they are treated
      as interoperable AIP namespaces.
    </t>
  </section>

  <section anchor="agent-identity-object-schema" 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 5.2 (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 anchor="aid-uniqueness" numbered="true">
  <name>Uniqueness and Immutability</name>
    <t>
      An AID MUST be unique within the authoritative Registry namespace and is
      cryptographically derived from the agent public key. Once an agent is
      registered in a Registry, its AID is immutable. An AID cannot be reused
      or transferred within that Registry.
      Two agents with identical public keys will derive identical AIDs, so
      the cryptographic relationship is deterministic. If two different
      public keys derive the same AID, the Registry MUST treat the later
      registration as an AID collision and reject it as a duplicate AID.
      If a registration attempt presents a public key that is already
      associated with a registered, non-revoked AID in that Registry, the
      Registry MUST reject the attempt with <tt>aid_already_registered</tt>.
    </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
    registration envelopes. Non-JWT signed JSON objects use the canonical JSON
    serialization rules of Section 2.1 unless a specific object defines a
    narrower detached-signature target.
  </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. AID syntax is normatively defined by the
      <tt>AID</tt>, <tt>namespace</tt>, and <tt>agent-id</tt> ABNF productions
      in Section 4.1. This section does not define a second AID grammar.
      JSON Schemas and implementations MUST use grammar equivalent to Section
      4.1.
    </t>
    <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>
      Concrete namespace values are maintained in the AIP Namespace Catalog
      (<xref target="scope-map-registry"/>), not in this prose table. The namespace grammar from
      <xref target="aid-syntax"/> remains the wire-format constraint; catalog membership
      supplies the interoperability and lifecycle metadata for each namespace.
    </t>

    <t>
      Any namespace marked <tt>reserved: true</tt> in the AIP Namespace
      Catalog MUST NOT be registered via the standard POST /v1/agents
      endpoint. The standard <tt>registry</tt> namespace is reserved and
      MUST be created exclusively via the Registry Genesis procedure.
    </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-446655440000</td>
        </tr>
        <tr>
          <td>Revocation Object</td>
          <td>rev:</td>
          <td>rev:6ba7b810-9dad-41d1-80b4-00c04fd430c8</td>
        </tr>
        <tr>
          <td>Endorsement Object</td>
          <td>end:</td>
          <td>end:6ba7b811-9dad-41d1-80b4-00c04fd430c8</td>
        </tr>
        <tr>
          <td>Grant Request</td>
          <td>gr:</td>
          <td>gr:550e8401-e29b-41d4-a716-446655440000</td>
        </tr>
        <tr>
          <td>Approval Envelope</td>
          <td>apr:</td>
          <td>apr:550e8402-e29b-41d4-a716-446655440000</td>
        </tr>
      </tbody>
    </table>
  </section>

  <section anchor="agent-identity" numbered="true">
    <name>Agent Identity Object</name>
    <t>
      The Agent Identity Object is the canonical signed JSON document that
      establishes an agent's identity. The key-continuity signing target for
      key rotation is defined by the <tt>previous_key_signature</tt>
      requirements below.
    </t>

    <dl newline="false">
      <dt>aid</dt>
      <dd><t>Type: string. Required: REQUIRED. Constraints: MUST match did:aip ABNF; pattern is lowercase namespace plus a 32-character lowercase hexadecimal agent-id derived from the leftmost 128 bits of the SHA-256 digest of the agent public key.</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 for Tier 1, SHOULD be present for Tier 2, REQUIRED for Tier 3. Constraints: pattern <tt>sha256:&lt;64 lowercase hex characters&gt;</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 computed by the
      retiring previous private key over the new full Agent Identity Object,
      serialized using the Section 2.1 canonical JSON procedure with
      <tt>previous_key_signature</tt> 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>The <tt>model.attestation_hash</tt> field is the protocol binding
        between the registered agent identity and a model binary or version
        manifest. It SHOULD be present for Tier 2 deployments and MUST be
        present for Tier 3 deployments. A missing hash means the AID is not
        cryptographically pinned to a specific model artifact.</li>
      <li>When version is 2 or greater, previous_key_signature MUST be present and MUST be a non-empty base64url string.</li>
      <li>For key rotation, the <tt>previous_key_signature</tt> value MUST be
        generated with the retiring previous private key, not the new private
        key. The signing target is the complete new Agent Identity Object
        after applying the new <tt>version</tt> and <tt>public_key</tt>,
        serialized per Section 2.1 with <tt>previous_key_signature</tt>
        temporarily set to <tt>""</tt>. The computed signature then replaces
        that empty string in the stored Agent Identity Object. A Registry MUST
        verify this signature with the public key from the immediately
        previous Agent Identity Object version before accepting the
        rotation.</li>
    </ul>
  </section>

  <section anchor="capability-manifest" numbered="true">
    <name>Capability Manifest</name>
    <t>
      The Capability Manifest is a versioned, signed JSON document that
      declares the specific permissions granted to an agent. The
      <tt>signature</tt> field is computed using the Section 2.1 canonical
      JSON procedure with <tt>signature</tt> set to <tt>""</tt>. The
      <tt>signature_kid</tt> field identifies the Ed25519 verification method
      used for the 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:&lt;UUIDv4-lowerhex&gt;</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 matching did:&lt;method&gt;:&lt;method-specific-id&gt;</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 5.9 for all sub-fields</td>
        </tr>
        <tr>
          <td>signature_kid</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>DID URL controlled by <tt>granted_by</tt></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 identified by
      <tt>signature_kid</tt> before trusting any capability declared within it.
      The DID portion of <tt>signature_kid</tt> MUST be byte-for-byte equal to
      <tt>granted_by</tt>.
    </t>
  </section>

  <section anchor="credential-token" numbered="true">
    <name>Credential Token</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].
      Credential Tokens conforming to this specification MUST be signed with
      EdDSA using the registered Ed25519 key identified by the JOSE
      <tt>kid</tt>. Credential Tokens signed with <tt>ES256</tt>,
      <tt>RS256</tt>, symmetric algorithms, <tt>none</tt>, or non-Ed25519 key
      material MUST be rejected.
    </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"</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>

    <dl newline="false">
      <dt><tt>aip_version</tt></dt>
      <dd>string, REQUIRED. AIP protocol compatibility version. MUST be
      <tt>"0.3"</tt> for tokens conforming to this specification. See
      <xref target="versioning"/> for version semantics and
      <xref target="validation-step-5f"/> for validation.</dd>

      <dt><tt>iss</tt></dt>
      <dd>string, REQUIRED. MUST match the <tt>did:aip</tt> ABNF; MUST equal
      the AID identified by the JWT header <tt>kid</tt>; MUST equal
      <tt>sub</tt>; MUST equal <tt>sub</tt> of the last
      <tt>aip_chain</tt> element.</dd>

      <dt><tt>sub</tt></dt>
      <dd>string, REQUIRED. MUST match the <tt>did:aip</tt> ABNF; MUST equal
      <tt>iss</tt>. For an agent-issued Credential Token, this is the acting
      leaf agent's AID. This specification does not define an
      on-behalf-of subject distinct from the signing agent.</dd>

      <dt><tt>aud</tt></dt>
      <dd>string or array, REQUIRED. Single string or array; MUST include the
      Relying Party's identifier.</dd>

      <dt><tt>iat</tt></dt>
      <dd>integer, REQUIRED. Unix timestamp; MUST NOT be in the future, with a
      30-second clock skew tolerance.</dd>

      <dt><tt>exp</tt></dt>
      <dd>integer, REQUIRED. Unix timestamp; MUST be strictly greater than
      <tt>iat</tt>; TTL limits per <xref target="token-issuance"/>.</dd>

      <dt><tt>jti</tt></dt>
      <dd>string, REQUIRED. UUID v4 canonical lowercase; unique per issuer AID
      across all delegation chains.</dd>

      <dt><tt>aip_scope</tt></dt>
      <dd>array, REQUIRED. <tt>minItems: 1</tt>; <tt>uniqueItems: true</tt>;
      each item matches
      <tt>&lt;dot-separated-scope-name&gt;</tt>.</dd>

      <dt><tt>aip_chain</tt></dt>
      <dd>array, REQUIRED. <tt>minItems: 1</tt>; <tt>maxItems: 11</tt>; each
      element is a compact-serialised signed Principal Token JWT. Elements are
      ordered root-to-leaf, and the last element's <tt>sub</tt> is the acting
      agent AID. The maximum length corresponds to delegation depths 0 through
      10.</dd>

      <dt><tt>aip_principal_assertion</tt></dt>
      <dd>string, OPTIONAL for Tier 1 and Tier 2; REQUIRED for Tier 3. A
      compact-serialised signed JWT issued by an enterprise Identity Provider,
      such as an OIDC ID Token or equivalent JWT-formatted assertion. The
      assertion MUST contain a <tt>sub</tt> claim identifying the human
      principal. When present, the Relying Party MUST verify the assertion's
      signature against the IdP's JWKS endpoint before treating the human
      identity context as valid. The <tt>sub</tt> claim in
      <tt>aip_principal_assertion</tt> MUST match the root Principal Token's
      <tt>principal.id</tt> value in <tt>aip_chain[0]</tt>. The issuer
      (<tt>iss</tt>) of this JWT MUST NOT be the AIP Registry. See
      <xref target="validation-step-11"/> Steps 11a, 11b, and 11c for
      normative validation.</dd>

      <dt><tt>aip_originator_aid</tt></dt>
      <dd>string, OPTIONAL; MUST be present when the agent is acting in a
      delegated agent-to-agent (A2A) workflow as specified in
      <xref target="delegated-identity-chaining-a2a"/>. The value is the AID
      of the originating agent that initiated the A2A workflow. When present,
      it MUST be a valid <tt>did:aip</tt> AID conforming to the ABNF in
      <xref target="aid-syntax"/> and MUST NOT equal the <tt>sub</tt> claim
      of this Credential Token.</dd>

      <dt><tt>aip_registry</tt></dt>
      <dd>string, OPTIONAL. Advisory URI of the AIP Registry; if present, Step
      6a trust anchoring is REQUIRED.</dd>

      <dt><tt>aip_engagement_id</tt></dt>
      <dd>string, OPTIONAL. Pattern:
      <tt>eng:&lt;UUIDv4-lowerhex&gt;</tt>;
      present when the token is scoped to an Engagement Object.</dd>
    </dl>

  <section anchor="step-execution-token" numbered="true">
    <name>Step Execution Token</name>
    <t>
      A Step Execution Token (SET) is a compact JWT issued by a Registry after
      a step actor successfully claims an Approval Envelope step. It is not an
      agent-issued Credential Token. Its issuer is the Registry ID, and its
      subject is the actor AID that claimed the step.
    </t>
    <t>
      <strong>SET JWT Header fields:</strong>
    </t>

    <table>
      <name>Step Execution Token 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-SET+JWT"</td>
        </tr>
        <tr>
          <td>alg</td>
          <td>MUST</td>
          <td>"EdDSA"</td>
        </tr>
        <tr>
          <td>kid</td>
          <td>MUST</td>
          <td>HTTPS URI identifying a Registry step-execution verification key listed in the Registry Trust Record version current at issuance time</td>
        </tr>
        <tr>
          <td>aip_trv</td>
          <td>MUST</td>
          <td>Integer Registry Trust Record version whose <tt>active_verification_keys.step_execution</tt> contains <tt>kid</tt></td>
        </tr>
      </tbody>
    </table>

    <t>
      The <tt>aip_trv</tt> header is a protected JOSE header value
      used only to select an already pinned Registry Trust Record. It is not a
      trust anchor. Relying Parties MUST NOT accept a Step Execution Token
      unless they already have pinned trust state for <tt>iss</tt> and for
      the indicated Registry Trust Record version.
    </t>

    <t>
      <strong>SET Payload fields:</strong>
    </t>

    <table>
      <name>Step Execution 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>AIP protocol compatibility version; MUST be "0.3" for this spec</td>
        </tr>
        <tr>
          <td>iss</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>Registry ID HTTPS URI; MUST match the issuing Registry Trust Record</td>
        </tr>
        <tr>
          <td>sub</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>AID of the claimed step actor; MUST match the referenced Approval Envelope step actor</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</td>
        </tr>
        <tr>
          <td>exp</td>
          <td>integer</td>
          <td>REQUIRED</td>
          <td>Unix timestamp; MUST be strictly greater than iat; MUST NOT exceed the Step Execution Token TTL limit defined below</td>
        </tr>
        <tr>
          <td>jti</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>UUID v4 canonical lowercase; unique per Registry issuer across all SETs</td>
        </tr>
        <tr>
          <td>aip_scope</td>
          <td>array</td>
          <td>REQUIRED</td>
          <td>Scopes authorised for the claimed step</td>
        </tr>
        <tr>
          <td>aip_chain</td>
          <td>array</td>
          <td>REQUIRED</td>
          <td>Principal Token chain for the actor AID in sub</td>
        </tr>
        <tr>
          <td>aip_registry</td>
          <td>string</td>
          <td>OPTIONAL</td>
          <td>If present, MUST equal iss</td>
        </tr>
        <tr>
          <td>aip_approval_id</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>Approval Envelope identifier; pattern: apr:&lt;UUIDv4-lowerhex&gt;</td>
        </tr>
        <tr>
          <td>aip_step_kind</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>Either <tt>forward</tt> or <tt>compensation</tt></td>
        </tr>
        <tr>
          <td>aip_approval_step</td>
          <td>integer</td>
          <td>CONDITIONAL</td>
          <td>1-based step_index; REQUIRED when <tt>aip_step_kind</tt> is <tt>forward</tt>; MUST be absent for compensation SETs</td>
        </tr>
        <tr>
          <td>aip_compensation_step</td>
          <td>integer</td>
          <td>CONDITIONAL</td>
          <td>1-based compensation_index; REQUIRED when <tt>aip_step_kind</tt> is <tt>compensation</tt>; MUST be absent for forward SETs</td>
        </tr>
        <tr>
          <td>aip_engagement_id</td>
          <td>string</td>
          <td>OPTIONAL</td>
          <td>Engagement Object identifier when the parent Approval Envelope is engagement-scoped</td>
        </tr>
      </tbody>
    </table>
    <t>
      The Registry MUST derive a Step Execution Token TTL limit as the minimum
      of: (1) the Registry's <tt>step_claim_timeout_seconds</tt> value
      published in <tt>/v1/registry-metadata</tt>; (2) the lowest
      <tt>ttl_max_seconds</tt> value for any requested <tt>aip_scope</tt> in
      the synced AIP Scope Catalog; and (3) the Tier ceiling for the
      highest-risk requested scope from <xref target="token-issuance"/>. The
      Registry MUST set <tt>exp</tt> no later than
      <tt>iat + effective_set_ttl_limit</tt>. A Relying Party validating a
      SET MUST compute the same scope and Tier limit through
      <xref target="validation-step-6"/> and reject a SET whose
      <tt>exp - iat</tt> exceeds that limit with <tt>invalid_token</tt>. A
      Registry completing or failing a step MUST also enforce the stored claim
      deadline, even if the SET has not yet expired.
    </t>
  </section>
  </section>

  <section anchor="principal-token" numbered="true">
    <name>Principal Token</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 or Step
      Execution Token.
    </t>

    <t>
      A Principal Token MUST be a compact-serialised JWT signed using
      EdDSA. Its JOSE header is outside the payload schema but is
      normative: <tt>typ</tt> MUST be <tt>"JWT"</tt>, <tt>alg</tt> MUST
      be <tt>"EdDSA"</tt>, and <tt>kid</tt> MUST be a DID URL identifying
      an Ed25519 verification method controlled by the token issuer
      identified by <tt>iss</tt>. For the root Principal Token
      (<tt>delegation_depth</tt> 0), the issuer is the root
      <tt>principal.id</tt>. For delegated Principal Tokens, the issuer
      is the parent agent AID in <tt>delegated_by</tt>, and that parent AID
      MUST be registered in the authoritative AIP Registry. Relying Parties
      resolve delegated Principal Token signing keys through the Registry
      historical public-key endpoint and verify that the returned key was
      valid at the Principal Token's <tt>issued_at</tt> instant. Principal Tokens
      signed with <tt>ES256</tt>, <tt>RS256</tt>, symmetric algorithms,
      <tt>none</tt>, or non-Ed25519 key material MUST be rejected.
      X25519 verification methods MUST NOT be used for Principal Token
      signing.
    </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; MUST NOT equal sub</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>OPTIONAL</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; signed consent/audit metadata only; MUST NOT be used as an authorization constraint</td>
        </tr>
        <tr>
          <td>task_id</td>
          <td>string/null</td>
          <td>OPTIONAL; REQUIRED when the subject namespace catalog entry has <tt>requires_task_id: true</tt></td>
          <td>minLength: 1, maxLength: 256 when non-null; enforced at registration and during Step 8 chain validation</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>

    <t>
      For a root Principal Token produced by AIP-GRANT,
      <tt>issued_at</tt> is the issuer's current UTC signing time and
      <tt>expires_at</tt> equals <tt>issued_at</tt> plus the GrantResponse
      <tt>approved_delegation_valid_for_seconds</tt> value. The detailed
      derivation rule is defined in Section 12.3.
    </t>

    <t>
      The <tt>purpose</tt> field, when present, is a signed
      human-readable consent and audit claim. It is not a machine-enforced
      authorization constraint. Relying Parties MAY display or log
      <tt>purpose</tt> for operator context, but MUST NOT use it to grant
      capabilities, satisfy scope checks, or override Capability Manifest,
      Capability Overlay, Approval Envelope, or policy evaluation.
    </t>
  </section>

  <section anchor="registration-envelope" numbered="true">
    <name>Registration Envelope</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); for direct registration delegation_depth MUST be 0 and delegated_by MUST be null; for sub-agent registration delegation_depth MUST be greater than 0 and delegated_by MUST identify the registered parent AID</td>
        </tr>
        <tr>
          <td>grant_tier</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>enum: ["G1", "G2", "G3"]; MUST meet the minimum Grant Tier required by the highest synced AIP Scope Catalog tier in capability_manifest</td>
        </tr>
      </tbody>
    </table>
    <t>
      A Registration Envelope registers exactly one agent: the AID in
      <tt>identity.aid</tt>. The submitted <tt>principal_token</tt> payload
      <tt>sub</tt> MUST equal that AID. Direct principal-to-agent
      registrations use a root Principal Token with
      <tt>delegation_depth: 0</tt> and <tt>delegated_by: null</tt>.
      Sub-agent registrations use a delegated Principal Token signed by the
      registered parent agent identified in <tt>delegated_by</tt>; the
      Registry reconstructs the complete principal chain from the parent's
      stored registration chain and the submitted token during Registration
      Check 9.
    </t>
  </section>

  <section anchor="resource-revocation-object" numbered="true">
    <name>Revocation Object</name>
    <t>
      Revocation is performed by submitting a signed Revocation Object to
      POST /v1/revocations. The <tt>signature</tt> field is computed using
      the Section 2.1 canonical JSON procedure with <tt>signature</tt> set to
      <tt>""</tt>. The signing input order is
      <tt>revocation_id</tt>, <tt>target_id</tt>, <tt>type</tt>,
      <tt>issued_by</tt>, <tt>kid</tt>, <tt>reason</tt>,
      <tt>timestamp</tt>, <tt>propagate_to_children</tt>,
      <tt>scopes_revoked</tt>, and <tt>signature</tt>.
    </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:&lt;UUIDv4-lowerhex&gt;</td>
        </tr>
        <tr>
          <td>target_id</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>MUST be a did:aip AID (for full_revoke, scope_revoke, delegation_revoke) or a Principal DID (for principal_revoke)</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>Issuer DID authorised for the target, or Registry ID for Registry-generated revocations</td>
        </tr>
        <tr>
          <td>kid</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>Verification key identifier; DID URL controlled by issued_by for external submissions, or active Registry CRL key for Registry-generated revocations</td>
        </tr>
        <tr>
          <td>reason</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>enum defined in the Revocation Reason Codes table</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> - Invalidates specific scopes for the agent. For Tier 1 and 2, this MUST cause Relying Parties to reject Credential Tokens containing the revoked scopes (Step 7, Section 9).</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 authorisation of a target agent (via AID) or all agents under their authority (via Principal DID). Existing Credential Tokens and Step Execution Tokens depending on that authorisation become invalid immediately. This validity effect is independent of <tt>propagate_to_children</tt>; that flag controls only whether descendant Revocation Objects are materialised.</li>
    </ul>
    <t>
      <strong>Revocation Reason Codes:</strong> The <tt>reason</tt> field MUST
      be one of the following values:
    </t>
    <table>
      <name>Revocation Reason Codes</name>
      <thead>
        <tr>
          <th align="left">Reason</th>
          <th align="left">Semantics</th>
        </tr>
      </thead>
      <tbody>
        <tr><td><tt>device_compromised</tt></td><td>The principal's device or agent host holding relevant key material was compromised.</td></tr>
        <tr><td><tt>key_compromised</tt></td><td>The agent private key or signing key was directly compromised.</td></tr>
        <tr><td><tt>task_complete</tt></td><td>The agent completed its assigned task and no longer requires the delegated authority.</td></tr>
        <tr><td><tt>policy_violation</tt></td><td>The agent or deployment violated Registry, principal, or relying-party policy.</td></tr>
        <tr><td><tt>principal_request</tt></td><td>The principal requested revocation without a more specific protocol reason.</td></tr>
        <tr><td><tt>account_closure</tt></td><td>The principal or deployer account associated with the delegation is being closed.</td></tr>
        <tr><td><tt>heartbeat_timeout</tt></td><td>Registry-generated reason for Dead Man's Switch expiry.</td></tr>
        <tr><td><tt>lifecycle_expired</tt></td><td>Registry-generated reason for namespace lifecycle expiry.</td></tr>
        <tr><td><tt>parent_revoked</tt></td><td>Registry-generated reason for recursive child revocation after a parent revocation.</td></tr>
        <tr><td><tt>other</tt></td><td>Other auditable reason. Implementations SHOULD record additional detail in local audit metadata.</td></tr>
      </tbody>
    </table>
    <t>
      Externally submitted Revocation Objects MUST NOT use
      <tt>parent_revoked</tt>, <tt>heartbeat_timeout</tt>, or
      <tt>lifecycle_expired</tt>; those values are reserved for
      Registry-generated Revocation Objects.
    </t>
  </section>

  <section anchor="endorsement" numbered="true">
    <name>Endorsement Object</name>
    <t>
      Any Relying Party or agent MAY submit a signed Endorsement Object to
      the Registry after a completed interaction. The <tt>signature</tt>
      field is computed using the Section 2.1 canonical JSON procedure with
      <tt>signature</tt> set to <tt>""</tt>.
    </t>

    <dl newline="false">
      <dt><tt>endorsement_id</tt></dt>
      <dd>string, REQUIRED. Pattern:
      <tt>end:&lt;UUIDv4-lowerhex&gt;</tt>.</dd>

      <dt><tt>from_aid</tt></dt>
      <dd>string, REQUIRED. MUST NOT equal <tt>to_aid</tt>.</dd>

      <dt><tt>to_aid</tt></dt>
      <dd>string, REQUIRED. MUST NOT equal <tt>from_aid</tt>.</dd>

      <dt><tt>task_id</tt></dt>
      <dd>string, REQUIRED. <tt>minLength: 1</tt>;
      <tt>maxLength: 256</tt>.</dd>

      <dt><tt>outcome</tt></dt>
      <dd>string, REQUIRED. One of <tt>success</tt>, <tt>partial</tt>, or
      <tt>failure</tt>.</dd>

      <dt><tt>score</tt></dt>
      <dd>number, REQUIRED. Endorsement score in the range 1 through 5
      inclusive. Values outside this range MUST be rejected with
      <tt>endorsement_invalid</tt>.</dd>

      <dt><tt>notes</tt></dt>
      <dd>string or null, OPTIONAL. Maximum length 512. Signed audit context
      only; MUST NOT affect reputation scoring or validation.</dd>

      <dt><tt>timestamp</tt></dt>
      <dd>string, REQUIRED. ISO 8601 UTC timestamp.</dd>

      <dt><tt>signature_kid</tt></dt>
      <dd>string, REQUIRED. DID URL identifying an Ed25519 verification method
      controlled by <tt>from_aid</tt>.</dd>

      <dt><tt>signature</tt></dt>
      <dd>string, REQUIRED. Base64url EdDSA signature by
      <tt>from_aid</tt>.</dd>
    </dl>

    <t>
      The Registry MUST verify every submitted Endorsement Object signature.
      The DID portion of <tt>signature_kid</tt> MUST equal
      <tt>from_aid</tt>.
      Only success and partial outcomes increment endorsement_count.
      failure increments incident_count.
    </t>

    <t>
      The <tt>notes</tt> field, when present, is signed human-readable audit
      context about the observed task outcome. The Registry MUST preserve it
      as part of the Endorsement Object, but MUST NOT parse, classify, or use
      <tt>notes</tt> to compute reputation scores, increment counters, validate
      capability authorization, or alter endorsement acceptance. Reputation
      effects are determined by the structured fields defined in this section
      and Section 14.
    </t>
  </section>

  <section anchor="field-constraints" numbered="true">
    <name>Field Constraints and Catalog-Bound Scope Identifiers</name>
    <t>
      The Capability Manifest <tt>capabilities</tt> object is a closed object:
      top-level capability families not listed below MUST NOT appear unless a
      Registry accepts them through an explicit private extension policy. The
      immutable machine-readable schema for this draft is
      <tt>https://provai.dev/schemas/aip/capability-manifest/draft-02</tt>,
      also listed as <tt>capability-manifest.schema.json</tt> in the JSON
      Schema Index.
    </t>

    <dl newline="false">
      <dt><tt>email</tt></dt>
      <dd><t>Permitted sub-fields are boolean <tt>read</tt>,
        <tt>write</tt>, <tt>send</tt>, and <tt>delete</tt>, plus optional
        integer <tt>max_recipients_per_send</tt> in the range 1-100. Boolean
        fields default to <tt>false</tt> when absent. The recipient cap applies
        only when <tt>send</tt> is <tt>true</tt>.</t></dd>

      <dt><tt>calendar</tt></dt>
      <dd><t>Permitted sub-fields are boolean <tt>read</tt>,
        <tt>write</tt>, and <tt>delete</tt>. Boolean fields default to
        <tt>false</tt> when absent.</t></dd>

      <dt><tt>filesystem</tt></dt>
      <dd><t>Permitted sub-fields are <tt>read</tt> and <tt>write</tt> arrays
        of absolute path strings, plus boolean <tt>execute</tt> and
        <tt>delete</tt>. Each path string MUST be non-empty and no longer than
        512 characters. Empty or absent path arrays mean deny-all for that
        operation. File deletion is permitted only for paths also permitted by
        <tt>write</tt>.</t></dd>

      <dt><tt>web</tt></dt>
      <dd><t>Permitted sub-fields are boolean <tt>browse</tt>,
        <tt>forms_submit</tt>, and <tt>download</tt>, plus optional integer
        <tt>max_requests_per_hour</tt> in the range 1-10000. Boolean fields
        default to <tt>false</tt> when absent.</t></dd>

      <dt><tt>transactions</tt></dt>
      <dd><t>The <tt>enabled</tt> boolean is REQUIRED when the
        <tt>transactions</tt> object is present. When <tt>enabled</tt> is
        <tt>true</tt>, <tt>max_single_transaction</tt>,
        <tt>max_daily_total</tt>, and <tt>currency</tt> are REQUIRED;
        transaction limits MUST be greater than zero; <tt>currency</tt> MUST
        be an ISO 4217 three-letter uppercase currency code. Optional
        <tt>require_confirmation_above</tt> MUST be less than or equal to
        <tt>max_single_transaction</tt> when both are present.</t></dd>

      <dt><tt>communicate</tt></dt>
      <dd><t>The <tt>enabled</tt> boolean is REQUIRED when the
        <tt>communicate</tt> object is present. Permitted channel fields are
        boolean <tt>whatsapp</tt>, <tt>telegram</tt>, <tt>sms</tt>, and
        <tt>voice</tt>. When <tt>enabled</tt> is <tt>true</tt>, at least one
        channel field MUST be explicitly set to <tt>true</tt>; when
        <tt>enabled</tt> is <tt>false</tt>, channel fields have no granting
        effect.</t></dd>

      <dt><tt>spawn_agents</tt></dt>
      <dd><t>The <tt>enabled</tt> boolean is REQUIRED when the
        <tt>spawn_agents</tt> object is present. When <tt>enabled</tt> is
        <tt>true</tt>, integer <tt>max_concurrent</tt> in the range 1-100 is
        REQUIRED. Optional <tt>types_allowed</tt> lists namespace labels the
        agent may spawn; each value MUST be an active, spawnable entry in the
        synced AIP Namespace Catalog unless a private namespace policy
        explicitly permits it.</t></dd>

      <dt><tt>registry</tt></dt>
      <dd><t>Permitted sub-fields are Registry control-plane grants. Draft-02
        defines boolean <tt>heartbeat</tt>, which grants the
        <tt>registry.heartbeat</tt> scope for authenticated liveness
        submissions.</t></dd>

      <dt><tt>approvals</tt></dt>
      <dd><t>Permitted sub-fields are Approval Envelope control-plane grants.
        Draft-02 defines boolean <tt>create</tt>, which grants the
        <tt>approvals.create</tt> scope for submitting Approval Envelopes to
        <tt>POST /v1/approvals</tt>.</t></dd>
    </dl>

    <t>
      An absent top-level capability family grants no capability in that
      family. An empty <tt>capabilities</tt> object grants no capabilities.
      Implementations MUST NOT treat absent fields as allow-all.
    </t>

    <t>
      <strong>Scope Identifier Grammar and Catalog Binding:</strong>
    </t>

    <t>
      Scope identifiers are dot-separated lowercase ASCII strings. Each
      segment MUST begin with <tt>a-z</tt> or <tt>_</tt>, and subsequent
      characters in the same segment MAY be <tt>a-z</tt>, <tt>0-9</tt>, or
      <tt>_</tt>. The normative pattern is
      <tt>&lt;dot-separated-scope-name&gt;</tt>. This permits
      versioned or tiered scope names such as <tt>transactions.v2</tt> or
      <tt>filesystem.read.tier1</tt> while preserving a non-numeric first
      character in every segment.
    </t>

    <t>
      Concrete scope identifiers and their security metadata are defined by
      the immutable AIP Catalog Snapshot identified in
      <xref target="scope-map-registry"/> and exposed by conformant
      Registries through the Scope Map endpoint defined in
      <xref target="scope-map-registry"/>. A
      scope is interoperable only when it is present with
      <tt>status: "active"</tt> or <tt>status: "experimental"</tt> in the
      synced AIP Scope Catalog, or when a Registry explicitly documents a
      private local extension policy.
    </t>

    <t>
      Tokens containing a scope that is absent from the synced AIP Scope
      Catalog, marked <tt>reserved</tt>, marked <tt>removed</tt>, or outside
      an explicitly documented local extension policy MUST be rejected with
      <tt>invalid_scope</tt>.
    </t>

    <t>
      Where this document references <tt>transactions.*</tt> or
      <tt>communicate.*</tt>, the wildcard is category notation rather than a
      literal scope string. The matching semantics for scope-family entries
      are defined by the AIP Scope Catalog. In the Draft-02 catalog,
      <tt>transactions.*</tt> includes the bare <tt>transactions</tt> scope,
      any active scope beginning with <tt>transactions.</tt>, and
      <tt>capabilities.transactions.enabled: true</tt> in the Capability
      Manifest; <tt>communicate.*</tt> includes active scopes beginning with
      <tt>communicate.</tt> and <tt>capabilities.communicate.enabled: true</tt>
      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</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 <tt>delegation_depth</tt> of any chain token exceeds the root token's
      <tt>max_delegation_depth</tt>. The hard protocol maximum for
      <tt>max_delegation_depth</tt> is 10.
    </t>

    <t>
      <strong>Rule D-4.</strong> The root Principal Token (index 0) MUST have
      <tt>delegation_depth</tt> = 0. Each subsequent token at index
      <tt>i</tt> MUST have <tt>delegation_depth</tt> =
      <tt>i</tt>. No gaps, skips, or repeated values are permitted. Because
      depths 0 through 10 are the complete allowed range, a valid
      <tt>aip_chain</tt> contains at most 11 elements.
    </t>

    <t>
      <strong>Rule D-5.</strong> Each delegation chain token MUST be signed by the
      private key of the <tt>delegated_by</tt> AID (or root principal for
      depth 0). For non-root delegation tokens, the signing AID MUST be a
      registered AIP agent whose public key can be resolved from the Registry
      as specified by Credential Token validation Step 8d-2.
    </t>
  </section>

  <section anchor="capability-overlays" numbered="true">
    <name>Capability Overlays</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:&lt;UUIDv4-lowerhex&gt;</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:&lt;UUIDv4-lowerhex&gt;; links to Engagement Object</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_kid</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>DID URL controlled by <tt>issued_by</tt></td>
        </tr>
        <tr>
          <td>signature</td>
          <td>string</td>
          <td>REQUIRED</td>
          <td>Base64url EdDSA signature by issued_by over the Section 2.1 signing input</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. Constraint
        comparison is recursive for nested objects. An omitted overlay field
        inherits the effective base value and MUST NOT be interpreted as
        removing or relaxing the base constraint.</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. The DID portion of
        <tt>signature_kid</tt> MUST be byte-for-byte equal to
        <tt>issued_by</tt>.</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. A submission whose
        version is less than or equal to the current active overlay version
        MUST be rejected with <tt>overlay_version_conflict</tt>.</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, the following type-specific rules are
      normative. Scope set to <tt>false</tt> removes that scope through the
      boolean rule below.
    </t>
    <t>
      The <tt>intersect</tt> operation is defined recursively over JSON
      values:
    </t>
    <dl newline="false">
      <dt>Objects</dt>
      <dd>
        For each object member present in the base value, if the overlay omits
        that member, the effective value is the base member unchanged. If the
        overlay supplies that member, apply <tt>intersect</tt> recursively. If
        the overlay supplies a member that is absent from the base value, the
        overlay is valid only when this specification or the synced AIP Scope
        Catalog defines absence of that member as an unrestricted allowance or
        default value that the overlay narrows. Otherwise, the overlay expands
        the manifest and MUST be rejected with
        <tt>overlay_exceeds_manifest</tt>.
      </dd>

      <dt>Booleans</dt>
      <dd>
        Boolean fields use logical AND. <tt>false</tt> is more restrictive
        than <tt>true</tt>.
      </dd>

      <dt>Numbers</dt>
      <dd>
        Numeric fields that represent maximum allowances, rate limits, budget
        caps, concurrency caps, or confirmation thresholds use
        <tt>min(base, overlay)</tt>. In the Draft-02 standard Capability
        Manifest schema, this rule applies to
        <tt>email.max_recipients_per_send</tt>,
        <tt>web.max_requests_per_hour</tt>,
        <tt>transactions.max_single_transaction</tt>,
        <tt>transactions.max_daily_total</tt>,
        <tt>transactions.require_confirmation_above</tt>, and
        <tt>spawn_agents.max_concurrent</tt>. A lower
        <tt>require_confirmation_above</tt> value is more restrictive because
        it requires explicit confirmation for a larger set of transactions.
        If the base manifest omits an optional numeric cap whose absence is
        defined as "no cap" or "no confirmation threshold", an overlay MAY add
        that cap and the effective value is the overlay value.
        Numeric fields whose ordering semantics are not defined by the synced
        AIP Scope Catalog or this specification MUST NOT be narrowed by
        ordering; the overlay value MUST equal the base value or be rejected
        with <tt>overlay_exceeds_manifest</tt>.
      </dd>

      <dt>Arrays</dt>
      <dd>
        Arrays of allowed values, including filesystem path arrays and
        <tt>spawn_agents.types_allowed</tt>, use set intersection. The result
        MAY be an empty array, which means deny-all for that allowed-value
        list. If the base manifest omits an optional allowed-value array whose
        absence is defined as all catalog-valid values being allowed, an
        overlay MAY add the array and the effective value is the overlay
        array.
      </dd>

      <dt>Strings and enums</dt>
      <dd>
        String and enum fields are not ordered. If an overlay supplies a
        string or enum value, it MUST be byte-for-byte equal to the base value
        unless the synced AIP Scope Catalog defines explicit subset semantics
        for that field. Otherwise the overlay MUST be rejected with
        <tt>overlay_exceeds_manifest</tt>. For example,
        <tt>transactions.currency</tt> set to <tt>"GBP"</tt> cannot attenuate
        a base <tt>transactions.currency</tt> value of <tt>"USD"</tt>. If the
        overlay omits <tt>transactions.currency</tt>, the base currency is
        inherited unchanged.
      </dd>

      <dt>Null and unknown types</dt>
      <dd>
        <tt>null</tt> and any value type not covered above MUST NOT be used to
        relax or erase a base constraint. A non-equal overlay value of an
        unknown type MUST be rejected with <tt>overlay_exceeds_manifest</tt>.
      </dd>
    </dl>
  </section>

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

    <t><strong>Engagement Object Fields:</strong></t>

    <dl newline="true" spacing="normal">
      <dt><tt>engagement_id</tt> (string; REQUIRED)</dt>


      <dd>Pattern: eng:&lt;UUIDv4-lowerhex&gt;</dd>


      <dt><tt>title</tt> (string; REQUIRED)</dt>


      <dd>maxLength: 256</dd>


      <dt><tt>hiring_operator</tt> (string; REQUIRED)</dt>


      <dd>DID; MUST be did:web or did:aip</dd>


      <dt><tt>deploying_principal</tt> (string; REQUIRED)</dt>


      <dd>DID of the deploying principal</dd>


      <dt><tt>created_at</tt> (string; REQUIRED)</dt>


      <dd>ISO 8601 UTC</dd>


      <dt><tt>expires_at</tt> (string; REQUIRED)</dt>


      <dd>ISO 8601 UTC; MUST be after created_at</dd>


      <dt><tt>status</tt> (string; REQUIRED)</dt>


      <dd>One of: "proposed", "active", "suspended", "completed", "terminated"</dd>


      <dt><tt>participants</tt> (array; REQUIRED)</dt>


      <dd>Array of Participant objects</dd>


      <dt><tt>approval_gates</tt> (array; OPTIONAL)</dt>


      <dd>Array of Approval Gate objects</dd>


      <dt><tt>change_log</tt> (array; REQUIRED)</dt>


      <dd>Append-only array of Change Log Entry objects</dd>


      <dt><tt>hiring_operator_signature</tt> (string; REQUIRED)</dt>


      <dd>Base64url EdDSA signature by hiring_operator over the top-level engagement signing input defined below</dd>

      <dt><tt>hiring_operator_kid</tt> (string; REQUIRED)</dt>


      <dd>DID URL identifying the Ed25519 verification method for <tt>hiring_operator_signature</tt></dd>


      <dt><tt>deploying_principal_signature</tt> (string; COND.)</dt>


      <dd>Base64url EdDSA countersignature by deploying_principal over the same top-level engagement signing input defined below; REQUIRED for active, suspended, completed, and terminated Engagement Objects</dd>

      <dt><tt>deploying_principal_kid</tt> (string; COND.)</dt>


      <dd>DID URL identifying the Ed25519 verification method for <tt>deploying_principal_signature</tt>; REQUIRED whenever <tt>deploying_principal_signature</tt> is present</dd>


      <dt><tt>version</tt> (integer; REQUIRED)</dt>


      <dd>Positive integer; MUST equal max(change_log.seq)</dd>


    </dl>

    <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:&lt;lowercase-token&gt;), name (REQUIRED, maxLength: 128),
      required_approver (REQUIRED, DID), trigger (REQUIRED,
      <tt>scope:&lt;scope-string&gt;</tt> or
      <tt>action_type:&lt;action-type&gt;</tt>),
      status (REQUIRED, one of: "pending", "approved", "rejected"),
      approved_at (OPTIONAL, ISO 8601).
    </t>

    <t>
      <strong>Top-level engagement signature input:</strong> Both
      hiring_operator_signature and, when present,
      deploying_principal_signature MUST
      be computed over the same JCS-canonical JSON serialization of the
      immutable engagement agreement object containing only
      <tt>engagement_id</tt>, <tt>title</tt>, <tt>hiring_operator</tt>,
      <tt>deploying_principal</tt>, <tt>created_at</tt>, and
      <tt>expires_at</tt>. The top-level signing input MUST NOT include
      mutable lifecycle or roster state (<tt>status</tt>,
      <tt>participants</tt>, <tt>approval_gates</tt>, <tt>change_log</tt>,
      or <tt>version</tt>) and MUST NOT include top-level signature or
      <tt>*_kid</tt> fields. Mutable state is authenticated by the signed
      append-only <tt>change_log</tt> entries. The countersignature does not
      sign the other party's signature value.
    </t>

    <t>
      <strong>Change Log Entry fields:</strong> seq (REQUIRED,
      monotonically increasing from 1), timestamp (REQUIRED, ISO 8601),
      action (REQUIRED), actor (REQUIRED, DID), actor_kid (REQUIRED DID URL),
      payload (OPTIONAL, object), signature (REQUIRED, Base64url EdDSA by
      actor over the Section 2.1 signing input with the entry
      <tt>signature</tt> excluded).
    </t>

    <t>
      <strong>Engagement signature verification profile:</strong> Each
      top-level signature and each change-log entry signature MUST identify
      its verification key with the corresponding <tt>*_kid</tt> field. The
      <tt>kid</tt> value MUST be a DID URL whose DID portion is byte-for-byte
      equal to the signer DID: <tt>hiring_operator</tt> for
      <tt>hiring_operator_signature</tt>, <tt>deploying_principal</tt> for
      <tt>deploying_principal_signature</tt>, and the change-log entry
      <tt>actor</tt> for an entry <tt>signature</tt>. For <tt>did:aip</tt>
      signer DIDs, the Registry MUST resolve the verification method through
      its registered public-key endpoint for that AID. For other DID methods,
      the Registry MUST use the signer's DID method resolution rules. The
      resolved verification method MUST be Ed25519 verification material
      controlled by the signer DID. Missing <tt>kid</tt> fields, malformed DID
      URLs, signer/key DID mismatches, unsupported key types, failed DID
      resolution, or failed EdDSA signature verification MUST cause rejection
      with <tt>engagement_signature_invalid</tt>.
    </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 version semantics:</strong> The <tt>version</tt>
      field is the current change-log sequence number, not an independent
      counter. On creation, the Registry MUST create exactly one
      <tt>engagement_created</tt> entry with <tt>seq: 1</tt> and set
      <tt>version: 1</tt>. Each accepted mutation after creation MUST append
      exactly one new change-log entry whose <tt>seq</tt> is
      <tt>previous version + 1</tt>, and the Registry MUST set
      <tt>version</tt> to that new <tt>seq</tt> in the same atomic update.
      Therefore, for every stored Engagement Object, <tt>version</tt> MUST
      equal <tt>max(change_log[*].seq)</tt>. A mutation that changes status,
      participants, approval gates, countersignature state, or termination
      state without appending a matching change-log entry MUST be rejected.
    </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. Missing
        deploying_principal_signature during activation MUST be rejected
        with engagement_countersign_required.</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 using the Approval Envelope
        cancellation rules in Section 13.6.</li>
      <li>Subsequent Credential Token validation for operations scoped to
        this engagement MUST fail with engagement_terminated.</li>
    </ol>

    <t>
      This termination cascade applies only when an Engagement transitions
      to <tt>terminated</tt>. Transition to <tt>completed</tt> does not
      trigger any Capability Overlay invalidation or Approval Envelope
      cancellation cascade.
    </t>
  </section>
</section>
    <section anchor="registration-protocol" xml:base="sections/06-registration.xml">
  <name>Registration Protocol</name>

  <section anchor="registration-envelope-submission">
    <name>Envelope Submission</name>
    <t>
      The Registration Envelope request body is defined only in Section 5.6.
      An agent is registered by submitting that object to
      <tt>POST /v1/agents</tt>. This section defines Registry processing and
      validation; it does not define a second Registration Envelope schema.
    </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>
    <t>
      If any required condition in Checks 1 through 15, including their
      lettered subchecks, fails and that check does not explicitly name a more
      specific error code, the Registry MUST reject the Registration Envelope
      with <tt>registration_invalid</tt>. Because the checks are ordered, the
      Registry SHOULD report the error code associated with the first failed
      check.
    </t>
    <ol>
      <li>Check 1. <tt>identity</tt> MUST be present and MUST be valid JSON conforming to the Agent Identity schema.</li>
      <li>Check 2. <tt>identity.aid</tt> MUST match the <tt>did:aip</tt> ABNF grammar defined in Section 4.1.</li>
      <li>Check 3. <tt>identity.type</tt> MUST equal the namespace component of <tt>identity.aid</tt>. The namespace MUST have an active, non-reserved entry in the Registry's synced AIP Namespace Catalog, unless the Registry explicitly documents a private local namespace policy. A mismatch, unknown namespace, removed namespace, or reserved namespace MUST be rejected with <tt>registration_invalid</tt>.</li>
      <li>Check 4. <tt>identity.aid</tt> MUST NOT already exist in the Registry,
      and <tt>identity.public_key</tt> MUST NOT already be associated with a
      registered, non-revoked AID in the Registry. If either condition fails,
      the Registry MUST reject with <tt>aid_already_registered</tt>.</li>
      <li>Check 5. <tt>identity.public_key</tt> MUST be an Ed25519 JWK with <tt>kty="OKP"</tt>, <tt>crv="Ed25519"</tt>, and a 43-character base64url <tt>x</tt> value.</li>
      <li>Check 6. <tt>capability_manifest</tt> MUST be present and MUST be valid JSON conforming to the Capability Manifest schema. It MUST include <tt>manifest_id</tt>, <tt>aid</tt>, <tt>granted_by</tt>, <tt>version</tt>, <tt>issued_at</tt>, <tt>expires_at</tt>, <tt>capabilities</tt>, and <tt>signature</tt>. The <tt>version</tt> field MUST equal 1 for initial registration; <tt>manifest_id</tt> MUST match the <tt>cm:</tt> UUID pattern; <tt>issued_at</tt> and <tt>expires_at</tt> MUST be valid date-time values; <tt>capabilities</tt> MUST be an object; and <tt>signature</tt> MUST be a non-empty base64url value. The <tt>capability_manifest.expires_at</tt> value MUST be in the future at the time of registration.</li>
      <li>Check 7. <tt>capability_manifest.aid</tt> MUST equal <tt>identity.aid</tt>.</li>
      <li>Check 8. The <tt>principal_token</tt> string MUST be decodable as a compact-serialised JWT, MUST conform to the Principal Token schema, and MUST carry a JOSE header with <tt>typ</tt> equal to <tt>"JWT"</tt>, <tt>alg</tt> equal to <tt>"EdDSA"</tt>, and <tt>kid</tt> identifying an Ed25519 verification method controlled by the decoded <tt>iss</tt> DID or AID. For a <tt>did:aip</tt> issuer, the Registry MUST resolve the key through its public-key endpoint for that registered AID. The Registry MUST verify the Principal Token signature using the resolved verification method. If any Check 8 condition fails, the Registry MUST reject with <tt>registration_invalid</tt>.</li>
      <li>Check 9. The decoded <tt>principal_token</tt> payload <tt>sub</tt> MUST equal <tt>identity.aid</tt>. If <tt>delegation_depth</tt> is <tt>0</tt>, <tt>delegated_by</tt> MUST be null and <tt>iss</tt> MUST equal <tt>principal.id</tt>. If <tt>delegation_depth</tt> is greater than <tt>0</tt>, <tt>delegated_by</tt> MUST be a registered, non-revoked parent AID, <tt>iss</tt> MUST equal <tt>delegated_by</tt>, and the Registry MUST reconstruct the complete candidate chain by appending the submitted Principal Token to the parent's stored registration chain. The reconstructed chain MUST satisfy the delegation-chain rules in <xref target="validation-step-8"/>, including linkage, consistent root principal, scope inheritance, task binding, and maximum delegation depth. The submitted <tt>capability_manifest</tt> scopes MUST be a subset of both the submitted Principal Token <tt>scope</tt> array and the parent's effective Capability Manifest. If any Check 9 condition fails, the Registry MUST reject with <tt>registration_invalid</tt> or <tt>invalid_delegation_depth</tt> when the depth limit is exceeded.</li>
      <li>Check 9a. <tt>max_delegation_depth</tt> Bounds: If the root
      Principal Token for the reconstructed registration chain contains a
      <tt>max_delegation_depth</tt> claim, the Registry MUST verify that its
      value is an integer in the range 0 through 10 inclusive. If
      <tt>max_delegation_depth</tt> is absent from the root Principal Token,
      the Registry MUST treat it as
      3 for all subsequent registration processing. If the value exceeds 10
      or is a negative integer, the Registry MUST reject the registration with
      <tt>registration_invalid</tt> and MUST include the detail string
      <tt>max_delegation_depth exceeds the hard cap of 10</tt> in the error
      response.</li>
      <li>Check 10. The decoded <tt>principal_token</tt> payload <tt>principal.id</tt> MUST NOT begin with <tt>did:aip:</tt>.</li>
      <li>Check 11. If the synced AIP Namespace Catalog entry for <tt>identity.type</tt> has <tt>requires_task_id: true</tt>, the decoded <tt>principal_token</tt> payload <tt>task_id</tt> MUST be non-null and non-empty.</li>
      <li>Check 12. <tt>capability_manifest.signature</tt> MUST be verifiable against the <tt>granted_by</tt> DID's public key.</li>
      <li>Check 13. <tt>identity.version</tt> MUST equal 1 and
      <tt>identity.previous_key_signature</tt> MUST be absent. Registration
      Envelopes are initial-registration objects only; key rotation is
      processed through <tt>PUT /v1/agents/{aid}</tt>.</li>
      <li>Check 14a. <tt>grant_tier</tt> MUST be present and MUST be one of <tt>"G1"</tt>, <tt>"G2"</tt>, or <tt>"G3"</tt>. If <tt>grant_tier</tt> is absent or has any other value, the Registry MUST reject with <tt>registration_invalid</tt>.</li>
      <li>Check 14b. The Registry MUST determine the requested security Tier as the highest AIP Scope Catalog <tt>tier</tt> among all active scopes in <tt>capability_manifest.capabilities</tt>.</li>
      <li>Check 14c. The submitted <tt>grant_tier</tt> MUST satisfy the requested security Tier determined in Check 14b. Tier 1 permits <tt>"G1"</tt>, <tt>"G2"</tt>, or <tt>"G3"</tt>. Tier 2 permits <tt>"G2"</tt> or <tt>"G3"</tt> unless the Registry's <tt>identity_proofing_required_for_tier2</tt> metadata field is <tt>true</tt>, in which case Tier 2 permits only <tt>"G3"</tt>. Tier 3 permits only <tt>"G3"</tt>. If <tt>grant_tier</tt> is inconsistent with the requested security Tier or the Registry's Tier 2 identity-proofing policy, the Registry MUST reject with <tt>registration_invalid</tt>.</li>
      <li>Check 14d. If the requested security Tier determined in Check 14b is 2 or 3, the decoded <tt>principal_token</tt> payload <tt>principal.id</tt> MUST use the <tt>did:web</tt> method. Otherwise, the Registry MUST reject with <tt>principal_did_method_forbidden</tt>.</li>
      <li>Check 14e. If <tt>grant_tier</tt> is <tt>"G3"</tt>, the decoded <tt>principal_token</tt> payload MUST include a non-empty <tt>acr</tt> string and a non-empty <tt>amr</tt> array. If either claim is absent or empty, the Registry MUST reject with <tt>identity_proofing_insufficient</tt>.</li>
      <li>Check 15. If the requested security Tier determined in Check 14b is 2 and <tt>identity.model.attestation_hash</tt> is absent, the Registry SHOULD accept the Registration Envelope, but only if it creates a structured registration warning with <tt>code</tt> equal to <tt>"model_attestation_missing_tier2"</tt>, <tt>severity</tt> equal to <tt>"warning"</tt>, <tt>source_check</tt> equal to <tt>"registration_check_15"</tt>, and <tt>issued_at</tt> set to the Registry's current time. The warning message MUST state that the Tier 2 agent is not cryptographically pinned to a model artifact. The Registry MUST persist this warning in Agent Registration Metadata, MUST include it in the successful <tt>POST /v1/agents</tt> response, and MUST return it in subsequent <tt>GET /v1/agents/{aid}</tt> metadata responses while the registered Agent Identity Object lacks <tt>model.attestation_hash</tt>. The persisted warning is the required audit artifact for this condition. If the requested security Tier is 3, <tt>identity.model.attestation_hash</tt> MUST be present and MUST match the <tt>sha256:&lt;64 lowercase hex characters&gt;</tt> pattern. If the requested security Tier is 3 and the field is absent or malformed, the Registry MUST reject with <tt>registration_invalid</tt>.</li>
    </ol>
    <t>On acceptance, the Registry MUST store the reconstructed registration
    chain for the registered AID. For a direct registration, that chain
    contains the submitted root Principal Token. For a sub-agent registration,
    it contains the parent's stored registration chain followed by the
    submitted delegated Principal Token. The Registry MUST record the
    <tt>delegated_by</tt> field from the decoded <tt>principal_token</tt>
    payload (or the principal's DID if <tt>delegated_by</tt> is null) in the
    parent-child delegation index, for use in revocation propagation
    (<xref target="revocation-checking"/>).</t>
    <t>If the synced AIP Namespace Catalog entry for <tt>identity.type</tt> defines an automatic lifecycle expiry rule, the Registry MUST persist the resulting <tt>lifecycle_expires_at</tt> value in its stored registration record before accepting the Registration Envelope. For the Draft-02 <tt>ephemeral</tt> namespace, <tt>lifecycle_expires_at</tt> MUST be no later than the earlier of the decoded root Principal Token <tt>expires_at</tt> value and <tt>capability_manifest.expires_at</tt>.</t>
  </section>

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

  <!-- 7.1. DID Resolution -->
  <section anchor="did-resolution">
    <name>DID Resolution</name>
    
    <t>
      Every registered AID MUST have a corresponding W3C DID Document
      resolvable by a DID resolver that implements the <tt>did:aip</tt>
      method defined in this section. The DID Document is derived
      deterministically from the Agent Identity, active Capability Manifest,
      and lifecycle state stored in the authoritative 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.
      Without one of these DID-specific <tt>Accept</tt> values, the endpoint
      returns the default Agent Registration Metadata response defined in
      <xref target="agent-registration-metadata-registry"/>.
    </t>
    <t>
      AIP implementations MUST support DID resolution for at minimum
      <tt>did:aip</tt>, <tt>did:key</tt>, and <tt>did:web</tt> DID methods.
      Resolved DID Documents MAY be cached for a maximum of 300 seconds.
    </t>
    <t>
      DID resolution performed during token validation MUST use an explicit
      timeout. For any validation step that is REQUIRED because the token
      has security Tier 2 or Tier 3 or because the token contains
      <tt>aip_registry</tt>, each remote DID resolution attempt MUST time out
      no later than 2 seconds after it is started, and the aggregate wall-clock
      budget for DID resolution across a single token validation attempt MUST
      NOT exceed 5 seconds. Local
      non-network resolution, such as <tt>did:key</tt>, SHOULD complete without
      consuming this remote-resolution budget. Implementations MAY use shorter
      timeouts, but MUST NOT use a zero-duration timeout for a required remote
      DID resolution attempt.
    </t>
    <t>
      If DID resolution fails or times out during a REQUIRED validation step,
      implementations MUST treat the result as <tt>registry_unavailable</tt>
      and MUST reject the operation. If a Tier 1-only operation performs
      Registry Trust Anchoring as a RECOMMENDED check and DID resolution times
      out, the Relying Party MAY either reject with
      <tt>registry_unavailable</tt> or skip that RECOMMENDED check according to
      local policy.
    </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>

  <section anchor="did-aip-method-resolution" numbered="true">
    <name><tt>did:aip</tt> Method Resolution</name>

    <t>
      A <tt>did:aip</tt> resolver MUST NOT infer the authoritative Registry
      from the namespace or agent-id components of the DID. The resolver MUST
      be invoked with an authoritative Registry base URI, or MUST obtain one
      from protocol context: the <tt>aip_registry</tt> claim in a Credential
      Token, the <tt>iss</tt> or <tt>aip_registry</tt> value in a Step
      Execution Token, the root principal DID Document's <tt>AIPRegistry</tt>
      service entry, or a local trust policy that binds the AID namespace to a
      Registry. If no authoritative Registry can be determined, resolution
      fails and protocol validation that requires the result MUST reject with
      <tt>registry_unavailable</tt>.
    </t>

    <t>
      The resolver's input DID MUST match the <tt>did:aip</tt> syntax in
      <xref target="aid-syntax"/>. A resolver receiving a malformed AID
      MUST return a DID Core resolution result with
      <tt>didResolutionMetadata.error</tt> set to <tt>invalidDid</tt> and
      MUST NOT contact a Registry.
    </t>

    <t>
      To resolve a valid AID, the resolver MUST perform the following steps:
    </t>
    <ol>
      <li>Resolve and, when required by the validation context, pin the
      authoritative Registry trust state using the Registry Genesis procedure
      in <xref target="registry-genesis"/>.</li>
      <li>Percent-encode the complete AID for use as the
      <tt>{aid}</tt> path parameter according to
      <xref target="aid-encoding"/>.</li>
      <li>Send <tt>GET /v1/agents/{aid}</tt> to the authoritative Registry
      with <tt>Accept: application/did+json</tt> or
      <tt>Accept: application/did+ld+json</tt>.</li>
      <li>If the Registry returns HTTP 404, return a DID resolution result
      with <tt>didResolutionMetadata.error</tt> set to <tt>notFound</tt>.</li>
      <li>If the Registry cannot be reached or times out under the limits in
      <xref target="did-resolution"/>, return a DID resolution result with
      <tt>didResolutionMetadata.error</tt> set to
      <tt>registry_unavailable</tt>.</li>
      <li>If the requested representation is unsupported, return a DID
      resolution result with <tt>didResolutionMetadata.error</tt> set to
      <tt>representationNotSupported</tt>.</li>
      <li>On success, return a DID Core resolution result whose
      <tt>didDocument.id</tt> equals the input AID, whose
      <tt>didResolutionMetadata.contentType</tt> matches the returned
      representation, and whose <tt>didDocumentMetadata</tt> contains
      <tt>versionId</tt>, <tt>updated</tt>, <tt>deactivated</tt>, and
      <tt>aip_registry</tt>.</li>
    </ol>

    <t>
      A Registry constructs the <tt>did:aip</tt> DID Document from its stored
      registration record as follows. The DID Document <tt>id</tt> is the AID.
      The <tt>verificationMethod</tt> array contains the current active
      Ed25519 public key from the current Agent Identity Object. The
      verification method <tt>id</tt> MUST equal
      <tt>&lt;aid&gt;#key-&lt;identity.version&gt;</tt>, its
      <tt>controller</tt> MUST equal the AID, and its <tt>publicKeyJwk</tt>
      MUST contain only public JWK members. The <tt>authentication</tt> array
      MUST contain that verification method identifier. The DID Document
      <tt>controller</tt> value MUST equal the active Capability Manifest
      <tt>granted_by</tt> value. The <tt>didDocumentMetadata.versionId</tt>
      value MUST equal the decimal string form of <tt>identity.version</tt>.
    </t>

    <t>
      DID URL dereferencing for <tt>did:aip</tt> supports only the empty
      fragment and key fragments of the form
      <tt>#key-&lt;positive-integer&gt;</tt>. The empty fragment dereferences to
      the DID Document. A key fragment dereferences to the corresponding
      verification method if the Registry retains the requested historical key
      version at <tt>GET /v1/agents/{aid}/public-key/{key-id}</tt>; otherwise
      dereferencing returns <tt>didResolutionMetadata.error</tt> set to
      <tt>notFound</tt>. Other fragments MUST return <tt>notFound</tt>.
    </t>

    <t>
      The CRUD operations for the <tt>did:aip</tt> method are:
    </t>
    <table>
      <thead>
        <tr><th>Operation</th><th>Mechanism</th></tr>
      </thead>
      <tbody>
        <tr><td>Create</td><td><tt>POST /v1/agents</tt> with a Registration Envelope that passes Section 6 validation</td></tr>
        <tr><td>Read</td><td><tt>GET /v1/agents/{aid}</tt>; default metadata response, or DID Document with DID Accept header</td></tr>
        <tr><td>Update</td><td><tt>PUT /v1/agents/{aid}</tt> for key rotation only; arbitrary DID Document mutation is not supported</td></tr>
        <tr><td>Deactivate</td><td><tt>POST /v1/revocations</tt> with a valid <tt>full_revoke</tt> Revocation Object</td></tr>
      </tbody>
    </table>

    <t>
      Deactivation is permanent for the affected AID. After full revocation,
      the Registry MUST return <tt>didDocumentMetadata.deactivated</tt> as
      <tt>true</tt>. The Registry MAY still return the last DID Document and
      MUST continue to serve retained historical public keys for audit and
      signature verification, but Relying Parties MUST reject new protocol
      operations for a deactivated AID during revocation and lifecycle
      validation.
    </t>
  </section>

  <!-- 7.2. DID Document Structure -->
  <section anchor="did-document-structure">
    <name>DID Document Structure</name>
    
    <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/did-document.json</em>. Method operations and
      DID URL dereferencing rules are defined in
      <xref target="did-aip-method-resolution"/>.
    </t>
    <t>
      A principal that authorises agents for Tier 2 or Tier 3 operations MUST
      use the <tt>did:web</tt> DID method, and that principal's DID Document
      MUST include an <tt>AIPRegistry</tt> service entry in its
      <tt>service</tt> array. Principals MUST NOT use the <tt>did:aip</tt>
      method anywhere in the protocol.
    </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).
      <tt>did:key</tt> principals MUST NOT authorise Tier 2 or Tier 3
      operations.
    </t>

    <t>
      For principals using any W3C DID method other than <tt>did:web</tt> or
      <tt>did:aip</tt>, authorisation is limited to Tier 1. Even if such a DID
      method supports service entries, this specification does not permit it
      for Tier 2 or Tier 3 principal authorisation. For <tt>did:web</tt>
      principals, the <tt>AIPRegistry</tt> service entry is REQUIRED when the
      principal authorises any agent with Tier 2 or Tier 3 scopes. It is
      OPTIONAL for principals authorising only Tier 1 agents.
    </t>

  </section>

  <!-- 7.3. Registry Genesis -->
  <section anchor="registry-genesis">
    <name>Registry Genesis</name>
    
    <t>
      Registry Genesis is the one-time initialisation procedure by which a new AIP Registry establishes its
      stable service identifier, generates its initial trust keys, and publishes discovery and trust metadata.
      It MUST be performed before the Registry serves any requests.
    </t>

    <!-- 7.3.1 Key Generation -->
    <section anchor="registry-genesis-key-generation">
      <name>Key Generation</name>
      
      <t>
        The Registry MUST generate a fresh Ed25519 trust keypair at first
        boot. The private key MUST be stored encrypted at rest (AES-256-GCM
        or an equivalent at-rest key-protection control satisfying
        <xref target="key-management"/>). The corresponding public key is
        published in the initial Registry Trust Record. Separate active
        verification keys for CRL documents (<xref target="crl"/>) and Step Execution
        Tokens are RECOMMENDED so that routine operational-key rotation does
        not require trust-root rotation.
      </t>
    </section>

    <!-- 7.3.2 Registry ID Establishment -->
    <section anchor="registry-genesis-aid-construction">
      <name>Registry ID Establishment</name>
      
      <t>
        The Registry MUST establish a stable <tt>registry_id</tt> for the
        Registry service. The <tt>registry_id</tt> MUST be an HTTPS URI
        under the Registry operator's control. The RECOMMENDED value is the
        origin that serves <tt>/v1/registry-metadata</tt>.
      </t>

      <artwork type="text">
  https://registry.example.com
      </artwork>

      <t>
        The <tt>registry_id</tt> identifies the Registry service, not a
        single signing key. It MUST remain stable across planned key
        rotation. At most one active <tt>registry_id</tt> MUST exist per
        Registry deployment at any time.
      </t>
    </section>

    <!-- 7.3.3 Self-Registration Exemption -->
    <section anchor="registry-genesis-self-registration-exemption">
      <name>Self-Registration Exemption</name>
      
      <t>
        The Registry service identifier MUST NOT be created via
        <tt>POST /v1/agents</tt>. Instead, the Registry MUST persist its
        <tt>registry_id</tt> and trust keys directly to its own data store
        during genesis. The following Registration Envelope checks
        (Section 6.2) are explicitly inapplicable to the Registry service:
      </t>
      <ul>
        <li>
          <strong>Check 8</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 <tt>registry_id</tt> already exists in the data
          store the existing record MUST be used and genesis MUST NOT
          overwrite it.
        </li>
      </ul>
    </section>

    <!-- 7.3.4 Registry Metadata Publication -->
    <section anchor="registry-genesis-well-known-publication">
      <name>Registry Metadata Publication</name>
      
      <t>
        The Registry MUST publish Registry Metadata at the following endpoint
        relative to its <tt>registry_id</tt> origin:
      </t>
      <artwork type="text">
GET /v1/registry-metadata
      </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_id</tt></td><td>string</td><td>REQUIRED</td><td>Stable HTTPS identifier for the Registry service</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 AIP protocol compatibility version this Registry conforms to; not the Internet-Draft revision</td></tr>
          <tr><td><tt>registry_trust_uri</tt></td><td>string</td><td>REQUIRED</td><td>URI of the current Registry Trust Record</td></tr>
          <tr><td><tt>endpoints</tt></td><td>object</td><td>REQUIRED</td><td>Map of service names to relative paths or absolute HTTPS URIs</td></tr>
          <tr><td><tt>enterprise_idp_federation_supported</tt></td><td>boolean</td><td>OPTIONAL</td><td>True only when the Registry supports the Enterprise IdP Federation Profile in <xref target="enterprise-idp-federation-profile"/>. SHOULD be present in all Registry Metadata documents. Absence is treated as equivalent to false. Gateways that surface this capability through <tt>/.well-known/oauth-protected-resource</tt> SHOULD ensure the value is consistent with the Registry's declared support in this document. A mismatch between the two documents MUST be treated as a Registry configuration error.</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>Preferred CRL retrieval URI. This value MAY be the origin path <tt>/v1/crl</tt> or an absolute HTTPS CDN/distribution URI.</td></tr>
          <tr><td><tt>revocations</tt></td><td>Revocation submission path (e.g., <tt>/v1/revocations</tt>)</td></tr>
        </tbody>
      </table>

      <t>
        First-contact bootstrapping: On first contact with a Registry, Relying Parties MUST:
      </t>
      <ul>
        <li>Fetch <tt>/v1/registry-metadata</tt> over HTTPS.</li>
        <li>Verify that the response <tt>registry_id</tt> matches the Registry URI established via the root principal DID Document when such a DID binding is available. Unless otherwise specified, this comparison MUST use origin comparison per <xref target="RFC6454"/> (scheme + host + port).</li>
        <li>Fetch the Registry Trust Record from <tt>registry_trust_uri</tt>.</li>
        <li>Pin the <tt>registry_id</tt>, the trusted Registry Trust Record version, and the trust keys from that record locally.</li>
        <li>Use the accepted Registry Trust Record's signed <tt>endpoints</tt> object as the authoritative endpoint map for Registry interactions. The Registry Metadata <tt>endpoints</tt> object is a bootstrap hint and MUST NOT override a valid signed Registry Trust Record.</li>
        <li>On subsequent contacts, use the versioned trust-update procedure in
        <xref target="registry-genesis-registry-key-rotation-planned"/> before
        updating the local trust state.</li>
      </ul>

      <t>
        Endpoint values that begin with <tt>/</tt> are resolved relative to
        <tt>registry_id</tt>. Absolute endpoint values MUST use HTTPS. The
        <tt>endpoints.crl</tt> value MAY use a different HTTPS origin from
        <tt>registry_id</tt> so that CRL documents can be served from a CDN
        or distributed object store. A different CRL origin does not identify
        a different Registry; verifiers bind CRL documents to the Registry by
        verifying the CRL signature against the accepted Registry Trust Record
        version current at CRL issuance time.
      </t>

      <t>
        The <tt>registry_id</tt> identifies the Registry instance and MUST remain stable across planned Registry key rotation.
        Planned key rotation updates the Registry Trust Record and the
        Registry's active verification keys while preserving the same
        <tt>registry_id</tt>. Emergency compromise recovery is handled
        separately under
        <xref target="registry-genesis-registry-key-rotation-emergency"/>.
      </t>

      <t>
        The Registry Metadata document is discovery metadata. The canonical
        trust state for a Registry is defined by its Registry Trust Record,
        not by the Registry Metadata document itself.
      </t>
    </section>

    <section anchor="registry-genesis-trust-record" numbered="true">
      <name>Registry Trust Record</name>
      <t>
        A Registry Trust Record is the canonical versioned trust metadata
        object for a Registry. It MUST be published at:
      </t>
      <table>
        <thead>
          <tr><th>Method</th><th>Path</th><th>Description</th></tr>
        </thead>
        <tbody>
          <tr><td>GET</td><td>/v1/registry-trust/current</td><td>Current Registry Trust Record</td></tr>
          <tr><td>GET</td><td>/v1/registry-trust/{version}</td><td>Immutable Registry Trust Record for the specified version</td></tr>
        </tbody>
      </table>

      <t>
        The Registry Trust Record MUST use a detached-signature structure
        consisting of a <tt>signed</tt> object and a <tt>signatures</tt>
        array. The <tt>signed</tt> object MUST include at minimum:
      </t>
      <ul>
        <li><tt>registry_id</tt></li>
        <li><tt>version</tt></li>
        <li><tt>issued_at</tt></li>
        <li><tt>expires_at</tt></li>
        <li><tt>discovery_uri</tt></li>
        <li><tt>endpoints</tt></li>
        <li><tt>trust_signature_threshold</tt></li>
        <li><tt>trusted_keys</tt></li>
        <li><tt>active_verification_keys</tt></li>
      </ul>

      <t>
        The canonical signing input is the RFC 8785 JCS serialisation of the
        <tt>signed</tt> object only. Signature verification MUST ignore the
        <tt>signatures</tt> array except as the container for detached
        signatures over that <tt>signed</tt> object.
      </t>

      <t>
        Verifiers use the latest trusted Registry Trust Record for current
        Registry interactions. For historical verification of Step
        Execution Tokens, CRLs, or signed notifications, verifiers MUST use
        the Registry Trust Record version that was current at artifact
        issuance time. The <tt>trusted_keys</tt> set governs trust-record
        acceptance, while <tt>active_verification_keys</tt> governs which
        runtime keys are valid for Registry-signed artifacts issued under
        that trust-record version.
      </t>

      <t>
        Each entry in <tt>active_verification_keys.step_execution</tt>,
        <tt>active_verification_keys.crl</tt>, and
        <tt>active_verification_keys.notifications</tt> MUST be an Ed25519
        public JWK containing <tt>kty</tt>, <tt>crv</tt>, <tt>x</tt>, and
        <tt>keyid</tt>. A verifier MUST NOT treat a bare key identifier as
        sufficient for runtime artifact verification; the accepted Registry
        Trust Record version must provide the public key material used to
        verify the artifact signature.
      </t>

      <t>
        For CRL retrieval, verifiers MUST use <tt>signed.endpoints.crl</tt>
        from the accepted Registry Trust Record, resolving it according to
        the endpoint rules above. The unsigned Registry Metadata
        <tt>endpoints.crl</tt> value MAY be used only to locate bootstrap
        metadata before the Registry Trust Record is accepted.
      </t>

      <t>
        Historical Registry Trust Records MUST remain retrievable for at
        least the maximum lifetime of Step Execution Tokens and CRL
        artifacts plus a 30-day audit margin.
      </t>
    </section>

    <!-- 7.3.5 Single-Instance Constraint -->
    <section anchor="registry-genesis-single-instance-constraint">
      <name>Single-Instance Constraint</name>
      
      <t>
        Each Registry deployment MUST have exactly one active Registry ID. Provisioning a second Registry ID within the same deployment
        MUST be treated as a fatal configuration error. Horizontal scaling and high-availability deployments MUST share a single Registry ID
        and trust state.
      </t>
    </section>

    <!-- 7.3.6 Registry Key Rotation -->
    <section anchor="registry-genesis-registry-key-rotation">
      <name>Registry Key Rotation</name>
      
      <t>
        AIP defines two Registry key rotation paths:
      </t>
      <ul>
        <li><strong>Planned rotation:</strong> continuity-preserving rotation in which the Registry keeps the same <tt>registry_id</tt> and publishes a new Registry Trust Record version.</li>
        <li><strong>Emergency re-bootstrap:</strong> continuity-breaking recovery for suspected key compromise or loss of the retiring private key.</li>
      </ul>

      <t>
        Validators bootstrapping trust in a Registry MUST fetch and pin the current Registry Trust Record before processing any
        Step Execution Tokens or CRL documents signed by that Registry. Cached trust state MUST NOT be used beyond the
        trust record's <tt>expires_at</tt> without refresh.
      </t>
      <t>
        A Step Execution Token is not a trust-bootstrap artifact. Relying
        Parties MUST NOT establish first-contact Registry trust from a SET
        received in an application request. A SET whose <tt>iss</tt> Registry
        ID is unknown, unpinned, expired, or missing the locally retained
        Registry Trust Record version named by the SET protected header
        <tt>aip_trv</tt> MUST be rejected with
        <tt>registry_untrusted</tt>. An implementation MAY perform
        first-contact bootstrap or sequential trust update as a separate
        pre-validation operation, but it MUST complete and pin the accepted
        trust state before reprocessing the SET.
      </t>

      <section anchor="registry-genesis-registry-key-rotation-planned">
        <name>Planned Rotation</name>
        <t>
          During planned rotation, the Registry MUST publish a new immutable
          Registry Trust Record whose <tt>signed.version</tt> is exactly one
          greater than the previously trusted version and whose
          <tt>signed.registry_id</tt> matches the existing pinned
          <tt>registry_id</tt>.
        </t>

        <t>
          The <tt>signed.trust_signature_threshold</tt> value defines the
          minimum number of signatures from <tt>signed.trusted_keys</tt>
          required for ordinary acceptance of a Registry Trust Record.
          Implementations MAY require a higher threshold than 1 for
          high-assurance deployments, but they MUST still preserve the
          overlapping-signature rule below during planned rotation.
        </t>

        <t>
          Planned rotation MUST use overlapping trust signatures. A new
          Registry Trust Record version MUST satisfy the threshold rules in
          both the currently pinned and the candidate new trust record. At
          a minimum, a planned rotation update MUST be signed by:
        </t>
        <ol>
          <li>at least one key trusted in the currently pinned Registry Trust Record; and</li>
          <li>at least one key trusted in the new Registry Trust Record.</li>
        </ol>

        <t>
          If the Registry Metadata document's <tt>registry_trust_uri</tt> does not
          share the same origin as <tt>registry_id</tt>, the Relying Party
          MUST treat the Registry as untrusted unless an explicit trust
          policy permits that alternate trust-record origin.
        </t>

        <t>
          A Relying Party that has pinned trust version <tt>N</tt> MUST
          update sequentially. It MUST fetch and validate version
          <tt>N+1</tt> before accepting version <tt>N+2</tt> or later.
        </t>
        <t>
          This sequential trust-update model is intentionally similar to
          repository root update patterns in The Update Framework
          <xref target="TUF"/>,
          where clients advance trust state one version at a time to resist
          rollback and mix-and-match attacks.
        </t>
        <ol>
          <li>The fetched record's <tt>signed.registry_id</tt> MUST match the pinned value.</li>
          <li>The fetched record's <tt>signed.version</tt> MUST equal the pinned version plus exactly 1.</li>
          <li>The fetched record's signatures MUST satisfy the overlapping-signature rule above.</li>
          <li>The fetched record's <tt>expires_at</tt> MUST be in the future.</li>
          <li>The fetched record MUST NOT be older than or equal to any previously accepted version for that <tt>registry_id</tt>.</li>
        </ol>

        <t>
          If all checks succeed, the Relying Party MAY replace its pinned
          trust state with the new Registry Trust Record while retaining the
          prior trust records for historical verification.
        </t>

        <t>
          Historical Step Execution Tokens and CRL documents signed before
          the rotation remain valid for their stated <em>exp</em> period
          only. Relying Parties MUST verify such historical artifacts
          against the Registry Trust Record version that was current at
          issuance time; they MUST NOT re-verify them against the newest
          trust record automatically.
        </t>
      </section>

      <section anchor="registry-genesis-registry-key-rotation-emergency">
        <name>Emergency Re-Bootstrap</name>
        <t>
          This distinction between planned rollover and emergency
          continuity-breaking recovery is similar in spirit to DNSSEC trust
          anchor rollover guidance in <xref target="RFC5011"/>, where
          automated trust continuity and exceptional recovery are treated as
          different operational cases.
        </t>
        <t>
          If a Registry Trust Record update cannot satisfy the planned
          rotation checks, or if the retiring trust key is suspected
          compromised or unavailable, the event MUST be treated as an
          emergency re-bootstrap.
        </t>

        <ol>
        <li>
          Relying Parties that have pinned Registry trust state and
          subsequently receive discovery or trust metadata that does not
          satisfy the planned-rotation checks above MUST treat this as a
          potential MITM condition.
        </li>
        <li>
          Relying Parties MUST NOT use the new trust state automatically.
          They MUST halt Registry-dependent operations and require explicit
          re-bootstrap or out-of-band operator confirmation before
          re-pinning.
        </li>
        <li>
          Emergency re-bootstrap is continuity-breaking by design.
          Automatic verifier acceptance rules for planned rotation do not
          apply.
        </li>
        <li>
          Historical Step Execution Tokens and CRL documents signed by the
          previous Registry trust state remain valid for their stated
          <em>exp</em> period only. Relying Parties MUST verify historical
          artifacts against the trust record version that was current at
          issuance time; they MUST NOT re-verify them against the emergency
          replacement trust state.
        </li>
        </ol>
      </section>

    </section>
  </section>

  <section anchor="aip-gateway-discovery-document" numbered="true">
    <name>AIP Gateway Protected Resource Metadata</name>
    <t>
      Any MCP gateway that enforces AIP authentication for tool listing or
      tool execution MUST publish OAuth Protected Resource Metadata
      <xref target="RFC9728"/> using the registered
      <tt>oauth-protected-resource</tt> well-known URI suffix:
    </t>
    <artwork type="text">
https://{gateway-host}/.well-known/oauth-protected-resource
    </artwork>
    <t>
      AIP does not define or require an AIP-specific well-known URI suffix for
      gateway discovery. The response MUST be a JSON object conforming to
      RFC 9728 and containing the following fields:
    </t>
    <sourcecode type="json">
{
  "resource": "https://gateway.example.com/",
  "authorization_servers": ["https://registry.example.com"],
  "scopes_supported": ["email.read", "calendar.read"],
  "bearer_methods_supported": ["header"],
  "dpop_signing_alg_values_supported": ["EdDSA"],
  "dpop_bound_access_tokens_required": true,
  "resource_name": "Example AIP MCP Gateway",
  "resource_documentation": "https://gateway.example.com/docs"
}
    </sourcecode>
    <dl newline="false">
      <dt><tt>resource</tt></dt>
      <dd>REQUIRED. The gateway protected resource identifier. It MUST be an
      HTTPS URL with no fragment, as defined by <xref target="RFC9728"/>.</dd>

      <dt><tt>authorization_servers</tt></dt>
      <dd>REQUIRED for AIP gateways. The array MUST contain at least one AIP
      Registry OAuth authorization-server issuer identifier acceptable for this
      protected resource.</dd>

      <dt><tt>scopes_supported</tt></dt>
      <dd>RECOMMENDED. When present, values MUST be AIP scope strings from the
      synced AIP Scope Catalog or accepted local extension policy.</dd>

      <dt><tt>bearer_methods_supported</tt></dt>
      <dd>REQUIRED for AIP gateways that accept non-DPoP Bearer tokens and
      MUST include <tt>"header"</tt> when present. AIP uses the registered
      Bearer authentication scheme for requests that do not require
      proof-of-possession and the registered DPoP authentication scheme for
      DPoP-bound tokens.</dd>

      <dt><tt>dpop_signing_alg_values_supported</tt></dt>
      <dd>REQUIRED when any advertised scope or endpoint requires DPoP, and
      MUST include <tt>"EdDSA"</tt>.</dd>

      <dt><tt>dpop_bound_access_tokens_required</tt></dt>
      <dd>REQUIRED when the gateway requires DPoP-bound access tokens for all
      requests to the protected resource. If omitted or false, clients MUST
      still apply AIP DPoP requirements derived from the requested scope Tier,
      Scope Catalog metadata, and endpoint policy.</dd>

      <dt><tt>tls_client_certificate_bound_access_tokens</tt></dt>
      <dd>REQUIRED and set to <tt>true</tt> when the gateway enforces AIP
      Tier 3 mTLS-bound access tokens for the protected resource.</dd>
    </dl>
    <t>
      <strong>Endpoint Requirements:</strong>
    </t>
    <ol type="%d">
      <li>The <tt>/.well-known/oauth-protected-resource</tt> endpoint MUST respond to
      unauthenticated HTTP GET requests. Authentication MUST NOT be required
      to retrieve the Protected Resource Metadata document.</li>
      <li>The response MUST use HTTP status 200 and
      <tt>Content-Type: application/json</tt>.</li>
      <li>The response SHOULD include a <tt>Cache-Control</tt> header with a
      <tt>max-age</tt> value between 60 and 300 seconds. Clients MUST NOT
      cache the Protected Resource Metadata document for longer than 300
      seconds.</li>
      <li><strong>Consistency Constraint:</strong> If a gateway advertises
      Tier 2 or Tier 3 scopes in <tt>scopes_supported</tt>, or if local
      endpoint policy marks the protected resource as Tier 2 or Tier 3, the
      gateway MUST either set <tt>dpop_bound_access_tokens_required</tt> to
      <tt>true</tt> or document per-scope DPoP requirements through Registry
      policy. A client MUST treat contradictory metadata as
      <tt>gateway_config_invalid</tt>.</li>
      <li>For stateless gateway deployments
      (<xref target="token-cache-requirements-stateless"/>), the gateway MUST
      NOT serve stale Protected Resource Metadata; the document MUST reflect
      the currently active resource configuration from the shared configuration
      store.</li>
    </ol>
    <t>
      MCP clients that discover a gateway through OAuth Protected Resource
      Metadata SHOULD fetch this document before tool listing or tool
      execution. If the document is absent, malformed, or does not identify an
      acceptable AIP Registry authorization server, an AIP-aware MCP client
      MUST NOT assume that unauthenticated tool access is permitted.
    </t>
  </section>
</section>
    <section anchor="credential-tokens" numbered="true" xml:base="sections/08-credential-tokens.xml">
  <name>Credential Tokens</name>

  <section anchor="token-structure" numbered="true">
    <name>Credential Token Transport and Version Header</name>
    <t>
      The Credential Token JWT header and payload fields are defined in
      Section 5.4. This section defines only the HTTP transport profile,
      version-header requirements, issuance lifetime rules, and refresh
      behavior for those tokens.
    </t>
    <t>
      An AIP Credential Token for a request that does not require
      proof-of-possession is transmitted as an HTTP Authorization header using
      the registered Bearer authentication scheme <xref target="RFC6750"/>:
    </t>
    <t>EXAMPLE (informative):</t>
    <sourcecode>
 Authorization: Bearer &lt;token&gt;
 X-AIP-Version: 0.3
</sourcecode>
    <t>
      For interactions requiring Proof-of-Possession, the Credential Token is
      transmitted using the registered DPoP authentication scheme
      <xref target="RFC9449"/> and the request also carries a DPoP proof JWT:
    </t>
    <t>EXAMPLE (informative):</t>
    <sourcecode>
 Authorization: DPoP &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. For
      this specification, the value MUST be
      <tt>"0.3"</tt>. This value is the AIP protocol compatibility version
      defined in Section 20, not the Internet-Draft revision suffix.
      Implementations MUST reject requests with <tt>unsupported_version</tt>
      when the <tt>X-AIP-Version</tt> header is absent, carries an unsupported
      value, or does not match the token <tt>aip_version</tt> claim.
    </t>
    <t>
      Every AIP-aware HTTP response to an AIP protocol request MUST include
      an <tt>X-AIP-Version</tt> response header containing the AIP protocol
      compatibility version used to interpret the request and generate the
      response. For responses conforming to this specification, the value
      MUST be <tt>"0.3"</tt>. This requirement applies to successful
      responses and error responses, including responses that reject a
      request with <tt>unsupported_version</tt>. When a request is rejected
      because the requested version is unsupported, the responder MUST set
      <tt>X-AIP-Version</tt> to a protocol version it supports for that
      endpoint and SHOULD include <tt>X-AIP-Supported-Versions</tt> with a
      comma-separated list of supported AIP protocol versions.
    </t>
  </section>

  <section anchor="token-issuance" numbered="true">
    <name>Token Issuance</name>
    <t>
      Implementations MUST enforce the following maximum Credential Token
      lifetimes. The effective TTL limit for a token is the lowest applicable
      limit from this table across all requested scopes.
    </t>
    <table>
      <thead>
        <tr><th>Scope Category</th><th>Maximum TTL</th></tr>
      </thead>
      <tbody>
        <tr><td>Tier 1 scope</td><td>3600 seconds, unless the synced AIP Scope Catalog entry sets a lower <tt>ttl_max_seconds</tt></td></tr>
        <tr><td>Tier 2 scope</td><td>300 seconds, unless the synced AIP Scope Catalog entry sets a lower <tt>ttl_max_seconds</tt></td></tr>
        <tr><td>Tier 3 scope</td><td>300 seconds, unless the synced AIP Scope Catalog entry sets a lower <tt>ttl_max_seconds</tt></td></tr>
        <tr><td>Any requested scope</td><td>The <tt>ttl_max_seconds</tt> value in the synced AIP Scope Catalog entry for that scope, capped by the Tier ceiling above</td></tr>
        <tr><td>Multiple scopes in one token</td><td>The lowest <tt>ttl_max_seconds</tt> value across all requested scopes</td></tr>
      </tbody>
    </table>
    <t>
      When a token contains multiple scopes, the most restrictive TTL applies.
      Zero-duration and negative-duration tokens
      (where <tt>exp &lt;= iat</tt>) MUST be rejected.
      A synced AIP Scope Catalog entry for a standard or extension scope MUST
      NOT advertise a <tt>ttl_max_seconds</tt> value greater than the ceiling
      for that scope's Tier. A token containing a scope whose active catalog
      entry is missing, inactive, or exceeds its Tier ceiling MUST be rejected
      with <tt>invalid_scope</tt> or <tt>invalid_token</tt> as applicable to the
      validation step.
    </t>
    <t>
      A token's security Tier is determined by its highest-risk scope
      (<xref target="architecture-tiers"/>). 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 anchor="token-cache-requirements-stateless" numbered="true">
      <name>Token Cache Requirements for Stateless Deployments</name>
      <t>
        AIP Registries and Relying Parties operating in stateless transport
        environments, including MCP stateless transport
        <xref target="MCP-STATELESS"/>, or horizontally-scaled deployments
        MUST store validated Credential Token cache entries in a shared,
        externally-accessible store, such as Redis or a distributed key-value
        store, that is accessible to all instances that may validate tokens for
        the same AID. Local in-process or filesystem-backed caches are
        PERMITTED only in single-instance development deployments and MUST NOT
        be used in a Production Deployment as defined in
        <xref target="terminology"/>.
      </t>
      <t>
        A validation cache entry MUST be keyed by
        <tt>SHA-256(exact-compact-token)</tt>, not by <tt>jti</tt> alone.
        The cache entry MUST include:
      </t>
      <ul>
        <li>the token <tt>iss</tt> and <tt>jti</tt> for replay-cache
        correlation;</li>
        <li>the token <tt>exp</tt> as the maximum cache TTL;</li>
        <li>the cached Registry-dependent validation subresults and their
        freshness sources;</li>
        <li>the set of validation step identifiers whose results were cached;
        and</li>
        <li>the validated AIP Tier.</li>
      </ul>
      <t>
        A cache entry is an optimization for previously completed validation,
        not a replacement for the validation algorithm. Implementations MAY
        reuse cached Registry-dependent validation results only when the
        token's Tier and the relevant validation step permit bounded
        staleness. For Tier 2 and Tier 3 tokens, a cached accepted result MUST
        NOT bypass the live revocation lookup required by
        <xref target="revocation-checking"/> and <xref target="validation-step-7"/>.
        Per-request checks that are not cacheable, including
        proof-of-possession, mTLS, audience matching, expiration, and replay
        policy, remain in force for every interaction. A cached accepted
        result MUST NOT cause a token whose <tt>(iss, jti)</tt> pair has
        already been consumed to be accepted again. This cache prevents
        redundant Registry round-trips only for validation results whose
        freshness rules allow reuse.
      </t>
    </section>
  </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> across
        all tokens issued by that agent AID. This uniqueness requirement is
        independent of <tt>aip_chain</tt>, principal DID, registration grant,
        scope set, or audience. An agent delegated by multiple principals
        MUST still maintain a single <tt>jti</tt> uniqueness domain for its
        issuer AID.
      </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. The refresh threshold is derived
        from the token's effective TTL, not from a fixed Tier label:
      </t>
      <sourcecode>
effective_ttl_seconds = exp - iat

refresh_lead_seconds =
  max(1, min(
    300,
    max(30, ceiling(effective_ttl_seconds * 0.10)),
    floor(effective_ttl_seconds / 2)
  ))

begin refresh when (exp - now) &lt;= refresh_lead_seconds
</sourcecode>
      <t>
        This formula preserves the former 300-second refresh lead for a
        3600-second catalog TTL and the former 30-second refresh lead for a
        300-second catalog TTL, while remaining valid for future catalog TTL
        values.
      </t>
      <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 or sub-agent delegation flow.</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
        re-establish their delegation rather than merely refreshing their
        Credential Token. AIP does not define a separate on-wire error code
        for "delegation refresh required"; chain renewal is the required
        agent-side handling of <tt>chain_token_expired</tt>.
      </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 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 catalog-derived Credential Token TTLs apply only at execution time.</li>
        <li>When an agent claims an Approval Step, 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 <xref target="token-issuance"/> catalog-derived rules as for any other interaction involving those scopes.</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 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 9 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
       .
      </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=email.read 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. For OAuth-protected resources, the value MUST be an RFC 9728 <xref target="RFC9728"/> resource identifier.</li>
        <li><tt>scope</tt> MUST use AIP scope strings from the synced AIP Scope Catalog or accepted local extension policy. 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.</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>
          <t>Verify the <tt>resource</tt> identifies a registered MCP server
          or OAuth-protected resource. The Registry MUST perform this check
          using at least one of the following mechanisms:</t>
          <ul>
            <li><t>match the <tt>resource</tt> URI against a Registry-local
            registration record for an MCP server or protected resource; or</t></li>
            <li><t>obtain the resource's Protected Resource Metadata using
            RFC 9728 <xref target="RFC9728"/> and verify that the returned
            <tt>resource</tt> metadata value is the same resource identifier
            as the requested <tt>resource</tt>.</t></li>
          </ul>
          <t>
            When RFC 9728 metadata is used, the metadata MUST identify an
            authorization server acceptable to the Registry, either by listing
            the Registry's OAuth authorization-server issuer identifier in
            <tt>authorization_servers</tt> or by satisfying an equivalent
            local trust policy. The Registry MUST reject with
            <tt>invalid_target</tt> if metadata retrieval fails, the returned
            <tt>resource</tt> value does not match the requested
            <tt>resource</tt>, the resource is not locally registered, or no
            acceptable authorization server relationship is established.
          </t>
        </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": "email.read calendar.read",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token"
 }
</sourcecode>
    </section>

    <section anchor="enterprise-idp-federation-profile" numbered="true">
      <name>Enterprise IdP Federation Profile</name>
      <t>
        When the target resource in a token exchange request is registered in
        the Registry with <tt>enterprise_idp_required: true</tt> (see
        <xref target="resource-registration-registry"/>), the AIP
        Registry acting as intermediary MUST perform a secondary token
        exchange with the configured Enterprise IdP. The Registry MUST NOT
        perform the enterprise IdP leg for resources registered with
        <tt>enterprise_idp_required: false</tt> or with
        <tt>enterprise_idp_required</tt> absent. Enterprise integrations such
        as Microsoft Entra Agent ID <xref target="MS-ENTRAAGENTID"/> can use
        this profile when the resource registration record requires it.
      </t>
      <ol type="%d">
        <li>The AIP Registry validates the inbound AIP Credential Token per
        <xref target="credential-token-validation"/>.</li>
        <li>
          <t>The Registry constructs a signed JWT assertion using its
          registered client identity with the enterprise IdP. The assertion
          MUST include:</t>
          <ul>
            <li><tt>iss</tt>: the AIP Registry's <tt>client_id</tt>
            registered with the enterprise IdP;</li>
            <li><tt>sub</tt>: the agent's AID;</li>
            <li><tt>aud</tt>: the enterprise IdP token endpoint URI;</li>
            <li><tt>aip_sub_principal</tt>: the root Principal Token
            <tt>principal.id</tt> value from the validated
            <tt>aip_chain</tt>;</li>
            <li><tt>aip_scopes</tt>: the validated <tt>aip_scope</tt> values
            from the Credential Token.</li>
          </ul>
        </li>
        <li>The Registry submits this assertion to the enterprise IdP token
        endpoint using
        <tt>grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer</tt> per
        RFC 7523 <xref target="RFC7523"/> or
        <tt>grant_type=urn:ietf:params:oauth:grant-type:token-exchange</tt>
        per RFC 8693 <xref target="RFC8693"/>.</li>
        <li>The enterprise IdP evaluates the request against its Conditional
        Access, ABAC, and equivalent enterprise access policies using
        <tt>aip_sub_principal</tt> as the human or organizational principal
        context and the agent AID as the client identity.</li>
        <li>On success, the enterprise IdP issues an access token scoped to
        the target resource.</li>
        <li>The AIP Registry returns the enterprise-issued token in the token
        exchange response.</li>
      </ol>

      <section anchor="enterprise-assertion-jwt-schema" numbered="true">
        <name>Enterprise Assertion JWT Schema</name>
        <t>
          The JWT assertion constructed by the Registry for the enterprise IdP
          token endpoint MUST contain the following payload fields:
        </t>
        <table>
          <thead>
            <tr><th>Field</th><th>Type</th><th>Required</th><th>Constraints</th></tr>
          </thead>
          <tbody>
            <tr><td><tt>iss</tt></td><td>string</td><td>REQUIRED</td><td>MUST be the AIP Registry's <tt>client_id</tt> as registered with the enterprise IdP. MUST NOT be the Registry's <tt>registry_id</tt>.</td></tr>
            <tr><td><tt>sub</tt></td><td>string</td><td>REQUIRED</td><td>MUST be the acting agent's AID.</td></tr>
            <tr><td><tt>aud</tt></td><td>string or array</td><td>REQUIRED</td><td>MUST be the enterprise IdP's token endpoint URI.</td></tr>
            <tr><td><tt>aip_sub_principal</tt></td><td>string</td><td>REQUIRED</td><td>MUST be the root Principal Token <tt>principal.id</tt> value from <tt>aip_chain[0]</tt> of the validated Credential Token. MUST be a valid W3C DID.</td></tr>
            <tr><td><tt>aip_scopes</tt></td><td>array of strings</td><td>REQUIRED</td><td>MUST be the validated <tt>aip_scope</tt> values from the Credential Token. MUST NOT include scopes not present in the validated Credential Token.</td></tr>
            <tr><td><tt>iat</tt></td><td>integer</td><td>REQUIRED</td><td>Unix timestamp representing the current time at assertion construction.</td></tr>
            <tr><td><tt>exp</tt></td><td>integer</td><td>REQUIRED</td><td>Unix timestamp. MUST be <tt>iat + 60</tt> or earlier. Registries MUST NOT issue assertions with <tt>exp - iat &gt; 60</tt>.</td></tr>
            <tr><td><tt>jti</tt></td><td>string</td><td>REQUIRED</td><td>UUID v4. MUST be unique per assertion to prevent replay.</td></tr>
          </tbody>
        </table>
      </section>

      <section anchor="enterprise-idp-error-handling" numbered="true">
        <name>Enterprise IdP Error Handling</name>
        <t>
          When the enterprise IdP token endpoint returns an error or cannot be
          reached, the AIP Registry MUST map the result to the agent-facing
          AIP response as follows:
        </t>
        <table>
          <thead>
            <tr><th>Enterprise IdP Response</th><th>AIP Registry Response to Agent</th></tr>
          </thead>
          <tbody>
            <tr><td>HTTP 400 (<tt>invalid_request</tt>)</td><td>HTTP 400, error: <tt>invalid_request</tt></td></tr>
            <tr><td>HTTP 400 (<tt>invalid_client</tt>)</td><td>HTTP 500, error: <tt>idp_client_misconfigured</tt></td></tr>
            <tr><td>HTTP 400 (<tt>invalid_grant</tt>)</td><td>HTTP 400, error: <tt>invalid_grant</tt></td></tr>
            <tr><td>HTTP 400 (<tt>unauthorized_client</tt>)</td><td>HTTP 403, error: <tt>insufficient_scope</tt></td></tr>
            <tr><td>HTTP 403 (<tt>access_denied</tt> or Conditional Access denial)</td><td>HTTP 403, error: <tt>enterprise_policy_denied</tt></td></tr>
            <tr><td>HTTP 4xx (other)</td><td>HTTP 400, error: <tt>invalid_request</tt></td></tr>
            <tr><td>HTTP 5xx</td><td>HTTP 503, error: <tt>registry_unavailable</tt></td></tr>
            <tr><td>Timeout greater than 5 seconds</td><td>HTTP 503, error: <tt>registry_unavailable</tt></td></tr>
            <tr><td>TLS or network error</td><td>HTTP 503, error: <tt>registry_unavailable</tt></td></tr>
          </tbody>
        </table>
        <t>
          The Registry MUST NOT expose raw enterprise IdP error messages to
          the calling agent. When <tt>enterprise_policy_denied</tt> is
          returned, the <tt>error_description</tt> SHOULD indicate that the
          request was denied by enterprise policy without revealing specific
          policy rules. The Registry MUST log the full enterprise IdP response
          for audit purposes per <xref target="key-management"/>.
        </t>
      </section>
      <section anchor="enterprise-idp-federation-metadata" numbered="true">
        <name>Federation Metadata and Grant Tier Guidance</name>
        <t>
          AIP Registries that support this profile MUST advertise
          <tt>enterprise_idp_federation_supported: true</tt> in their
          <tt>/v1/registry-metadata</tt> metadata as described by
          <xref target="registry-genesis-well-known-publication"/> and
          <xref target="well-known-metadata-registry"/>. Relying Parties and
          agents MUST NOT infer federation support from absence of this field.
        </t>
        <t>
          Enterprise deployments using this profile SHOULD require G3 grants
          from <xref target="principal-grant"/> for the root Principal Token so
          that human authentication context and IdP policy context are
          available during token exchange and Tier 3 validation.
        </t>
      </section>
    </section>
  </section>
</section>
    <section anchor="credential-token-validation" numbered="true" xml:base="sections/09-validation.xml">
  <name>Credential and Step Execution Token Validation</name>
  <t>
    The Credential Token Validation Algorithm is the normative heart of AIP
    for agent-issued Credential Tokens. A Relying Party MUST execute the
    following steps in the order presented unless it is validating a
    Registry-issued Step Execution Token under the SET validation profile
    below.
    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.
    Additional lettered steps marked 2a, 6a, 6b, 9a, 9b, 9c, 9d, and 10a are part
    of the numbered step at which they appear and do not change the base step
    numbering.
  </t>
  <t>
    The labels in the following table are normative validation step identifiers
    for implementation traceability and conformance tests. Lettered labels,
    including Steps 5a through 5g, 8a through 8l, and 11a through 11c, are
    discrete sub-steps whether they appear as separate XML section headings or
    as labelled items within a validation step.
  </t>
  <table>
    <name>Credential Token Validation Step Index</name>
    <thead>
      <tr>
        <th align="left">Label</th>
        <th align="left">Location</th>
        <th align="left">Validation subject</th>
      </tr>
    </thead>
    <tbody>
      <tr><td>1</td><td><xref target="validation-step-1"/></td><td>JWT parse</td></tr>
      <tr><td>2</td><td><xref target="validation-step-2"/></td><td>JWT header validation</td></tr>
      <tr><td>2a</td><td><xref target="validation-step-2a"/></td><td>Expiration preflight before Registry lookup</td></tr>
      <tr><td>3</td><td><xref target="validation-step-3"/></td><td>Agent identification and public-key retrieval</td></tr>
      <tr><td>4</td><td><xref target="validation-step-4"/></td><td>Credential Token signature verification</td></tr>
      <tr><td>5a</td><td><xref target="validation-step-5a"/></td><td><tt>iat</tt> issued-at validation</td></tr>
      <tr><td>5b</td><td><xref target="validation-step-5b"/></td><td><tt>exp</tt> ordering validation</td></tr>
      <tr><td>5c</td><td><xref target="validation-step-5c"/></td><td>Credential Token expiration validation</td></tr>
      <tr><td>5d</td><td><xref target="validation-step-5d"/></td><td><tt>aud</tt> validation</td></tr>
      <tr><td>5e</td><td><xref target="validation-step-5e"/></td><td><tt>jti</tt> replay validation</td></tr>
      <tr><td>5f</td><td><xref target="validation-step-5f"/></td><td><tt>aip_version</tt> validation</td></tr>
      <tr><td>5g</td><td><xref target="validation-step-5g"/></td><td>Issuer and subject binding</td></tr>
      <tr><td>6</td><td><xref target="validation-step-6"/></td><td>Credential Token TTL validation</td></tr>
      <tr><td>6a</td><td><xref target="validation-step-6a"/></td><td>Registry trust anchoring</td></tr>
      <tr><td>6b</td><td><xref target="validation-step-6b"/></td><td>Engagement validation</td></tr>
      <tr><td>7</td><td><xref target="validation-step-7"/></td><td>Revocation validation</td></tr>
      <tr><td>8a-8l</td><td><xref target="validation-step-8"/></td><td>Delegation chain sub-step validation</td></tr>
      <tr><td>8 Post-Check A</td><td><xref target="validation-step-8"/></td><td>Credential Token issuer equals chain leaf</td></tr>
      <tr><td>8 Post-Check B</td><td><xref target="validation-step-8"/></td><td>Credential Token subject equals issuer</td></tr>
      <tr><td>8 Post-Check C</td><td><xref target="validation-step-8"/></td><td>Identity proofing claims</td></tr>
      <tr><td>9</td><td><xref target="validation-step-9"/></td><td>Capability Manifest validation</td></tr>
      <tr><td>9a</td><td><xref target="validation-step-9a"/></td><td>Scope-to-manifest mapping</td></tr>
      <tr><td>9b</td><td><xref target="validation-step-9b"/></td><td>Capability Overlay application</td></tr>
      <tr><td>9c</td><td><xref target="validation-step-9c"/></td><td>Delegation inheritance</td></tr>
      <tr><td>9d</td><td><xref target="validation-step-9d"/></td><td>Grant Tier conformance</td></tr>
      <tr><td>10</td><td><xref target="validation-step-10"/></td><td>DPoP validation</td></tr>
      <tr><td>10a</td><td><xref target="validation-step-10a"/></td><td>Approval step verification</td></tr>
      <tr><td>11a</td><td><xref target="validation-step-11"/></td><td><tt>aip_principal_assertion</tt> presence</td></tr>
      <tr><td>11b</td><td><xref target="validation-step-11"/></td><td><tt>aip_principal_assertion</tt> signature verification</td></tr>
      <tr><td>11c</td><td><xref target="validation-step-11"/></td><td><tt>aip_principal_assertion</tt> principal binding</td></tr>
      <tr><td>12</td><td><xref target="validation-step-12"/></td><td>Accept</td></tr>
    </tbody>
  </table>

  <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 <tt>"EdDSA"</tt></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-2a" numbered="true">
  <name>Step 2a: Expiration Preflight</name>
    <t>
      Before performing any Registry key lookup, the Relying Party MUST decode
      the JWT payload without using it for any acceptance decision. This
      preflight is only an error-ordering and lookup-amplification control.
      Signature verification in Step 4 and full claim validation in Step 5
      remain mandatory before a token can be accepted.
    </t>
    <t>
      The Relying Party MUST inspect only <tt>iat</tt>, <tt>exp</tt>, and
      <tt>aip_scope</tt> during this preflight. If <tt>iat</tt> or
      <tt>exp</tt> is absent, not an integer, or <tt>exp &lt;= iat</tt>,
      reject with <tt>invalid_token</tt>. If <tt>exp</tt> is in the past,
      reject with <tt>token_expired</tt>. This rejection applies even if the
      token's <tt>kid</tt> references a historical key version that is no
      longer retained by the Registry.
    </t>
    <t>
      If the unsigned payload's <tt>exp - iat</tt> exceeds the largest
      <tt>ttl_max_seconds</tt> value among active entries in the synced AIP
      Scope Catalog, the Relying Party MAY reject with
      <tt>invalid_token</tt> before key lookup. This check is conservative:
      it can reject tokens that no valid catalog entry could authorize, but it
      MUST NOT be used to accept a token.
    </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> using
      <tt>GET /v1/agents/{aid}/public-key/{key-id}</tt>, where
      <tt>{aid}</tt> is the DID URL without the fragment and
      <tt>{key-id}</tt> is the fragment without the leading <tt>#</tt>.
      The Registry response MUST conform to
      <tt>public-key-response.schema.json</tt> and return the public key
      material along with its validity period (<tt>valid_from</tt> timestamp
      and <tt>valid_until</tt> timestamp or <tt>null</tt>).
    </t>
    <t>
      If the Registry cannot be reached or the public key lookup cannot be
      completed because the Registry is unavailable, reject with
      <tt>registry_unavailable</tt>. If the Registry lookup succeeds but 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>. Because
      Step 2a runs before this lookup, an already-expired token MUST surface
      as <tt>token_expired</tt>, not <tt>unknown_aid</tt>, even when its
      historical signing key has aged out of Registry retention.
    </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>
    <section anchor="validation-step-5a" numbered="true">
      <name>Step 5a: iat (Issued-At Time)</name>
      <t>
        <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>.
      </t>
    </section>

    <section anchor="validation-step-5b" numbered="true">
      <name>Step 5b: exp (Expiration Time)</name>
      <t>
        <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>.
      </t>
    </section>

    <section anchor="validation-step-5c" numbered="true">
      <name>Step 5c: Token Not Expired</name>
      <t>
        <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>.
      </t>
    </section>

    <section anchor="validation-step-5d" numbered="true">
      <name>Step 5d: aud (Audience)</name>
      <t>
        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>.
      </t>
    </section>

    <section anchor="validation-step-5e" numbered="true">
      <name>Step 5e: jti (JWT ID) Replay Check</name>
      <t>
        <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>. This replay rule applies to authorisation
        decisions. Registry state-transition endpoints that define explicit
        idempotency behavior MAY return a previously stored response for an
        exact retry, but MUST NOT re-execute side effects and MUST NOT treat
        replay-cache acceptance as proof of fresh authority.
        The replay cache key MUST NOT include <tt>aip_chain</tt>, principal
        DID, registration grant, scope set, or audience. Credential Token
        issuers therefore MUST treat <tt>jti</tt> values as globally unique
        within the issuer AID across all delegation chains. After the token
        expires, the replay cache entry MAY be discarded.
      </t>
    </section>

    <section anchor="validation-step-5f" numbered="true">
      <name>Step 5f: aip_version</name>
      <t>
        <tt>aip_version</tt> MUST be present and MUST be <tt>"0.3"</tt> for
        tokens conforming to this specification. This value is the AIP
        protocol compatibility version defined in Section 20, not the
        Internet-Draft revision suffix. If <tt>aip_version</tt> is absent,
        reject with <tt>invalid_token</tt>. If <tt>aip_version</tt> is
        present but unsupported, or if it does not match the
        <tt>X-AIP-Version</tt> request header, reject with
        <tt>unsupported_version</tt> and include the received
        <tt>aip_version</tt> and <tt>X-AIP-Version</tt> values in the error
        description to aid debugging.
      </t>
    </section>

    <section anchor="validation-step-5g" numbered="true">
      <name>Step 5g: Issuer and Subject Binding</name>
      <t>
        The <tt>iss</tt> and <tt>sub</tt> claims MUST each match the
        <tt>did:aip</tt> ABNF. Let <tt>kid_aid</tt> be the DID URL in the
        JWT header <tt>kid</tt> with the fragment removed. The payload
        <tt>iss</tt> MUST equal <tt>kid_aid</tt>. The payload <tt>sub</tt>
        MUST equal <tt>iss</tt>. This specification does not define
        agent-issued Credential Tokens where a delegated token subject differs
        from the signing leaf agent. If any comparison fails, reject with
        <tt>invalid_token</tt>.
      </t>
    </section>
  </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 <tt>ttl_max_seconds</tt> values in the synced AIP
      Scope Catalog and the Tier ceilings in <xref target="token-issuance"/>:
    </t>
    <artwork>
effective_ttl_limit =
  min(
    scope.ttl_max_seconds,
    tier_ttl_ceiling(scope.tier)
    for scope in aip_scope
  )

When a token contains multiple scopes, the most
restrictive catalog TTL applies.
    </artwork>
    <t>
      If <tt>lifetime</tt> exceeds the limit, reject with <tt>invalid_token</tt>.
    </t>
    <t>
      For validation, a token's security Tier is determined by its
      highest-risk scope (<xref target="architecture-tiers"/>). A token containing one Tier 2 scope
      and nine Tier 1 scopes is a Tier 2 token in its entirety. A Relying
      Party MUST NOT derive Tier from the majority of scopes or from the
      first scope in the array.
    </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 and Tier 3 operations and for any
      Credential Token that contains <tt>aip_registry</tt>. It is RECOMMENDED
      for Tier 1 operations when <tt>aip_registry</tt> is absent. 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>If the operation's security Tier is 2 or 3, <tt>principal_id</tt>
        MUST use the <tt>did:web</tt> method. If it uses <tt>did:key</tt>,
        <tt>did:aip</tt>, or any other DID method, reject with
        <tt>principal_did_method_forbidden</tt>.</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 operation's security Tier is 2 or 3 or the token
        contains <tt>aip_registry</tt>, reject with
        <tt>registry_untrusted</tt>.</li>
    </ol>
    <t>
      For Tier 1 operations where <tt>aip_registry</tt> is absent, 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 operation's security Tier is 2 or 3 or the
      token contains <tt>aip_registry</tt>, reject with
      <tt>registry_unavailable</tt>.
    </t>
    <t>
      DID resolution for this step MUST use the timeout requirements in
      Section 7.1. For Tier 2 or Tier 3 operations and tokens containing
      <tt>aip_registry</tt>, a DID resolution timeout is a validation failure
      and MUST be reported as <tt>registry_unavailable</tt>; implementations
      MUST NOT wait indefinitely for the principal DID resolver.
    </t>
    <t>
      A Relying Party MUST NOT use <tt>aip_registry</tt> as an authoritative
      Registry locator unless this step has verified it against the
      DID-Document-declared Registry endpoint.
    </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, evaluate each gate
        <tt>trigger</tt> using the trigger grammar in
        <xref target="engagement-objects"/>. A
        <tt>scope:&lt;scope-string&gt;</tt> trigger guards any operation whose
        requested <tt>aip_scope</tt> set contains that scope. An
        <tt>action_type:&lt;action-type&gt;</tt> trigger guards Approval
        Envelope steps whose <tt>action_type</tt> exactly equals that value.
        If a matching required gate is still pending, reject with
        <tt>engagement_gate_pending</tt>. If a matching required gate has
        status <tt>"rejected"</tt>, reject with
        <tt>engagement_terminated</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. Chain-wide and principal-wide
      revocation effects are completed in Step 8 after <tt>aip_chain</tt>
      has been validated.
    </t>
    <ul>
      <li><strong>Tier 1:</strong> Retrieve a verifiable CRL from the Registry
        cache. The CRL's <tt>next_update</tt> MUST be later than the Relying
        Party's current UTC validation time. If no fresh verifiable CRL is
        available, reject with <tt>registry_unavailable</tt>. If the agent
        appears on the CRL with revocation type <tt>full_revoke</tt> or
        <tt>principal_revoke</tt>, reject with <tt>agent_revoked</tt>. If the
        agent appears with <tt>scope_revoke</tt>, the Relying Party MUST verify
        that none of the scopes in the token's <tt>aip_scope</tt> are present
        in the <tt>scopes_revoked</tt> list. If any match, reject with
        <tt>agent_revoked</tt>. The same CRL MUST also be used in Step 8 to
        evaluate <tt>principal_revoke</tt> entries whose <tt>target_id</tt> is
        the root Principal DID or any AID in the validated
        <tt>aip_chain</tt>.</li>
      <li><strong>Tier 2:</strong> Perform a live Registry lookup. Query
        <tt>GET /v1/agents/{iss}/revocation</tt>. A successful response MUST
        be HTTP 200 with a body conforming to
        <tt>revocation-status.schema.json</tt>
        (<xref target="agent-revocation-status-registry"/>). If
        <tt>revoked</tt> is <tt>true</tt>, reject with
        <tt>agent_revoked</tt>. If any currently requested scope appears in
        <tt>scopes_revoked</tt>, reject with <tt>agent_revoked</tt>. If the
        token relies on delegation authority rooted at an AID whose response
        has <tt>delegation_revoked</tt> equal to <tt>true</tt>, reject with
        <tt>agent_revoked</tt>. If the Registry returns
        <tt>unknown_aid</tt>, reject with <tt>unknown_aid</tt>. If the
        Registry is unreachable, reject with
        <tt>registry_unavailable</tt>. A previously cached accepted token
        validation result MUST NOT be used to skip this lookup.</li>
    </ul>
    <t>
      The Relying Party MUST verify the Registry trust state used for this
      revocation check using the Registry trust bootstrapping and Registry
      Trust Record procedures in
      <xref target="registry-genesis-well-known-publication"/> and
      <xref target="registry-genesis-trust-record"/>, and the sequential
      trust-update procedure in
      <xref target="registry-genesis-registry-key-rotation-planned"/> when
      updating pinned trust state, before treating Registry responses as
      authoritative.
    </t>
  </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.
    </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. Its JOSE header MUST
        contain <tt>typ</tt> equal to <tt>"JWT"</tt>, <tt>alg</tt> equal
        to <tt>"EdDSA"</tt>, and a non-empty <tt>kid</tt> DID URL. If
        parsing, schema validation, or header 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>, and
        <tt>aip_chain[1]</tt>, when present, has
        <tt>delegation_depth: 1</tt>. 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. Issuer and kid Binding:</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>. For <tt>i &gt; 0</tt>, <tt>iss</tt> MUST equal
        <tt>delegated_by</tt>. The <tt>kid</tt> header MUST identify an
        Ed25519 verification method controlled by <tt>iss</tt>, and the DID
        portion of <tt>kid</tt> MUST equal <tt>iss</tt>. If any of these
        bindings fail, reject with <tt>delegation_chain_invalid</tt>.
      </dd>

      <dt>8d-1. Root Principal Signature Verification:</dt>
      <dd>
        For <tt>i = 0</tt>, resolve <tt>kid</tt> using the DID method of the
        root principal DID in <tt>principal.id</tt>. The AIP Registry MUST NOT
        be used as the authority for root-principal keys. If DID resolution
        fails, the key is not Ed25519 verification material, the verification
        method is not controlled by <tt>principal.id</tt>, or signature
        verification fails, reject with
        <tt>delegation_chain_invalid</tt>.
      </dd>

      <dt>8d-2. Delegated Agent Key Lookup:</dt>
      <dd>
        For <tt>i &gt; 0</tt>, <tt>iss</tt> is the parent agent AID that
        signed this delegated Principal Token. The Relying Party MUST resolve
        the signing key through the authoritative AIP Registry using the
        percent-encoded issuer AID and the <tt>kid</tt> fragment:
        <tt>GET /v1/agents/{iss}/public-key/{key-id}</tt>. The
        <tt>key-id</tt> path component is the fragment portion of
        <tt>kid</tt> without the leading number-sign character. The Registry response
        MUST identify the same AID as <tt>iss</tt> and return Ed25519 public
        key material whose validity interval covers the Principal Token's
        <tt>issued_at</tt> instant. If the Registry cannot be reached or the
        key lookup cannot be completed because the Registry is unavailable,
        reject with <tt>registry_unavailable</tt>. If the Registry lookup
        succeeds but <tt>iss</tt> is not a registered AID, <tt>kid</tt> is
        not found, or the key is not valid at <tt>issued_at</tt>, reject with
        <tt>unknown_aid</tt>.
      </dd>

      <dt>8d-3. Delegated Principal Token Signature Verification:</dt>
      <dd>
        For <tt>i &gt; 0</tt>, verify the Principal Token signature using the
        key resolved in Step 8d-2. If the resolved key is not Ed25519
        verification material, identifies a key not controlled by
        <tt>iss</tt>, or signature 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. A delegated
        Principal Token MUST NOT be self-delegated: <tt>delegated_by[i]</tt>
        MUST NOT equal <tt>sub[i]</tt>. If linkage fails or the token is
        self-delegated, 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 the Registry returns <tt>unknown_aid</tt> for any chain
        <tt>sub</tt> AID, reject with <tt>unknown_aid</tt>. If any agent in
        the chain is revoked, reject with <tt>agent_revoked</tt>. A
        <tt>principal_revoke</tt> whose <tt>target_id</tt> equals any chain
        <tt>sub</tt> AID MUST be treated as revoked even when no descendant
        Revocation Object has been materialised for that AID.
      </dd>

      <dt>8g. No Duplicate AIDs:</dt>
      <dd>
        No AID may appear more than once in the chain (no cycles or
        duplicates). This rule includes direct self-delegation, where a
        Principal Token attempts to delegate from an AID back to the same
        AID. 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>, parse <tt>issued_at</tt> and
        <tt>expires_at</tt> as ISO 8601 UTC instants. Verify
        <tt>issued_at</tt> is not more than 30 seconds in the future
        relative to the Relying Party's current UTC clock. If it is, reject
        with <tt>delegation_chain_invalid</tt>. Verify
        <tt>expires_at</tt> &gt; <tt>issued_at</tt>; if not, reject with
        <tt>delegation_chain_invalid</tt>. Verify <tt>expires_at</tt> is in
        the future; if expired, 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>

      <dt>8k. Namespace Task Binding:</dt>
      <dd>
        For every Principal Token whose <tt>sub</tt> namespace has
        <tt>requires_task_id: true</tt> in the synced AIP Namespace Catalog,
        the token payload MUST include a non-null, non-empty
        <tt>task_id</tt>. If such a chain element omits <tt>task_id</tt>, sets
        it to <tt>null</tt>, or sets it to an empty string, reject with
        <tt>delegation_chain_invalid</tt>.
      </dd>

      <dt>8l. Root Principal Revocation:</dt>
      <dd>
        Let <tt>root_principal_id</tt> be the <tt>principal.id</tt> value
        that Step 8i verified across the chain. Using the revocation source
        required for the token's Tier, verify that no active
        <tt>principal_revoke</tt> has <tt>target_id</tt> equal to
        <tt>root_principal_id</tt>. A match invalidates every Credential
        Token and Step Execution Token rooted at that Principal DID; reject
        with <tt>agent_revoked</tt>. This rejection is required regardless of
        the <tt>propagate_to_children</tt> value on the Revocation Object.
      </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>
        Verify that the Credential Token payload <tt>sub</tt> MUST equal
        <tt>iss</tt>. Together with Post-Check A, this confirms that the token
        subject, token issuer, signing key AID, and leaf Principal Token
        subject all identify the same acting agent. This check applies to
        both direct and delegated Credential Tokens. If mismatch, reject with
        <tt>delegation_chain_invalid</tt>.
      </t>
    </section>

    <section numbered="true">
  <name>Step 8 Post-Check C: Identity Proofing</name>
      <t>
        This check applies when any scope in <tt>aip_scope</tt> has synced
        AIP Scope Catalog tier <tt>3</tt>, when the Registry's
        <tt>identity_proofing_required_for_tier2</tt> metadata field is
        <tt>true</tt> and the operation security Tier is <tt>2</tt>, when
        Registry registration metadata for
        the agent identifies <tt>grant_tier: "G3"</tt>, or when the Relying
        Party or operation policy declares one or more required
        <tt>acr_values</tt>.
      </t>
      <t>
        When this check applies, the root Principal Token at
        <tt>aip_chain[0]</tt> MUST include a non-empty <tt>acr</tt> string and
        a non-empty <tt>amr</tt> array. If either claim is absent or empty, the
        Relying Party MUST reject the request with
        <tt>identity_proofing_insufficient</tt>.
      </t>
      <t>
        The Relying Party MUST evaluate only the <tt>acr</tt> and
        <tt>amr</tt> values contained in the signed root Principal Token. It
        MUST NOT accept unsigned or out-of-band <tt>acr</tt> or <tt>amr</tt>
        values for this check.
      </t>
      <t>
        If one or more <tt>acr_values</tt> are required, the <tt>acr</tt>
        value in <tt>aip_chain[0]</tt> MUST exactly match one of those required
        values unless the Registry's <tt>/v1/registry-metadata</tt>
        document contains an <tt>acr_equivalence_map</tt> object
        (<xref target="well-known-metadata-registry"/>) that explicitly maps
        the received <tt>acr</tt> value to one of the required values.
        Equivalence MUST only map a received value to a declared required
        value where the received value represents equal or higher assurance
        than the required value under the Registry's published ACR equivalence
        policy. The <tt>acr_equivalence_map</tt> MUST NOT map a
        lower-assurance <tt>acr</tt> to a higher-assurance <tt>acr</tt>. The
        policy MUST identify the assurance framework or frameworks used for
        each mapping and MUST give a stable ordering or rationale sufficient
        for an auditor to reproduce the decision. Implementations MUST NOT
        accept equivalence mappings from any source other than the Registry's
        signed <tt>/v1/registry-metadata</tt> document and its referenced
        policy. This specification does not define a global ordering over
        <tt>acr</tt> values. If the <tt>acr</tt> claim does not satisfy the
        declared requirement, the Relying Party MUST reject the request with
        <tt>identity_proofing_insufficient</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 <tt>GET /v1/agents/{iss}/capabilities</tt>. The
      response MUST conform to <tt>capability-manifest.schema.json</tt>, and
      <tt>manifest.aid</tt> MUST equal <tt>iss</tt>. If unavailable,
      malformed, or not bound to <tt>iss</tt>, reject with
      <tt>manifest_invalid</tt>.</li>
      <li>Verify the manifest signature using <tt>manifest.signature_kid</tt>.
      The DID portion of <tt>signature_kid</tt> MUST equal
      <tt>manifest.granted_by</tt>. If key resolution, key binding, or
      signature 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 each requested scope whose synced AIP Scope Catalog entry has a non-null
        <tt>constraint_schema</tt>, validate the corresponding Capability
        Manifest value against that schema. If validation fails, reject with
        <tt>manifest_invalid</tt>.</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 with
      <tt>status: "active"</tt> or, when local policy allows experimental
      behavior, <tt>status: "experimental"</tt> in the synced AIP Scope
      Catalog. If any requested scope is unknown, reserved, removed, or not
      permitted by local extension policy, reject with <tt>invalid_scope</tt>.
      Then verify each requested scope is present in the Capability Manifest.
      If any requested scope is absent from the Capability Manifest, reject
      with <tt>insufficient_scope</tt>.
    </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. For each overlay constraint whose synced AIP Scope Catalog entry has
      a non-null <tt>constraint_schema</tt>, validate the overlay constraint
      against that schema before applying attenuation. If schema validation
      fails, reject with <tt>overlay_exceeds_manifest</tt>.
    </t>
  </section>

  <section anchor="validation-step-9c" numbered="true">
    <name>Step 9c: Delegation Scope Inheritance</name>
    <t>
      For delegated Credential Tokens, the Relying Party MUST verify that the
      requested scopes and the leaf agent's Capability Manifest are valid
      attenuations of every delegation in <tt>aip_chain</tt>.
    </t>
    <t>
      Let <tt>chain[i]</tt> be the decoded Principal Token payload at
      <tt>aip_chain[i]</tt>, and let <tt>manifest(chain[i].sub)</tt> be the
      current Capability Manifest for the agent identified by
      <tt>chain[i].sub</tt>. Let <tt>n</tt> be the length of
      <tt>aip_chain</tt>. The Relying Party MUST execute the inheritance
      validation in increasing index order, from the root delegated agent
      (<tt>i = 0</tt>) to the leaf acting agent (<tt>i = n-1</tt>).
    </t>
    <ol>
      <li>For every <tt>i</tt> from <tt>0</tt> to <tt>n-1</tt>, obtain the
        current Capability Manifest <tt>M[i]</tt> for <tt>chain[i].sub</tt>.
        The leaf manifest <tt>M[n-1]</tt> MAY reuse the manifest already
        fetched in Step 9. All ancestor manifests MUST be fetched according to
        the caching rules in Section 10. If any required manifest is
        unavailable, reject with <tt>manifest_invalid</tt>.</li>
      <li>For every obtained manifest <tt>M[i]</tt>, verify
        <tt>M[i].aid == chain[i].sub</tt>, verify the manifest signature using
        <tt>M[i].signature_kid</tt>, verify the DID portion of
        <tt>M[i].signature_kid</tt> equals <tt>M[i].granted_by</tt>, and
        verify that <tt>M[i].expires_at</tt> is in the future. If the AID
        binding, key binding, or signature check fails, reject with
        <tt>manifest_invalid</tt>. If the manifest is expired, reject with
        <tt>manifest_expired</tt>.</li>
      <li>For every requested scope in <tt>aip_scope</tt>, verify that the
        scope is present in the <tt>scope</tt> array of every Principal Token
        in <tt>aip_chain</tt> and is granted by the leaf manifest
        <tt>M[n-1]</tt>. If any requested scope is absent from any chain token
        or from the leaf manifest, reject with
        <tt>insufficient_scope</tt>.</li>
      <li>For every adjacent pair <tt>(M[i-1], M[i])</tt> where
        <tt>i</tt> ranges from <tt>1</tt> to <tt>n-1</tt>, verify that the
        child manifest <tt>M[i]</tt> is a valid attenuation of the parent
        manifest <tt>M[i-1]</tt>. Every scope granted by <tt>M[i]</tt> MUST be
        present in <tt>M[i-1]</tt>, and every child constraint for that scope
        MUST be equal to or more restrictive than the corresponding parent
        constraint. If a child scope is absent from the parent manifest, or if
        a child constraint is more permissive than the parent constraint,
        reject with <tt>insufficient_scope</tt>.</li>
    </ol>
    <t>
      Constraint comparison MUST use the recursive attenuation semantics from
      Rule CO-1, treating the parent manifest constraint as the base value and
      the child manifest constraint as the overlay value. When the synced AIP
      Scope Catalog provides a non-null <tt>constraint_schema</tt>, both parent
      and child constraints MUST first validate against that schema. If schema
      validation fails, reject with <tt>manifest_invalid</tt>. Any violation at
      any adjacent pair is fatal to the chain. For example, a chain where
      <tt>M[0].transactions.max_single_transaction = 250</tt>,
      <tt>M[1].transactions.max_single_transaction = 300</tt>, and
      <tt>M[2].transactions.max_single_transaction = 200</tt> is invalid at
      pair <tt>(M[0], M[1])</tt>, even though <tt>M[2]</tt> is more restrictive
      than <tt>M[1]</tt>.
    </t>
  </section>

  <section anchor="validation-step-9d" numbered="true">
    <name>Step 9d: Grant Tier Conformance</name>
    <t>
      Determine the operation's security Tier as the highest
      <tt>tier</tt> value among all requested <tt>aip_scope</tt> entries in
      the synced AIP Scope Catalog. For every Principal Token payload in
      <tt>aip_chain</tt>, retrieve the registered <tt>grant_tier</tt> from
      the Registry registration metadata for the AID identified by that
      payload's <tt>sub</tt>. The Relying Party MUST reject the request with
      <tt>grant_tier_insufficient</tt> if any participating AID's registered
      <tt>grant_tier</tt> is absent, unrecognised, or not permitted for the
      operation's security Tier.
    </t>
    <t>
      The permitted runtime mappings are: Tier 1 permits <tt>G1</tt>,
      <tt>G2</tt>, or <tt>G3</tt>; Tier 2 permits <tt>G2</tt> or
      <tt>G3</tt>; Tier 3 permits only <tt>G3</tt>. For Tier 2 and Tier 3, the
      root principal DID MUST use the <tt>did:web</tt> method. A
      higher-assurance Grant Tier MAY satisfy a lower security Tier, but a
      lower-assurance Grant Tier MUST NOT satisfy a higher security Tier.
    </t>
    <t>
      If the operation's security Tier is <tt>2</tt> and the Registry's
      <tt>identity_proofing_required_for_tier2</tt> metadata field is
      <tt>true</tt>, Tier 2 permits only <tt>G3</tt> for this Registry.
      In that case, a participating AID registered with <tt>G2</tt> MUST be
      rejected with <tt>grant_tier_insufficient</tt>.
    </t>
  </section>

  <section anchor="validation-step-10" numbered="true">
  <name>Step 10: DPoP Validation (Conditional)</name>
    <t>
      For this step, the operation's security Tier is the highest Tier among
      all requested scopes. A token containing one Tier 2 scope is a Tier 2
      token for DPoP validation.
    </t>
    <t>
      DPoP (Demonstration of Proof-of-Possession) MUST be verified when the
      operation's security Tier is <tt>2</tt> or <tt>3</tt>, when any
      requested scope has <tt>requires_dpop: true</tt> in the synced AIP
      Scope Catalog, or when the requested Registry endpoint requires DPoP
      independently of scope metadata. A Tier 2 or Tier 3 operation requires
      DPoP even if every requested scope has <tt>requires_dpop: false</tt>.
    </t>
    <t>
      If DPoP is required, 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 absolute target URI
        of the current request after removing query and fragment components,
        with URI normalization performed as specified for DPoP in
        <xref target="RFC9449"/>. Because <tt>htu</tt> includes the target
        origin and path, a DPoP proof captured at one Relying Party or
        Registry MUST NOT validate at another Relying Party or Registry
        unless the normalized target URI is identical.</li>
      <li>The <tt>ath</tt> claim is present and equals
        <tt>BASE64URL(SHA-256(token))</tt>, where <tt>token</tt> is the exact
        compact Credential Token or Step Execution Token value from the
        <tt>Authorization</tt> header after removing the authorization scheme
        and surrounding whitespace. When DPoP is required, the authorization
        scheme MUST be <tt>DPoP</tt>. The hash input MUST NOT include the
        authorization scheme, whitespace, or the DPoP proof itself.</li>
      <li>The <tt>iat</tt> claim is within the verifier's accepted DPoP proof
        window. Unless the verifier requires a server-provided DPoP nonce per
        <xref target="RFC9449"/>, the verifier MUST reject proofs whose
        <tt>iat</tt> is more than 300 seconds before receipt time or more
        than 30 seconds after receipt time. When a server-provided nonce is
        required, the verifier MUST validate the <tt>nonce</tt> claim and the
        server-managed nonce lifetime instead of extending acceptance based on
        a client-chosen future <tt>iat</tt>.</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>). The replay cache is local to the verifier and
        MUST retain entries for at least the accepted DPoP proof window, or
        for the server-provided nonce lifetime when nonce validation is used,
        whichever is longer. AIP does not require cross-Relying Party or
        cross-Registry synchronization of DPoP replay caches; cross-target
        replay prevention relies on the mandatory <tt>htm</tt>,
        <tt>htu</tt>, and <tt>ath</tt> checks above.</li>
      <li>The public key in the <tt>jwk</tt> claim matches the effective
        actor's key material. For an agent-issued Credential Token, this is
        the agent key that signed the Credential Token. For a Step Execution
        Token, this is public key material controlled by the actor AID in
        <tt>sub</tt>; it is not the Registry step-execution key that signed
        the SET.</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 to Step Execution Tokens that have already passed
      the SET validation profile defined in this section. Agent-issued
      Credential Tokens MUST NOT be accepted for Approval Envelope step
      execution merely because they contain approval-related claims.
    </t>
    <t>
      The Relying Party MUST call the SET issuer Registry endpoint identified
      by <tt>aip_step_kind</tt>. For <tt>aip_step_kind: "forward"</tt>, call
      <tt>GET /v1/approvals/{aip_approval_id}/steps/{n}</tt> where
      <tt>{n}</tt> is the value of <tt>aip_approval_step</tt> verbatim. For
      <tt>aip_step_kind: "compensation"</tt>, call
      <tt>GET /v1/approvals/{aip_approval_id}/compensation-steps/{n}</tt>
      where <tt>{n}</tt> is the value of
      <tt>aip_compensation_step</tt> verbatim. In either case the index is an
      integer &gt;= 1 and MUST NOT be decremented or adjusted.
      Both <tt>aip_approval_step</tt> and
      <tt>aip_compensation_step</tt> use 1-based indexing; the first forward
      step in an envelope is step 1. This is a change from deprecated
      0-indexed drafts.
      Verify:
    </t>
    <ol>
      <li><strong>Cancellation Status:</strong> If the Registry response
        indicates the Approval Envelope or referenced step has status
        <tt>"cancelled"</tt>, reject with
        <tt>engagement_cancelled</tt>.</li>
      <li><strong>Step Status:</strong> The step's <tt>status</tt> field MUST
        be <tt>"claimed"</tt>. If status is <tt>"pending"</tt>, the step has not been claimed
        and cannot execute; reject with <tt>approval_step_not_claimed</tt>.
        If status is <tt>"completed"</tt>, <tt>"failed"</tt>,
        <tt>"compensated"</tt>, <tt>"skipped"</tt>, or
        <tt>"cancelled"</tt>, reject with
        <tt>approval_step_invalid</tt>.
        Prerequisite failures are reported at claim time by the Registry using
        <tt>approval_step_prerequisites_unmet</tt>; a Relying Party that sees
        an unclaimed step during Step Execution Token verification treats it as
        a state conflict rather than an authorization denial.</li>
      <li><strong>Actor Match:</strong> The returned forward or compensation step's <tt>actor</tt> field MUST
        equal the Step Execution Token's <tt>sub</tt> (the actor AID). Reject
        if mismatch with <tt>approval_step_invalid</tt>.</li>
      <li><strong>Relying Party Match:</strong> The returned 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 13.7. Compare against the returned step's
        <tt>action_hash</tt> field. If mismatch, reject with
        <tt>approval_step_action_mismatch</tt>. This ensures the approving
        principal authorised the exact action being executed.</li>
      <li><strong>Scope Match:</strong> The Step Execution Token
        <tt>aip_scope</tt> set MUST be identical to the returned step's
        <tt>scopes</tt> set. Order is not significant for this comparison,
        but duplicate scope strings are invalid under the SET schema. If the
        sets differ, reject with <tt>approval_step_invalid</tt>.</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>/v1/registry-metadata</tt>
      endpoint.
    </t>
    <t>
      For Tier 3 operations:
    </t>
    <ol>
      <li><strong>mTLS Client Certificate:</strong> The HTTP connection MUST
        use mutual TLS. Validate the client certificate using the Tier 3 mTLS
        Certificate Profile in <xref target="mtls-certificate-profile"/>. The
        effective actor AID is the Credential Token <tt>iss</tt> for
        agent-issued Credential Tokens, or the Step Execution Token
        <tt>sub</tt> for SETs. If the client certificate is absent, reject
        with <tt>mtls_required</tt>. If certificate path validation, key usage,
        EKU, SAN uniqueness, or SAN-to-actor matching fails, 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, or perform the equivalent
        deployment-profile revocation check allowed by
        <xref target="mtls-certificate-profile"/>. If the certificate is
        revoked, reject with <tt>agent_revoked</tt>. If revocation status is
        unavailable, stale, or indeterminate, reject with
        <tt>invalid_token</tt>.</li>
    </ol>
    <dl newline="false">
      <dt>Step 11a. Principal Assertion Presence Check:</dt>
      <dd>For
        Tier 3 agent-issued Credential Tokens, the Credential Token MUST
        contain an <tt>aip_principal_assertion</tt> claim. Absence MUST result
        in rejection with <tt>enterprise_assertion_missing</tt>.</dd>
      <dt>Step 11b. Principal Assertion Signature Verification:</dt>
      <dd>The Relying Party MUST resolve the <tt>iss</tt> claim of the
        <tt>aip_principal_assertion</tt> JWT to an OIDC provider configuration
        document and retrieve the provider's JWKS. The assertion signature
        MUST be verified against the matching <tt>kid</tt> in the JWKS.
        Failure MUST result in rejection with
        <tt>enterprise_assertion_invalid</tt>.</dd>
      <dt>Step 11c. Principal Binding Check:</dt>
      <dd>The <tt>sub</tt> claim of the verified <tt>aip_principal_assertion</tt>
        MUST match the root Principal Token's <tt>principal.id</tt> value in
        <tt>aip_chain[0]</tt>. A mismatch indicates that the agent is
        presenting human context that does not match its registered delegation
        chain and MUST result in rejection with
        <tt>enterprise_assertion_principal_mismatch</tt>.</dd>
    </dl>
    <t>
      If any Tier 3 check fails, reject with the appropriate error code
      (<tt>mtls_required</tt>, <tt>invalid_token</tt>,
      <tt>agent_revoked</tt>, <tt>enterprise_assertion_missing</tt>,
      <tt>enterprise_assertion_invalid</tt>, or
      <tt>enterprise_assertion_principal_mismatch</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 anchor="set-validation-profile" numbered="true">
    <name>Step Execution Token Validation Profile</name>
    <t>
      A Step Execution Token (SET) is a Registry-issued JWT. It MUST NOT be
      validated by applying Credential Token Step 2 and Step 3 literally,
      because its <tt>iss</tt> is the Registry ID and its <tt>kid</tt> is a
      Registry step-execution key URI, not a <tt>did:aip</tt> agent key. A
      Relying Party that receives a token for Approval Envelope step execution
      MUST use this profile before applying Step 10a.
    </t>
    <ol>
      <li>Parse the token as a JWT per Step 1.</li>
      <li>Decode the JOSE header and payload without using either for
        acceptance. The payload MUST conform to
        <tt>step-execution-token.schema.json</tt>. The <tt>iss</tt> claim
        MUST be an HTTPS Registry ID URI, <tt>sub</tt> MUST be the actor AID,
        and <tt>aip_approval_id</tt> and <tt>aip_step_kind</tt> MUST both
        be present. When <tt>aip_step_kind</tt> is <tt>"forward"</tt>,
        <tt>aip_approval_step</tt> MUST be present and
        <tt>aip_compensation_step</tt> MUST be absent. When
        <tt>aip_step_kind</tt> is <tt>"compensation"</tt>,
        <tt>aip_compensation_step</tt> MUST be present and
        <tt>aip_approval_step</tt> MUST be absent. If <tt>aip_registry</tt> is present, it MUST equal
        <tt>iss</tt>. If any of these checks fail, reject with
        <tt>invalid_token</tt>.</li>
      <li>Validate the SET header. <tt>typ</tt> MUST be
        <tt>"AIP-SET+JWT"</tt>, <tt>alg</tt> MUST be <tt>"EdDSA"</tt>, and
        <tt>kid</tt> MUST be an HTTPS URI whose origin matches the
        <tt>iss</tt> Registry ID origin. The protected header MUST also
        contain integer <tt>aip_trv</tt>. If any check fails,
        reject with <tt>invalid_token</tt>.</li>
      <li>Apply the expiration preflight from Step 2a.</li>
      <li>Resolve the issuing Registry trust state from local pinned trust
        records only. The Relying Party MUST have previously completed
        first-contact bootstrap or sequential trust update for the
        <tt>iss</tt> Registry ID under
        <xref target="registry-genesis-well-known-publication"/> and
        <xref target="registry-genesis-registry-key-rotation-planned"/>. It
        MUST select the locally retained Registry Trust Record whose
        <tt>signed.registry_id</tt> equals <tt>iss</tt> and whose
        <tt>signed.version</tt> equals the SET protected header
        <tt>aip_trv</tt>. The Relying Party MUST NOT fetch
        <tt>/v1/registry-metadata</tt> or a Registry Trust Record during
        SET validation to establish trust for the SET being validated. If no
        matching pinned trust record exists, or if the pinned trust record is
        expired and has not already been refreshed by a completed trust-update
        procedure, reject with <tt>registry_untrusted</tt>.</li>
      <li>The SET header <tt>kid</tt> MUST exactly match the
        <tt>keyid</tt> of a JWK listed in
        <tt>active_verification_keys.step_execution</tt> of the accepted
        Registry Trust Record version. If no such key is listed, reject with
        <tt>invalid_token</tt>.</li>
      <li>Verify the SET signature using the matched step-execution JWK. If
        signature verification fails, reject with <tt>invalid_token</tt>.</li>
      <li>Apply Steps 5a through 5f and Step 6 to the SET common claims.
        Step 5g is replaced for SETs by the following SET-specific binding:
        the payload <tt>iss</tt> MUST be the HTTPS Registry ID whose pinned
        Registry Trust Record was selected by protected header
        <tt>aip_trv</tt>; the payload <tt>sub</tt> MUST match the
        <tt>did:aip</tt> ABNF and identify the actor AID; and the protected
        header <tt>kid</tt> MUST identify a Registry step-execution key listed
        in that selected Trust Record. The SET replay cache is keyed by
        <tt>(iss, jti)</tt>, where <tt>iss</tt> is the Registry ID. A Registry
        MUST generate SET <tt>jti</tt> values that are unique within that
        Registry ID across all issued Step Execution Tokens. Registry
        complete/fail endpoints MAY return a stored idempotent response for an
        exact retry of a previously completed transition, but MUST NOT
        re-execute the transition or accept a replayed SET as fresh
        authority.</li>
      <li>Apply Step 6a when required, treating the SET issuer Registry
        <tt>iss</tt> as the Registry whose trust anchor is being verified.
        For Tier 2 or Tier 3 operations, or whenever <tt>aip_registry</tt> is
        present, the root principal DID Document's <tt>AIPRegistry</tt> service
        endpoint MUST match the SET issuer Registry by origin comparison.</li>
      <li>Apply Step 6b to any <tt>aip_engagement_id</tt>, using
        <tt>sub</tt> as the actor AID whose engagement participation is being
        checked.</li>
      <li>Apply Step 7 to the actor AID in <tt>sub</tt>, not to the Registry
        ID in <tt>iss</tt>. Apply Step 8 to <tt>aip_chain</tt>, except that
        Step 8 Post-Check A and B are replaced by the rule that
        <tt>aip_chain[n-1].sub</tt> MUST equal the SET <tt>sub</tt>.</li>
      <li>Apply Steps 9 through 9d using the actor AID in <tt>sub</tt> as the
        effective agent AID for manifest lookup, scope verification,
        delegation inheritance, and Grant Tier Conformance.</li>
      <li>Apply Step 10 when DPoP is required. For SETs, the DPoP proof key
        MUST match public key material controlled by the actor AID in
        <tt>sub</tt>, not the Registry step-execution key that signed the
        SET.</li>
      <li>Apply Step 10a, Step 11 using <tt>sub</tt> as the effective actor
        AID, and Step 12.</li>
    </ol>
    <t>
      A token with <tt>typ: "AIP+JWT"</tt> MUST NOT be accepted as a Step
      Execution Token. A token with <tt>typ: "AIP-SET+JWT"</tt> MUST NOT be
      accepted as an agent-issued Credential Token. This type separation
      prevents substitution between agent-issued and Registry-issued
      authorisation artifacts.
    </t>
  </section>
</section>
    <section anchor="delegation-rules" numbered="true" xml:base="sections/10-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. Subject to the
      Tier-specific Principal DID Method requirements in Section 20,
      <tt>did:web</tt>, <tt>did:key</tt>, or proprietary DID methods can be
      acceptable for Tier 1.
    </t>
    <t>
      The maximum delegation depth is a hard constraint of 10. Delegation
      depth is the zero-based <tt>delegation_depth</tt> value carried in each
      Principal Token and MUST equal that token's index in the
      <tt>aip_chain</tt> array. A valid chain therefore contains at most 11
      Principal Tokens: indexes 0 through 10. An agent MUST NOT delegate to a
      sub-agent if doing so would create a token with
      <tt>delegation_depth</tt> greater than 10 or an <tt>aip_chain</tt>
      longer than 11 elements.
    </t>
    <t>
      An agent MUST NOT delegate to itself. For every delegated Principal
      Token with <tt>delegation_depth</tt> greater than 0, the
      <tt>delegated_by</tt> AID MUST identify the immediate parent agent and
      MUST NOT equal the token's <tt>sub</tt> AID. Relying Parties enforce
      this prohibition during Step 8e and Step 8g of Credential Token
      validation.
    </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 greater than 3 and no greater
      than 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 5.10 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 equal to or more restrictive than the
      corresponding values in the parent's manifest.
    </t>
    <t>
      Constraint comparison uses the recursive attenuation semantics from Rule
      CO-1. Numeric caps and thresholds use the ordering defined for that field;
      booleans, arrays, strings, enums, omitted fields, and unknown types follow
      the type-specific CO-1 rules.
    </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
      using the ordered Step 9c algorithm in the Credential Token validation
      algorithm (see <xref target="validation-step-9c"/>). 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, the Relying Party MUST fetch a fresh manifest.
    </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-9c"/>) 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 anchor="delegated-identity-chaining-a2a" numbered="true">
    <name>Delegated Identity Chaining for A2A Workflows</name>
    <t>
      Agent-to-agent asynchronous workflows require Relying Parties to
      distinguish the acting agent from the originating agent that delegated
      the work. When Agent B makes a tool call on behalf of Agent A, Agent B's
      Credential Token MUST carry both identities using the profile in this
      section.
    </t>
    <ol type="%d">
      <li>Agent B's Credential Token <tt>aip_chain</tt> array MUST include
      Agent A's Principal Token as an additional chain element after Agent B's
      own root Principal Token.</li>
      <li>The <tt>aip_scope</tt> in Agent B's Credential Token MUST be a
      strict subset of the scopes granted in Agent A's Capability Manifest.
      Rule D-1 enforces this at delegation time; this rule applies the same
      attenuation requirement to the presented Credential Token.</li>
      <li>When Agent B is acting in a delegated A2A context, Agent B's
      Credential Token MUST include the <tt>aip_originator_aid</tt> claim.
      The value of <tt>aip_originator_aid</tt> MUST be Agent A's AID: the AID
      of the agent that initiated the workflow and delegated the task to
      Agent B. The <tt>aip_originator_aid</tt> value MUST conform to the
      <tt>did:aip</tt> ABNF defined in <xref target="aid-syntax"/>. The
      <tt>aip_originator_aid</tt> value MUST NOT equal the <tt>sub</tt> claim
      of Agent B's Credential Token. When <tt>aip_originator_aid</tt> is
      present but does not meet these constraints, the Relying Party MUST
      reject the request with <tt>a2a_originator_invalid</tt>.</li>
      <li>Relying Parties MAY use <tt>aip_originator_aid</tt> to apply
      originator-level access policies and to index audit events by the
      originating agent. Full chain validation under
      <xref target="validation-step-8"/> remains REQUIRED for every accepted
      Credential Token.</li>
      <li>
        <t>Relying Parties receiving a Credential Token that contains
        <tt>aip_originator_aid</tt> MUST perform the following validation:</t>
        <ol type="a">
          <li>Verify <tt>aip_originator_aid</tt> conforms to the
          <tt>did:aip</tt> ABNF in <xref target="aid-syntax"/>. If not,
          reject with <tt>a2a_originator_invalid</tt>.</li>
          <li>Verify <tt>aip_originator_aid</tt> does not equal the
          Credential Token <tt>sub</tt> claim. If equal, reject with
          <tt>a2a_originator_invalid</tt>.</li>
          <li>Verify <tt>aip_originator_aid</tt> appears as the
          <tt>sub</tt> of <tt>aip_chain[0]</tt> or as a registered AID in the
          validated delegation chain, so that Agent A is demonstrably the
          originator of the chain. If no such binding exists, reject with
          <tt>a2a_originator_invalid</tt>.</li>
          <li>Relying Parties MAY use <tt>aip_originator_aid</tt> to apply
          originator-level access policies and audit classification for the
          already validated request context. Every new Credential Token or
          Step Execution Token presented on a downstream call MUST still pass
          the full validation algorithm, including
          <xref target="validation-step-8"/>.</li>
        </ol>
      </li>
    </ol>
    <t>
      This profile aligns with RFC 8693 actor-token semantics: the signing
      Credential Token identifies the current actor, while
      <tt>aip_originator_aid</tt> and the additional Principal Token context
      allow enterprise audit systems to attribute tool calls to the
      originating agent chain.
    </t>
    <t>
      Relying Parties that support enterprise audit logging SHOULD record
      <tt>aip_originator_aid</tt> in their audit trail as the chain originator
      for all A2A tool calls.
    </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, its Credential Tokens and Step Execution Tokens MUST
    be rejected as soon as the applicable revocation check observes the active
    revocation. Tier 2 and Tier 3 checks are live and fail closed on Registry
    unavailability. Tier 1 uses bounded-staleness CRLs, so enforcement can
    lag until the next fresh CRL within the SLA defined in this section.
  </t>

  <section anchor="revocation-object" numbered="true">
  <name>Revocation Object</name>
    <t>
      Revocation is performed by submitting a signed Revocation Object to
      <tt>POST /v1/revocations</tt>. The Revocation Object fields, required
      members, type enum, and signing input are defined only in Section 5.7.
      This section defines processing effects for accepted Revocation Objects;
      it does not define a second Revocation Object schema.
    </t>
    <t>
      A <tt>principal_revoke</tt> with a Principal DID target invalidates
      every Credential Token and Step Execution Token whose
      <tt>aip_chain[0].principal.id</tt> equals that Principal DID. A
      <tt>principal_revoke</tt> with an AID target invalidates every token
      whose <tt>aip_chain</tt> depends on that AID's principal authorisation.
      This effect is independent of <tt>propagate_to_children</tt>.
      The <tt>propagate_to_children</tt> flag controls whether the Registry
      also materialises descendant Revocation Objects; it does not limit the
      validity effect of <tt>principal_revoke</tt>.
    </t>
  </section>

  <section anchor="revocation-submission-validation" numbered="true">
  <name>Revocation Submission Validation</name>
    <t>
      A conformant Registry MUST implement <tt>POST /v1/revocations</tt> for
      externally submitted Revocation Objects. The request body MUST be a JSON
      object conforming to <tt>revocation-object.schema.json</tt>. Unless a
      check below specifies a more precise error code, failed validation MUST
      be rejected with <tt>revocation_invalid</tt>.
    </t>

    <t>
      The Registry MUST perform the following checks in order:
    </t>
    <ol>
      <li>The request MUST use <tt>Content-Type: application/json</tt>. The
      object MUST contain all required Revocation Object members, including
      <tt>kid</tt>. If <tt>propagate_to_children</tt> is absent, the Registry
      MUST process it as <tt>false</tt>.</li>
      <li>If <tt>revocation_id</tt> was already accepted, the Registry MUST
      compare the Section 2.1 canonical JSON serialization of the submitted
      object to the stored object. If they are identical, the Registry MUST
      return HTTP 200 with the stored Revocation Object and MUST NOT apply side
      effects again. If they differ, the Registry MUST reject with
      <tt>revocation_conflict</tt>.</li>
      <li>The <tt>timestamp</tt> value MUST be a valid UTC date-time and MUST
      NOT be more than 300 seconds in the future relative to the Registry's
      current clock. Registries MAY accept older timestamps, but the
      <tt>revocation_id</tt> uniqueness check remains mandatory.</li>
      <li>The <tt>reason</tt> value MUST be one of the reason codes in the
      Revocation Object schema. Externally submitted Revocation Objects MUST
      NOT use <tt>parent_revoked</tt>, <tt>heartbeat_timeout</tt>, or
      <tt>lifecycle_expired</tt>; those reason codes are reserved for
      Registry-generated Revocation Objects.</li>
      <li>For <tt>full_revoke</tt>, <tt>scope_revoke</tt>, and
      <tt>delegation_revoke</tt>, <tt>target_id</tt> MUST be a registered
      <tt>did:aip</tt> AID in the authoritative Registry. Unknown targets
      MUST be rejected with <tt>unknown_aid</tt>. For
      <tt>principal_revoke</tt>, <tt>target_id</tt> MUST be either a
      registered AID or a Principal DID associated with at least one stored
      registration in that Registry.</li>
      <li>For <tt>scope_revoke</tt>, <tt>scopes_revoked</tt> MUST be present
      and non-empty, and every entry MUST be an active synced AIP Scope
      Catalog scope or an accepted local/private extension scope that is
      currently granted by the target's effective Capability Manifest. Invalid
      scope entries MUST be rejected with <tt>invalid_scope</tt>. For all
      other revocation types, <tt>scopes_revoked</tt> MUST be absent.</li>
      <li>The Registry MUST construct the target authorization chain from its
      registration records. That chain consists of the target's root Principal
      DID and every ancestor AID recorded through the parent-child delegation
      index. For <tt>full_revoke</tt>, <tt>scope_revoke</tt>, and
      <tt>delegation_revoke</tt>, <tt>issued_by</tt> MUST equal either the
      target's root Principal DID or an ancestor AID in that chain. For
      <tt>principal_revoke</tt> targeting a Principal DID,
      <tt>issued_by</tt> MUST equal that Principal DID. For
      <tt>principal_revoke</tt> targeting an AID, <tt>issued_by</tt> MUST
      equal the target AID's root Principal DID. Unauthorized issuers MUST be
      rejected with <tt>revocation_unauthorized</tt>.</li>
      <li>For a DID issuer, <tt>kid</tt> MUST be a DID URL whose DID portion
      equals <tt>issued_by</tt>. The Registry MUST resolve the DID, locate the
      Ed25519 verification method named by <tt>kid</tt>, and verify the
      Revocation Object signature over the Section 5.7 signing input. For a
      <tt>did:aip</tt> issuer, the key lookup MUST use the authoritative
      Registry public-key endpoint for the referenced AID and key version. If
      the key cannot be resolved, is not Ed25519, is not controlled by
      <tt>issued_by</tt>, or the signature fails, the Registry MUST reject
      with <tt>revocation_invalid</tt>.</li>
    </ol>

    <t>
      Registry-generated Revocation Objects are not externally submitted
      through the public <tt>POST /v1/revocations</tt> endpoint. For
      <tt>parent_revoked</tt>, <tt>heartbeat_timeout</tt>, and
      <tt>lifecycle_expired</tt>, the Registry MUST set <tt>issued_by</tt> to
      its <tt>registry_id</tt>, set <tt>kid</tt> to a JWK listed in
      <tt>active_verification_keys.crl</tt> of the Registry Trust Record
      current at the Revocation Object <tt>timestamp</tt>, and sign the object
      with the corresponding private key.
    </t>

    <t>
      Acceptance MUST be atomic. On success, the Registry MUST store the
      Revocation Object immutably, update live revocation status, schedule CRL
      publication within the 15-minute freshness window defined in
      <xref target="crl"/>, and return HTTP 201 with
      <tt>Content-Type: application/json</tt> and the stored
      Revocation Object. If <tt>propagate_to_children</tt> is <tt>true</tt>,
      the Registry MUST recursively materialise child Revocation Objects with
      reason <tt>parent_revoked</tt> within 15 seconds. For
      <tt>principal_revoke</tt>, token invalidation does not depend on
      materialised child objects; propagation only adds audit and index
      records.
    </t>
  </section>

  <section anchor="crl" numbered="true">
  <name>Certificate Revocation List (CRL)</name>
    <t>
      The Registry MUST expose a canonical origin CRL endpoint at
      <tt>GET /v1/crl</tt> and MUST publish the preferred CRL retrieval URI
      as <tt>endpoints.crl</tt> in both <tt>/v1/registry-metadata</tt>
      and the signed Registry Trust Record. The published
      <tt>endpoints.crl</tt> value MAY be <tt>/v1/crl</tt> or an absolute
      HTTPS URI for a CDN or distributed object store. Relying Parties MUST
      retrieve CRL documents from the accepted Registry Trust Record's
      <tt>signed.endpoints.crl</tt> value rather than assuming
      <tt>registry_id + "/v1/crl"</tt>.
    </t>
    <t>
      The CRL MUST be updated within 15 minutes of a new Revocation
      Object being accepted. The published CRL retrieval URI MUST be served
      from a CDN or distributed infrastructure. CDN and object-store
      distribution is not a trust anchor: CRL documents MUST be signed by a
      JWK listed in
      <tt>active_verification_keys.crl</tt> of the Registry
      Trust Record version current at issuance time.
    </t>
    <t>
      The CRL is an AIP signed JSON document, not an X.509 CRL. A successful
      CRL response MUST use <tt>Content-Type: application/aip-crl+json</tt>
      and MUST conform to <tt>crl.schema.json</tt>. The top-level object MUST
      contain <tt>signed</tt> and <tt>signatures</tt>. The <tt>signed</tt>
      object MUST include <tt>registry_id</tt>, <tt>trust_record_version</tt>,
      <tt>crl_id</tt>, <tt>issued_at</tt>, <tt>next_update</tt>,
      <tt>sequence</tt>, <tt>publication_mode</tt>,
      <tt>revocation_count</tt>, and <tt>revocations</tt>. The
      <tt>publication_mode</tt> value MUST be <tt>"complete"</tt>,
      <tt>"index"</tt>, or <tt>"segment"</tt>. In <tt>"complete"</tt> mode,
      the <tt>revocations</tt> array contains the full signed Revocation
      Objects active for bounded-staleness validation at <tt>issued_at</tt>.
      In <tt>"index"</tt> mode, the <tt>revocations</tt> array MUST be empty
      and the signed object MUST contain <tt>segments</tt> as defined in
      <xref target="crl-response-registry"/>. In <tt>"segment"</tt> mode, the <tt>revocations</tt> array
      contains only the Revocation Objects assigned to that segment, and the
      signed object MUST contain <tt>segment_id</tt>.
    </t>
    <t>
      The CRL signing input is the RFC 8785 JSON Canonicalization Scheme (JCS)
      serialization of the <tt>signed</tt> object only. Each signature entry
      MUST contain <tt>keyid</tt> and <tt>sig</tt>. The <tt>keyid</tt> MUST
      match a JWK <tt>keyid</tt> in
      <tt>active_verification_keys.crl</tt> for the Registry Trust Record whose
      <tt>signed.version</tt> equals the CRL
      <tt>signed.trust_record_version</tt>. The signature algorithm is
      EdDSA over Ed25519. A Relying Party MUST reject an unsigned, malformed,
      or unverifiable CRL and MUST NOT treat CDN integrity, TLS termination, or
      object-store metadata as a substitute for this signature verification.
    </t>
    <t>
      CRL consumers MUST verify that <tt>signed.registry_id</tt> equals the
      accepted Registry Trust Record's <tt>signed.registry_id</tt>, that
      <tt>signed.trust_record_version</tt> identifies an accepted Registry
      Trust Record, and that at least one signature verifies under that trust
      record's <tt>active_verification_keys.crl</tt>. The
      <tt>next_update</tt> timestamp MUST be no later than 15 minutes after
      <tt>issued_at</tt>. Relying Parties MUST NOT use a CRL for new token
      validations after <tt>next_update</tt>; if no fresh verifiable CRL is
      available when one is required, validation MUST fail with
      <tt>registry_unavailable</tt>.
    </t>
    <t>
      CRL documents MUST include active <tt>principal_revoke</tt> entries by
      <tt>target_id</tt>. For Tier 1 validation, Relying Parties MUST check
      <tt>principal_revoke</tt> entries whose <tt>target_id</tt> equals the
      token's root Principal DID and entries whose <tt>target_id</tt> equals
      any AID in the token's validated <tt>aip_chain</tt>. A matching
      <tt>principal_revoke</tt> is equivalent to <tt>agent_revoked</tt> for
      that token.
    </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> The Credential Token TTL is
      the effective catalog-derived TTL defined in <xref target="token-issuance"/>. Relying
      Parties validate revocation status at request validation time using a
      verifiable CRL whose <tt>next_update</tt> has not passed. A Relying Party
      MUST refresh its cached CRL before using it for new validations after
      <tt>next_update</tt>.
    </t>

    <t>
      <strong>Tier 2 - Real-time revocation:</strong> Applies when the
      highest synced AIP Scope Catalog <tt>tier</tt> among requested scopes is
      2. Real-time Registry check on EVERY request. MUST NOT cache
      revocation status. DPoP MUST be verified for the request regardless of
      per-scope <tt>requires_dpop</tt> metadata. If Registry unreachable:
      MUST deny and return registry_unavailable.
    </t>
    <t>
      The live Tier 2 lookup uses
      <tt>GET /v1/agents/{aid}/revocation</tt>. A registered AID returns
      HTTP 200 whether or not it is revoked. The response body is the
      Revocation Status response defined in
      <xref target="agent-revocation-status-registry"/>. Unknown AIDs return
      <tt>unknown_aid</tt> (HTTP 404).
    </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.
      A <tt>principal_revoke</tt> invalidates dependent tokens regardless of
      this flag; propagation only controls whether descendant Revocation
      Objects are written for indexing, CRL compactness, and auditability.
    </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 start the first push delivery attempt within 5 seconds of the event commit time defined in <xref target="rpnp-delivery"/></li>
        <li>Push payloads MUST be signed by a JWK listed in <tt>active_verification_keys.notifications</tt> of the Registry Trust Record version current at issuance time</li>
        <li>Subscriber authentication MUST be verified at subscription time</li>
      </ul>

      <t>
        For subscribing Relying Parties, the effective revocation
        notification window is the RPNP first-attempt latency (at most 5 seconds)
        rather than the CRL refresh interval. RPNP does not
        replace CRL; it supplements it. Confirmed delivery depends on
        subscriber availability and the retry rules in <xref target="rpnp-delivery"/>.
      </t>
    </section>

    <section anchor="rpnp-subscription" numbered="true">
  <name>Subscription</name>
      <t>
        A Relying Party subscribes by calling POST /v1/subscriptions:
      </t>
      <t>
        The request body MUST conform to the
        <tt>subscription_request</tt> definition in
        <tt>rpnp.schema.json</tt>. On success, the Registry assigns a
        <tt>subscription_id</tt> with the pattern
        <tt>sub:&lt;uuid-v4&gt;</tt> and returns HTTP 201 with a body
        conforming to the <tt>subscription_response</tt> definition in
        <tt>rpnp.schema.json</tt>.
      </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>Array containing one or more of <tt>full_revoke</tt>, <tt>scope_revoke</tt>, <tt>delegation_revoke</tt>, <tt>principal_revoke</tt>, and <tt>engagement_cancelled</tt></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</td>
                  <td>REQUIRED</td>
                  <td>Base64url-encoded shared secret; minimum 32 bytes of entropy</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 bound
        to <tt>subscriber_did</tt>. A DPoP proof that only proves possession
        of an arbitrary key is not sufficient subscription authentication.
        If the DPoP proof is absent, malformed, unverifiable, replayed,
        not bound to <tt>subscriber_did</tt>, or not bound to the current
        request target, the Registry MUST reject with
        <tt>subscription_auth_required</tt>.
      </t>
      <t>
        The Registry MUST accept exactly the following RPNP subscription
        authentication profiles:
      </t>
      <ol type="%d">
        <li>
          <t><strong>DID-bound DPoP:</strong> The DPoP proof protected header
          MUST contain a public verification key and a DID URL key identifier
          in <tt>jwk.kid</tt>. The DID portion of <tt>jwk.kid</tt> MUST equal
          <tt>subscriber_did</tt>. The Registry MUST resolve
          <tt>subscriber_did</tt> and verify that <tt>jwk.kid</tt> identifies
          an Ed25519 verification method controlled by that DID. For
          <tt>did:web</tt>, resolution uses the DID method's normal DID
          Document resolution. For <tt>did:aip</tt>, resolution uses the
          authoritative AIP Registry public-key endpoint for the referenced
          AID and key identifier. The public key in the DPoP header MUST be
          byte-for-byte equivalent to the resolved verification method, and
          the DPoP proof signature MUST verify under that key.</t>
        </li>
        <li>
          <t><strong>Authorization-bound DPoP:</strong> The request MAY
          include <tt>Authorization: DPoP &lt;token&gt;</tt>. In that case,
          the Registry MUST validate the token using Section 9 with the
          subscription endpoint as the audience, MUST validate the DPoP proof
          using Section 9 Step 10 including the <tt>ath</tt> check, and MUST
          verify that the token issuer, token subject, and leaf
          <tt>aip_chain</tt> subject all equal <tt>subscriber_did</tt>. This
          profile is available only when <tt>subscriber_did</tt> is a
          <tt>did:aip</tt> AID.</t>
        </li>
      </ol>
      <t>
        For either profile, the Registry MUST verify <tt>htm</tt>,
        <tt>htu</tt>, <tt>iat</tt>, and <tt>jti</tt> replay state using the
        DPoP validation rules in Section 9 Step 10. In DID-bound DPoP without
        an <tt>Authorization</tt> header, the <tt>ath</tt> claim MUST be
        absent. If an <tt>Authorization</tt> header is present, <tt>ath</tt>
        MUST be present and valid for that exact token.
      </t>
      <t>
        The Registry MUST authorize the requested <tt>scope_filter</tt>
        against the authenticated <tt>subscriber_did</tt>. If
        <tt>scope_filter</tt> is <tt>"all"</tt> and the subscriber is not
        explicitly authorized by Registry policy for global revocation
        notifications, the Registry MUST reject the request with
        <tt>subscription_scope_forbidden</tt>. If <tt>scope_filter</tt> is
        <tt>"aid"</tt> or <tt>"principal"</tt>, every value in
        <tt>targets</tt> MUST be within the subscriber's authorized
        observation set; otherwise, the Registry MUST reject with
        <tt>subscription_scope_forbidden</tt>.
      </t>
      <t>
        The Registry MUST enforce configured per-subscriber and global active
        subscription quotas before accepting a new subscription. If accepting
        the request would exceed any applicable quota, the Registry MUST
        reject with <tt>subscription_limit_exceeded</tt>. The Registry SHOULD
        include <tt>Retry-After</tt> when quota exhaustion is temporary.
      </t>
      <t>
        The <tt>hmac_secret</tt> value is shared secret material used by the
        Registry to authenticate push event delivery. The value MUST be
        transmitted only over TLS. The Registry MUST retain the secret, or a
        reversibly encrypted form of the secret, for the lifetime of the
        subscription so it can compute delivery HMACs. A Registry MUST NOT
        store only a one-way hash of this value while the subscription remains
        active. Registries MAY additionally store a hash of the secret for
        audit or lookup purposes, but that hash is not sufficient to sign
        delivery requests.
      </t>
      <t>
        <tt>GET /v1/subscriptions/{id}</tt> MUST return the current
        <tt>subscription_response</tt> object. The <tt>status</tt> field MUST
        be one of <tt>active</tt>, <tt>degraded</tt>, <tt>cancelled</tt>, or
        <tt>expired</tt>. Status responses MUST include
        <tt>consecutive_failures</tt>, <tt>last_delivery_attempt_at</tt>,
        <tt>last_success_at</tt>, <tt>next_retry_at</tt>, and
        <tt>last_delivery_status</tt>, using null for timestamp fields that
        have not yet occurred. <tt>DELETE /v1/subscriptions/{id}</tt> MUST
        atomically transition an active or degraded subscription to
        <tt>cancelled</tt> and return the updated
        <tt>subscription_response</tt>.
      </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 a JWK listed in
        <tt>active_verification_keys.notifications</tt> of the
        Registry Trust Record version current at issuance time.
      </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 notification key ID</td>
                </tr>
                <tr>
                  <td>aip_trv</td>
                  <td>Integer Registry Trust Record version whose <tt>active_verification_keys.notifications</tt> contains <tt>kid</tt></td>
                </tr>
              </tbody>
            </table>

      <t>
        The protected <tt>aip_trv</tt> header is used to select an already
        pinned Registry Trust Record for the event issuer. Subscribers MUST
        NOT fetch new trust state during push-event validation to establish
        trust for that event.
      </t>

                        <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 ID</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>exp</td>
                  <td>Unix timestamp; MUST be no more than 300 seconds after iat</td>
                </tr>
                <tr>
                  <td>jti</td>
                  <td>UUID v4; unique event ID</td>
                </tr>
                <tr>
                  <td>subscription_id</td>
                  <td>Subscription identifier receiving this push event</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 JWT payload MUST conform to the <tt>push_payload</tt> definition
        in <tt>rpnp.schema.json</tt>. The request MUST include an
        <tt>X-AIP-Signature</tt> header using this exact syntax:
      </t>
      <artwork type="text">
X-AIP-Signature: &lt;sig-header&gt;

sig-header = "v1;t=" timestamp ";kid=" subscription-id ";h=" hmac
      </artwork>
      <t>
        The HMAC input is the ASCII decimal <tt>t</tt> value, followed by a
        period (<tt>.</tt>), followed by the exact request body octets. The
        <tt>h</tt> value is <tt>BASE64URL(HMAC-SHA256(hmac_secret, input))</tt>
        without padding. The <tt>kid</tt> value MUST equal the JWT
        <tt>subscription_id</tt>. The subscriber MUST verify the Registry JWT
        signature, the HMAC, and the subscription binding before accepting the
        event.
      </t>
      <t>
        Subscribers MUST reject a push event if the header timestamp
        <tt>t</tt> is more than 300 seconds from the subscriber's current
        clock, if the JWT <tt>exp</tt> is in the past, if <tt>exp - iat</tt>
        exceeds 300 seconds, or if the <tt>jti</tt> has already been accepted
        for the same <tt>subscription_id</tt>. Subscribers MUST retain replay
        state keyed by <tt>(subscription_id, jti)</tt> at least until the
        later of the JWT <tt>exp</tt> time and 300 seconds after receipt.
      </t>
    </section>

    <section anchor="rpnp-delivery" numbered="true">
  <name>Delivery Guarantees</name>
      <t>
        RPNP provides at-least-once best-effort push delivery. It does not
        provide exactly-once delivery, and subscribers MUST use the
        <tt>jti</tt> replay rules in this section to make event processing
        idempotent.
      </t>
      <ol>
        <li>
          The Registry MUST record an <tt>event_committed_at</tt> instant
          using its reliable UTC clock when the state transition that creates
          the push event is durably committed. For revocation events, this is
          the instant at which the Revocation Object is accepted and live
          revocation status is committed. For engagement cancellation events,
          this is the instant at which the cancellation state transition is
          committed.
        </li>
        <li>
          The Registry MUST start the first HTTPS POST delivery attempt to
          each active matching subscription no later than 5 seconds after
          <tt>event_committed_at</tt>. The interval ends when the Registry
          begins sending the request to the subscriber's
          <tt>webhook_uri</tt>. This 5-second requirement applies to the first
          delivery attempt, not to confirmed successful receipt.
        </li>
        <li>
          The Registry MUST set <tt>last_delivery_attempt_at</tt> to the
          Registry UTC time at which each delivery attempt begins. If the first
          attempt starts more than 5 seconds after
          <tt>event_committed_at</tt>, the Registry MUST record
          <tt>last_delivery_status</tt> as
          <tt>first_attempt_sla_missed</tt> until a later delivery attempt
          overwrites it with a more recent status.
        </li>
        <li>
          On delivery failure, meaning timeout or any non-2xx HTTP response,
          the Registry MUST perform at least three retry attempts after the
          initial delivery attempt, using exponential backoff intervals of 1s,
          2s, and 4s as the minimum retry schedule. The Registry MAY add jitter
          or continue retrying after those attempts, but MUST NOT schedule the
          first three retries later than those intervals unless the subscription
          has expired or been cancelled.
        </li>
        <li>
          A delivery attempt MUST be treated as timed out if the subscriber
          does not return a final HTTP response within 5 seconds after the
          Registry finishes sending the request body, unless the Registry
          applies a shorter deployment-profile timeout.
        </li>
        <li>
          After 3 consecutive failures, mark the subscription
          <tt>degraded</tt>, update <tt>consecutive_failures</tt>,
          <tt>last_delivery_attempt_at</tt>, <tt>last_delivery_status</tt>,
          and <tt>next_retry_at</tt>, and continue to make the event available
          through CRL or live revocation status. Subscribers MUST fall back to
          CRL or live Registry checks while the subscription is degraded.
        </li>
        <li>On any successful 2xx delivery, reset
          <tt>consecutive_failures</tt> to 0, set
          <tt>last_success_at</tt>, clear <tt>next_retry_at</tt>, and restore
          <tt>status</tt> to <tt>active</tt> unless the subscription has
          expired or been cancelled.</li>
      </ol>
    </section>
  </section>
</section>
    <section anchor="principal-grant" numbered="true" xml:base="sections/12-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 <xref target="RFC6749"/>, 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 Principal 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;|                  |
         | (agent_aid,         |                  |
         |  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. For G2, the signed
      request MUST identify the deployer through the <tt>deployer_did</tt>
      field and the signature key MUST be bound to that DID. The nonce MUST be
      cryptographically random and MUST contain at least 128 bits of
      entropy.
    </t>
    <t>
      The request MUST also include <tt>deployer_name</tt> for consent UI
      display. The <tt>deployer_name</tt> value is human-readable metadata
      only; it is not a trust anchor and MUST NOT replace verification of
      <tt>deployer_did</tt> and the signed GrantRequest envelope.
    </t>

    <t><strong>GrantRequest Fields:</strong></t>

    <dl newline="true" spacing="normal">
                  <dt><tt>grant_request_id</tt> (string; REQUIRED)</dt>


                  <dd>pattern: gr:&lt;UUIDv4-lowerhex&gt;</dd>


                  <dt><tt>aip_version</tt> (string; REQUIRED)</dt>


                  <dd>AIP protocol compatibility version; MUST be "0.3"; not the Internet-Draft revision</dd>


                  <dt><tt>agent_aid</tt> (string; REQUIRED)</dt>


                  <dd>MUST match did:aip ABNF; pre-derived AID of the agent being authorised; Principal Token sub MUST equal this value</dd>


                  <dt><tt>agent_name</tt> (string; REQUIRED)</dt>


                  <dd>maxLength: 64; displayed in consent UI</dd>


                  <dt><tt>agent_type</tt> (string; REQUIRED)</dt>


                  <dd>registered namespace value</dd>


                  <dt><tt>model.provider</tt> (string; REQUIRED)</dt>


                  <dd>displayed in consent UI</dd>


                  <dt><tt>model.model_id</tt> (string; REQUIRED)</dt>


                  <dd>displayed in consent UI</dd>


                  <dt><tt>requested_capabilities</tt> (object; REQUIRED)</dt>


                  <dd>Capability Manifest capabilities sub-schema</dd>


                  <dt><tt>purpose</tt> (string; REQUIRED)</dt>


                  <dd>minLength: 1; maxLength: 512</dd>


                  <dt><tt>delegation_valid_for_seconds</tt> (integer; REQUIRED)</dt>


                  <dd>requested maximum delegation lifetime; min: 300; max: 31536000; approved value MUST NOT exceed this value</dd>


                  <dt><tt>nonce</tt> (string; REQUIRED)</dt>


                  <dd>minLength: 22; cryptographically random</dd>


                  <dt><tt>request_expires_at</tt> (string; REQUIRED)</dt>


                  <dd>ISO 8601 UTC</dd>


                  <dt><tt>callback_uri</tt> (string; CONDITIONAL)</dt>


                  <dd>REQUIRED for Web Redirect and G3 OAuth authorization requests; MUST use HTTPS; for G3, MUST equal the OAuth redirect_uri</dd>


                  <dt><tt>state</tt> (string; CONDITIONAL)</dt>


                  <dd>REQUIRED for G3 OAuth authorization requests; echoed unchanged and MUST equal the OAuth state parameter</dd>


                  <dt><tt>deployer_did</tt> (string; REQUIRED)</dt>


                  <dd>W3C DID of the deployer; used to verify signed GrantRequests and displayed in consent UI</dd>


                  <dt><tt>deployer_name</tt> (string; REQUIRED)</dt>


                  <dd>minLength: 1; maxLength: 128; displayed alongside deployer_did; display metadata only, not a trust anchor</dd>


                  <dt><tt>deployer_public_key</tt> (object; OPTIONAL)</dt>


                  <dd>JWK key hint only; MUST match the verification method resolved from deployer_did and MUST NOT replace DID resolution</dd>


                </dl>

    <t>
      The deployer MUST generate the agent's AID before constructing
      the GrantRequest and MUST include that value as <tt>agent_aid</tt>.
      The Principal Token produced from this grant MUST use the original
      GrantRequest <tt>agent_aid</tt> as its <tt>sub</tt> claim.
    </t>

    <t>
      <strong>Signed GrantRequest envelope:</strong> A GrantRequest sent
      directly to a Principal Wallet using the Web Redirect or QR Code
      binding MUST be carried as a compact JWS whose payload is the UTF-8
      JSON serialization of the GrantRequest object. The protected JWS
      header MUST contain <tt>typ</tt> equal to
      <tt>"aip-grant+jws"</tt>, <tt>alg</tt> equal to
      <tt>"EdDSA"</tt>, and <tt>kid</tt> as a DID URL. The DID portion of
      <tt>kid</tt> MUST equal the GrantRequest <tt>deployer_did</tt>. The
      referenced verification method MUST resolve from the
      <tt>deployer_did</tt> DID Document, MUST be controlled by
      <tt>deployer_did</tt>, and MUST be an Ed25519 signing key. If
      <tt>deployer_public_key</tt> is present, it is a cache hint only; the
      Principal Wallet MUST verify that it is byte-for-byte equivalent to
      the resolved verification method before using it and MUST NOT trust a
      self-supplied key that is not present in the resolved DID Document.
    </t>
    <t>
      The same signed GrantRequest envelope format is used by the G3 OAuth
      binding. In that case, the compact JWS is carried in the
      <tt>aip_grant_request</tt> authorization request parameter and is
      verified by the Registry acting as OAuth Authorization Server before
      any authentication or consent ceremony is started.
    </t>
  </section>

  <section anchor="grant-consent" numbered="true">
  <name>Principal 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>
      The Principal Wallet MUST use its own reliable UTC clock for
      <tt>request_expires_at</tt> checks and MAY allow a 30-second clock
      skew tolerance. If the Principal Wallet's current time is more than 30
      seconds after <tt>request_expires_at</tt>, the request is expired. If
      the Principal Wallet cannot determine reliable current UTC time, it MUST reject
      with <tt>grant_request_invalid</tt> rather than issuing a back-dated
      or future-dated Principal Token.
    </t>

    <t>
      <strong>Signed request checks:</strong> For a G2 GrantRequest, the
      Principal Wallet MUST verify the signed GrantRequest envelope before
      displaying the consent UI. The Principal Wallet MUST reject with
      <tt>grant_request_invalid</tt> and MUST NOT show the consent UI if
      the request is unsigned, if <tt>deployer_did</tt> is absent, if the
      JWS <tt>kid</tt> DID does not equal <tt>deployer_did</tt>, if the
      deployer DID cannot be resolved, if the referenced key is not an
      Ed25519 signing key controlled by <tt>deployer_did</tt>, or if the
      JWS signature verification fails.
    </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 using trusted AIP Scope Catalog display metadata</li>
      <li>Delegation validity - requested duration and the approximate expiry computed from delegation_valid_for_seconds using the Principal Wallet's current UTC clock</li>
        <li><strong>Destructive Operations:</strong> Any scope whose active
        AIP Scope Catalog entry has <tt>destructive: true</tt> MUST be
        highlighted in the UI using the mandatory two-step confirmation flow
        required for Principal Wallet conformance in
        <xref target="conformance-classes"/>.</li>
    </ol>

    <t>
      Before displaying the consent UI, the Principal Wallet MUST resolve
      <tt>requested_capabilities</tt> to the concrete requested scope
      identifiers using the scope-to-capability rules in
      <xref target="field-constraints"/> and any explicit private extension
      policy accepted by the Principal Wallet. The Principal Wallet MUST use a trusted immutable
      AIP Scope Catalog Snapshot for this resolution. For each requested
      scope, the Principal Wallet MUST find an <tt>active</tt> or
      <tt>experimental</tt> scope entry and MUST find complete trusted
      <tt>display</tt> metadata for the Principal Wallet's selected locale or for the
      entry's <tt>display.default_locale</tt>. If no trusted catalog snapshot
      is available, if a requested capability cannot be resolved to a
      concrete scope identifier, if the scope entry is unknown, removed,
      reserved, or outside an accepted private extension policy, or if the
      required display metadata is absent or incomplete, the Principal Wallet MUST
      reject with <tt>grant_request_invalid</tt> and MUST NOT show the
      consent UI.
    </t>

    <t>
      The Principal Wallet MUST NOT synthesize consent text from raw scope
      identifiers, capability field names, deployer-supplied display strings,
      generic family names, or untrusted catalog data. The title value in
      <tt>display.strings[locale].title</tt> is the canonical
      human-readable capability string for that locale. The Principal Wallet MAY also
      display <tt>display.strings[locale].summary</tt>, but it MUST NOT use
      the summary to replace or weaken required destructive-operation
      highlighting.
    </t>

    <t>
      If an issued Principal Token includes a <tt>purpose</tt> claim, the
      claim value MUST be either the exact GrantRequest <tt>purpose</tt>
      value or a shorter summary that was displayed to and approved by the
      principal before signing. A Principal Token issuer MUST NOT include a
      deployer-supplied or issuer-generated <tt>purpose</tt> claim that was
      not presented during consent.
    </t>

    <t>
      <strong>Principal Token signing profile:</strong> For a root Principal
      Token, the token issuer is always the root principal identified by
      <tt>principal.id</tt>. A Principal Wallet or other principal-controlled
      signing component that constructs a root Principal Token after consent
      MUST sign the JWT with EdDSA, MUST set <tt>iss</tt> to
      <tt>principal.id</tt>, and MUST use a JOSE header <tt>kid</tt>
      identifying an Ed25519 verification method controlled by
      <tt>principal.id</tt>. A Registry or authorization server MAY prepare
      the payload, mediate consent, collect high-assurance authentication
      context, store the GrantResponse, or deliver the signed token, but it
      MUST NOT sign a root Principal Token with a Registry-controlled or
      authorization-server-controlled key and MUST NOT set <tt>iss</tt> to
      the Registry or authorization server. The Principal Token payload
      <tt>sub</tt> claim MUST equal the <tt>agent_aid</tt> value from the
      original GrantRequest. A service that cannot obtain a
      principal-controlled Ed25519 signature MUST NOT issue a root Principal
      Token with that principal as <tt>iss</tt>.
    </t>
    <t>
      <strong>Delegation expiry derivation:</strong> The Principal Token
      issuer MUST compute a single issuance timestamp from its current
      reliable UTC clock at the moment of signing. It MUST set the Principal
      Token <tt>issued_at</tt> claim and the GrantResponse
      <tt>signed_at</tt> field to that same instant. The approved delegation
      lifetime MUST be represented as
      <tt>approved_delegation_valid_for_seconds</tt>, MUST be at least 300
      seconds, and MUST NOT exceed the original GrantRequest
      <tt>delegation_valid_for_seconds</tt>. The issuer MAY approve a
      shorter lifetime than requested.
    </t>
    <t>
      The Principal Token <tt>expires_at</tt> claim MUST equal
      <tt>issued_at</tt> plus
      <tt>approved_delegation_valid_for_seconds</tt> seconds. No clock skew
      tolerance is applied to this calculation; clock skew tolerance applies
      only when validators compare timestamps to their local current time.
      The issuer MUST NOT use the deployer's clock, callback receipt time, or
      <tt>request_expires_at</tt> as the Principal Token <tt>issued_at</tt>
      value.
    </t>

    <t>
      <strong>Draft-02 Catalog display metadata:</strong>
    </t>

    <t>
      The AIP Scope Catalog is the normative source of capability display
      metadata. Each scope entry's <tt>display</tt> member is defined in
      <xref target="scope-map-registry"/>. For standard Draft-02 scopes,
      <tt>display.strings["en-US"].title</tt> MUST equal the display string
      listed below. The corresponding <tt>summary</tt> value MUST be
      non-empty and MUST describe the same capability without broadening the
      authorization represented by the scope. Extension and private scopes
      MUST provide equivalent catalog display metadata through their catalog
      entry or accepted private extension policy before a Principal Wallet can present
      consent for them.
    </t>

                <table>
          <name>Draft-02 Standard Scope Display Titles</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.write</td>
              <td>Create and draft email messages</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>calendar.write</td>
              <td>Create and update calendar events</td>
            </tr>
            <tr>
              <td>calendar.delete</td>
              <td>Delete your calendar events</td>
            </tr>
            <tr>
              <td>filesystem.read</td>
              <td>Read files from your local storage</td>
            </tr>
            <tr>
              <td>filesystem.write</td>
              <td>Save and modify files on your local storage</td>
            </tr>
            <tr>
              <td>filesystem.execute</td>
              <td>Execute scripts and commands on your system - HIGH RISK</td>
            </tr>
            <tr>
              <td>filesystem.delete</td>
              <td>Delete files from your local storage</td>
            </tr>
            <tr>
              <td>web.browse</td>
              <td>Browse the web and read website content</td>
            </tr>
            <tr>
              <td>web.forms_submit</td>
              <td>Submit data to web forms</td>
            </tr>
            <tr>
              <td>web.download</td>
              <td>Download files from the web to your system</td>
            </tr>
            <tr>
              <td>transactions</td>
              <td>Make financial transactions up to specified limits</td>
            </tr>
            <tr>
              <td>communicate.whatsapp</td>
              <td>Send and receive messages via WhatsApp</td>
            </tr>
            <tr>
              <td>communicate.telegram</td>
              <td>Send and receive messages via Telegram</td>
            </tr>
            <tr>
              <td>communicate.sms</td>
              <td>Send and receive SMS messages</td>
            </tr>
            <tr>
              <td>communicate.voice</td>
              <td>Initiate and receive voice calls</td>
            </tr>
            <tr>
              <td>spawn_agents.create</td>
              <td>Create child AI agents on your behalf</td>
            </tr>
            <tr>
              <td>spawn_agents.manage</td>
              <td>Monitor and manage your existing child agents</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 approves, partially approves, or declines 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>
            <tr>
              <td>signed_at</td>
              <td>REQUIRED</td>
              <td>ISO 8601 UTC; same instant as Principal Token issued_at when principal_token is present</td>
            </tr>
          </tbody>
        </table>

    <t>
      A GrantResponse with status "approved" or "partial" MUST also include
      <tt>approved_delegation_valid_for_seconds</tt>. The value MUST be an
      integer from 300 through the original GrantRequest
      <tt>delegation_valid_for_seconds</tt>, inclusive, and is used to derive
      the Principal Token <tt>expires_at</tt> claim.
    </t>

    <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. If mismatch, the deployer MUST reject the
      GrantResponse with <tt>grant_nonce_mismatch</tt>; the mismatch MUST be
      treated as a forgery attempt and no agent is registered.</li>
      <li>If status is "rejected", the deployer MUST treat the grant as a
      terminal failure with <tt>grant_rejected_by_principal</tt> and no agent
      is registered.</li>
      <li>Verify <tt>approved_delegation_valid_for_seconds</tt> is present
      for "approved" or "partial" responses, is at least 300 seconds, and
      does not exceed the original GrantRequest
      <tt>delegation_valid_for_seconds</tt></li>
      <li>Decode and verify the principal_token JWT signature</li>
      <li>Verify the principal_token payload sub exactly matches
      <tt>agent_aid</tt> from the original GrantRequest</li>
      <li>Verify the principal_token payload <tt>issued_at</tt> represents
      the same instant as the GrantResponse <tt>signed_at</tt></li>
      <li>Verify <tt>signed_at</tt> and <tt>issued_at</tt> are not more than
      30 seconds in the future relative to the deployer's current UTC clock,
      and that <tt>signed_at</tt> is not more than 24 hours in the past</li>
      <li>Verify the principal_token payload <tt>expires_at</tt> equals
      <tt>issued_at</tt> plus
      <tt>approved_delegation_valid_for_seconds</tt> seconds</li>
    </ol>
    <t>
      If any GrantResponse validation check fails, the deployer MUST treat
      the GrantResponse as invalid and MUST NOT submit a Registration
      Envelope based on it.
    </t>
    <t>
      When a deployer, Registry, Principal Wallet, or authorization server returns an
      AIP error response for a rejected grant, the error code MUST be
      <tt>grant_rejected_by_principal</tt>. When it returns an AIP error
      response for a GrantResponse whose <tt>nonce</tt> does not match the
      original GrantRequest, the error code MUST be
      <tt>grant_nonce_mismatch</tt>.
    </t>
  </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;compact-JWS-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 delegation issuer for the child. No new human consent UI is
      required, because the parent can only delegate scopes and constraints
      that are already within its own accepted authority.
    </t>
    <t>
      The child is registered with a delegated Principal Token, not a second
      root Principal Token. The delegated token's <tt>sub</tt> is the child
      AID, <tt>delegated_by</tt> is the parent AID, and
      <tt>delegation_depth</tt> is the parent's depth plus one. The Registry
      validates the submitted Registration Envelope using Registration Check 9
      and stores the reconstructed parent-plus-child chain for later runtime
      validation.
    </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.
      This is a local implementation conformance and key-management
      requirement. The Registry and Relying Parties cannot determine from
      protocol messages whether the parent retained a copy of the child's
      private key, and MUST NOT treat private-key deletion as a Registry
      acceptance check or Credential Token validation precondition.
      Deployment profiles that audit this requirement MUST collect the
      sub-agent provisioning and deletion evidence defined in
      <xref target="key-management"/>.
    </t>

    <t>
      For sub-agent delegation, a parent agent MAY include a <tt>purpose</tt>
      claim in the child's Principal Token to describe the delegated work.
      That claim is signed audit metadata only. It MUST NOT expand the child
      agent's scopes or constraints and MUST NOT be treated by Relying
      Parties as a substitute for Capability Manifest or Approval Envelope
      validation.
    </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 Principal 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 Principal Wallet URI] ------&gt;|
         |                      |                      |
         |                      |&lt;--- authenticates ---|
         |                      |&lt;-- approve/decline --|
         |                      |                      |
         | [Principal-controlled signer signs 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 obtain a root Principal Token signed
        using the Principal Token signing profile above, with <tt>iss</tt>
        equal to the approving principal's DID and <tt>sub</tt> equal to the
        stored GrantRequest <tt>agent_aid</tt>. The Registry MAY construct
        the unsigned payload and present it to a principal-controlled
        Principal Wallet
        or signing component, but the final compact JWT MUST be signed by an
        Ed25519 verification method controlled by <tt>principal.id</tt>. The
        Registry MUST store the resulting GrantResponse without altering or
        re-signing the Principal Token.
      </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>
    <t>
      If <tt>grant_id</tt> does not identify a stored G1 grant, or if the
      referenced grant is expired and no GrantResponse is retrievable, the
      Registry MUST reject the request with <tt>grant_not_found</tt>.
    </t>
    <t>
      If <tt>grant_id</tt> identifies a stored, retrievable G1 grant but the
      authenticated requester does not match the <tt>deployer_did</tt> in the
      original GrantRequest, the Registry MUST reject the request with
      <tt>grant_deployer_mismatch</tt>.
    </t>
    <t>
      If <tt>grant_id</tt> identifies a stored G1 grant whose principal
      outcome is <tt>"rejected"</tt>, the Registry MUST reject the retrieval
      request with <tt>grant_rejected_by_principal</tt>. If the stored
      GrantResponse <tt>nonce</tt> does not match the original GrantRequest
      <tt>nonce</tt>, the Registry MUST reject the retrieval request with
      <tt>grant_nonce_mismatch</tt> and MUST NOT return the stored
      GrantResponse.
    </t>
  </section>

  <section anchor="g2-grant" numbered="true">
  <name>G2: Direct Deployer Grant Flow</name>
    <t>
      The G2 (Direct Deployer) grant profile is the standard flow for
      applications that integrate directly with Principal Wallets.
      The deployer signs the GrantRequest directly and transmits it
      to the Principal Wallet via a front-channel binding (Web Redirect or QR
      Code).
    </t>

    <t>
      <strong>Actors:</strong> Agent Deployer, Principal Wallet, Principal.
    </t>

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

    <ol>
      <li>The deployer generates the agent's Ed25519 keypair, derives the
      agent AID, and constructs a GrantRequest that includes
      <tt>agent_aid</tt> and <tt>deployer_did</tt>.</li>
      <li>The deployer signs the GrantRequest as a compact JWS using the Ed25519 key identified by a <tt>kid</tt> under <tt>deployer_did</tt>.</li>
      <li>The deployer transmits the request to the Principal Wallet via a front-channel binding.</li>
      <li>The Principal Wallet resolves <tt>deployer_did</tt>, verifies the signed GrantRequest envelope, and presents the consent UI to the principal only after successful verification.</li>
      <li>On approval, the Principal Wallet signs the Principal Token using the Principal Token signing profile above and returns a GrantResponse to the deployer's callback_uri.</li>
      <li>The deployer submits the Registration Envelope to the Registry with grant_tier: "G2".</li>
    </ol>
  </section>

  <section anchor="g3-grant" numbered="true">
  <name>G3: Full Ceremony Grant Flow (OAuth 2.1)</name>
    <t>
      The G3 (Full Ceremony) grant profile is required for high-assurance
      Tier 3 operations and environments requiring identity proofing.
      It uses an OAuth 2.1 Authorization Server (AS) typically operated
      by the Registry.
    </t>

    <t>
      <strong>Actors:</strong> Agent Deployer, AIP Registry (as OAuth AS),
      Principal Wallet, Principal.
    </t>

    <t>
      <strong>OAuth binding of the GrantRequest:</strong> Before initiating
      the OAuth authorization request, the deployer MUST construct and sign a
      GrantRequest using the signed GrantRequest envelope defined in Section
      12.2. The G3 OAuth authorization request MUST carry that compact JWS in
      an <tt>aip_grant_request</tt> form parameter. The authorization request
      MUST also include <tt>response_type=code</tt>, <tt>client_id</tt>,
      <tt>redirect_uri</tt>, <tt>scope</tt>, <tt>state</tt>,
      <tt>code_challenge</tt>, <tt>code_challenge_method=S256</tt>,
      <tt>acr_values</tt>, and <tt>aip_version=0.3</tt>.
    </t>

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

response_type=code
&amp;client_id=did:web:assistant.example.com
&amp;redirect_uri=https://assistant.example.com/aip/grant-callback
&amp;scope=email.read%20calendar.read
&amp;state=sess_abc123_csrf_xyz789
&amp;code_challenge=&lt;S256-code-challenge&gt;
&amp;code_challenge_method=S256
&amp;acr_values=&lt;requested-acr&gt;
&amp;aip_version=0.3
&amp;aip_grant_request=&lt;compact-JWS-signed-GrantRequest&gt;
</artwork>

    <t>
      The Registry Authorization Server MUST verify the signed
      GrantRequest envelope before redirecting the principal to a Principal
      Wallet or displaying any consent UI. Verification MUST include the signed
      GrantRequest checks in Section 12.2 and the following G3 binding
      checks:
    </t>
    <ol type="%d">
      <li>The <tt>aip_grant_request</tt> parameter is present and contains a
      compact JWS whose payload is a GrantRequest object.</li>
      <li>The OAuth <tt>client_id</tt> either equals the GrantRequest
      <tt>deployer_did</tt> or identifies a pre-registered OAuth client whose
      Registry metadata maps to that same <tt>deployer_did</tt>.</li>
      <li>The OAuth <tt>redirect_uri</tt> exactly matches the GrantRequest
      <tt>callback_uri</tt>.</li>
      <li>The OAuth <tt>state</tt> exactly matches the GrantRequest
      <tt>state</tt>.</li>
      <li>The GrantRequest <tt>request_expires_at</tt> has not passed,
      allowing at most 30 seconds of clock skew.</li>
      <li>The OAuth <tt>scope</tt> parameter does not request authority
      outside the GrantRequest <tt>requested_capabilities</tt>. If the
      Authorization Server cannot map a requested scope string to the
      GrantRequest capabilities under the synced AIP Scope Catalog, it MUST
      treat the authorization request as invalid.</li>
    </ol>
    <t>
      If any binding check fails, the Authorization Server MUST reject the
      authorization request with <tt>grant_request_invalid</tt> and MUST NOT
      issue an authorization code. After successful verification, the
      Authorization Server MUST store an immutable authorization-code binding
      that includes the JCS hash of the GrantRequest payload, the compact JWS
      signature value, <tt>grant_request_id</tt>, <tt>agent_aid</tt>,
      <tt>deployer_did</tt>, <tt>nonce</tt>, <tt>state</tt>,
      <tt>callback_uri</tt>, <tt>requested_capabilities</tt>, and
      <tt>delegation_valid_for_seconds</tt>. The token endpoint MUST use this
      stored binding when redeeming the authorization code and MUST reject
      any code that lacks such a binding with <tt>grant_request_invalid</tt>.
    </t>

    <t>
      <strong>G3 token endpoint response mapping:</strong> A successful
      <tt>POST /v1/oauth/token</tt> response for a G3 grant ceremony is an
      OAuth token response that transports, but does not replace, an AIP
      GrantResponse. The response body MUST be a JSON object with the following
      fields:
    </t>

    <ul>
      <li><tt>token_type</tt>: REQUIRED. MUST be <tt>"AIP+JWT"</tt>.</li>
      <li><tt>access_token</tt>: REQUIRED. The compact Principal Token JWT.
      This value is an OAuth transport alias only.</li>
      <li><tt>expires_in</tt>: REQUIRED. Integer seconds until the Principal
      Token expires; MUST equal
      <tt>grant_response.approved_delegation_valid_for_seconds</tt>.</li>
      <li><tt>state</tt>: REQUIRED. MUST equal the OAuth
      <tt>state</tt> value stored in the authorization-code binding.</li>
      <li><tt>grant_response</tt>: REQUIRED. A JSON object conforming to the
      GrantResponse schema in Section 12.4 with <tt>status</tt> equal to
      <tt>"approved"</tt> or <tt>"partial"</tt>.</li>
    </ul>

    <t>
      The <tt>access_token</tt> value MUST be byte-for-byte identical to
      <tt>grant_response.principal_token</tt>. The deployer MUST validate the
      nested <tt>grant_response</tt> using the GrantResponse validation rules in
      Section 12.4 and MUST use <tt>grant_response.principal_token</tt> when
      constructing the Registration Envelope. The <tt>access_token</tt> field
      exists only for OAuth client interoperability and MUST NOT be interpreted
      as replacing the <tt>principal_token</tt> field. The Principal Token
      payload MUST include the <tt>acr</tt> and <tt>amr</tt> claims from the
      completed G3 ceremony. If the principal rejects the grant or the ceremony
      fails before approval, the Authorization Server MUST return an OAuth error
      response and MUST NOT return a successful token response.
    </t>

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

    <ol>
      <li>The deployer constructs and signs a GrantRequest, then initiates an
      OAuth 2.1 Authorization Code Flow with PKCE at the Registry's
      authorization endpoint using the G3 OAuth binding above.</li>
      <li>The Registry redirects the principal to their configured Principal Wallet for authentication and consent.</li>
      <li>The Principal Wallet performs high-assurance authentication (e.g., FIDO2/WebAuthn) and obtains consent.</li>
      <li>The Principal Wallet returns an authorization code or equivalent signed
      ceremony result to the Registry.</li>
      <li>The Registry MUST obtain a root Principal Token signed using the
      Principal Token signing profile above by a principal-controlled Ed25519
      verification method. The Registry MAY prepare the token payload and
      include the high-assurance <tt>acr</tt> and <tt>amr</tt> values from the
      OAuth ceremony, but it MUST NOT sign the root Principal Token with the
      Registry's authorization-server key and MUST NOT change <tt>iss</tt>
      away from <tt>principal.id</tt>. The token <tt>sub</tt> MUST equal the
      <tt>agent_aid</tt> value from the stored authorization-code binding,
      and the token MUST include <tt>acr</tt> and <tt>amr</tt> claims from
      the completed ceremony.</li>
      <li>The token endpoint returns the G3 token endpoint response defined
      above. Its <tt>grant_response</tt> member is the AIP GrantResponse used
      by the deployer for Registration Envelope construction.</li>
      <li>The deployer submits the Registration Envelope to the Registry with grant_tier: "G3".</li>
    </ol>
  </section>
</section>
    <section anchor="approval-envelopes" numbered="true" xml:base="sections/13-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>
  </section>

  <section anchor="token-expiry-pending" numbered="true">
  <name>The Token-Expiry-While-Pending Problem</name>
    <t>
      A Credential Token with a catalog-derived TTL 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>
      Approval Envelopes have two wire profiles. An
      <tt>ApprovalEnvelopeSubmission</tt> is submitted by the orchestrating
      agent to <tt>POST /v1/approvals</tt>. A
      <tt>StoredApprovalEnvelope</tt> is returned by Registry read and
      mutation endpoints and includes Registry-managed lifecycle fields. A
      Registry MUST validate <tt>POST /v1/approvals</tt> request bodies
      against the submission profile and MUST NOT accept read-only stored
      fields in the submission body.
    </t>

                <table>
          <name>ApprovalEnvelopeSubmission 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:&lt;UUIDv4-lowerhex&gt;</td>
            </tr>
            <tr>
              <td>created_by</td>
              <td>REQUIRED</td>
              <td>AID of orchestrating agent</td>
            </tr>
            <tr>
              <td>creator_kid</td>
              <td>REQUIRED</td>
              <td>DID URL identifying the Ed25519 verification method controlled by <tt>created_by</tt></td>
            </tr>
            <tr>
              <td>principal_id</td>
              <td>REQUIRED</td>
              <td>W3C DID; MUST NOT be did:aip</td>
            </tr>
            <tr>
              <td>engagement_id</td>
              <td>OPTIONAL</td>
              <td>pattern: eng:&lt;UUIDv4-lowerhex&gt;; links to parent Engagement Object</td>
            </tr>
            <tr>
              <td>description</td>
              <td>REQUIRED</td>
              <td>minLength: 1; maxLength: 512</td>
            </tr>
            <tr>
              <td>created_at</td>
              <td>REQUIRED</td>
              <td>ISO 8601 UTC; set by orchestrating agent; MUST NOT be in the future beyond 30-second clock skew</td>
            </tr>
            <tr>
              <td>approval_window_expires_at</td>
              <td>REQUIRED</td>
              <td>ISO 8601 UTC; MUST be strictly after <tt>created_at</tt>; MUST NOT be more than 72 hours after <tt>created_at</tt></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, treating omitted step values as 0</td>
            </tr>
            <tr>
              <td>currency</td>
              <td>OPTIONAL</td>
              <td>ISO 4217; pattern: ^[A-Z]{3}$</td>
            </tr>
            <tr>
              <td>notification_uri</td>
              <td>OPTIONAL</td>
              <td>HTTPS URI for principal notification callbacks; included in creator and principal signing inputs when present</td>
            </tr>
            <tr>
              <td>creator_signature</td>
              <td>REQUIRED</td>
              <td>base64url EdDSA signature</td>
            </tr>
          </tbody>
        </table>

    <t>
      An <tt>ApprovalEnvelopeSubmission</tt> MUST NOT contain top-level
      <tt>status</tt>, <tt>principal_signature</tt>, or
      <tt>approved_at</tt>. Its forward steps MUST NOT contain read-only
      <tt>status</tt>, <tt>claimed_at</tt>, or <tt>completed_at</tt> fields.
      Its compensation steps MUST NOT contain read-only <tt>status</tt>
      fields. If any read-only field is present in the submission body, the
      Registry MUST reject with <tt>approval_envelope_invalid</tt>.
    </t>

    <table>
      <name>StoredApprovalEnvelope Registry Fields</name>
      <thead>
        <tr>
          <th align="left">Field</th>
          <th align="center">Required</th>
          <th align="left">Constraints</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>status</td>
          <td>REQUIRED</td>
          <td>Registry-managed lifecycle state; initially pending_approval</td>
        </tr>
        <tr>
          <td>principal_signature</td>
          <td>CONDITIONAL</td>
          <td>Set only by successful principal approval; required for approved, executing, completed, compensating, compensated, and failed envelopes</td>
        </tr>
        <tr>
          <td>principal_kid</td>
          <td>CONDITIONAL</td>
          <td>DID URL identifying the principal verification method; required whenever <tt>principal_signature</tt> is present</td>
        </tr>
        <tr>
          <td>approved_at</td>
          <td>CONDITIONAL</td>
          <td>Registry timestamp set with the winning approval transition; required whenever principal_signature is present</td>
        </tr>
        <tr>
          <td>steps[].status</td>
          <td>REQUIRED</td>
          <td>Registry-managed step lifecycle state; initially pending</td>
        </tr>
        <tr>
          <td>steps[].claimed_at</td>
          <td>CONDITIONAL</td>
          <td>Registry timestamp set when a Step Execution Token is issued</td>
        </tr>
        <tr>
          <td>steps[].completed_at</td>
          <td>CONDITIONAL</td>
          <td>Registry timestamp set when a forward step completes</td>
        </tr>
        <tr>
          <td>compensation_steps[].status</td>
          <td>REQUIRED when compensation_steps is present</td>
          <td>Registry-managed compensation lifecycle state; initially pending</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. A step that omits <tt>value</tt> contributes
      <tt>0</tt> to this sum. Required and optional steps are both included
      in the sum when they carry a <tt>value</tt> field. This value is the
      maximum approved financial exposure of the envelope, not a prediction
      that every optional step will execute.
    </t>

    <t>
      When present, <tt>engagement_id</tt> scopes the Approval Envelope
      to the parent Engagement Object. The field is part of the Approval
      Envelope body and MUST NOT be supplied only as external Registry
      metadata. Both <tt>creator_signature</tt> and
      <tt>principal_signature</tt> signing inputs MUST include
      <tt>engagement_id</tt> whenever it is present.
    </t>
    <t>
      The <tt>creator_signature</tt> signing input is the Section 2.1
      canonical JSON serialization of the
      <tt>ApprovalEnvelopeSubmission</tt> object with
      <tt>creator_signature</tt> set to <tt>""</tt>. The
      <tt>principal_signature</tt> signing input is the same immutable
      submission object after successful submission, including
      <tt>creator_signature</tt>, <tt>principal_kid</tt>, and a
      <tt>principal_signature</tt> member set to <tt>""</tt>. Both signing
      inputs include <tt>creator_kid</tt>, <tt>notification_uri</tt>, and
      <tt>engagement_id</tt> when present. The DID portion of
      <tt>creator_kid</tt> MUST equal <tt>created_by</tt>; the DID portion of
      <tt>principal_kid</tt> MUST equal <tt>principal_id</tt>. Neither
      signing input includes Registry-managed top-level lifecycle fields
      (<tt>status</tt> or <tt>approved_at</tt>) or Registry-managed step fields
      (<tt>status</tt>, <tt>claimed_at</tt>, or <tt>completed_at</tt>).
    </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 13.7</td>
            </tr>
            <tr>
              <td>scopes</td>
              <td>REQUIRED</td>
              <td>minItems: 1; uniqueItems: true; each item is an AIP scope string required for this step</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>compensation_index</td>
              <td>OPTIONAL</td>
              <td>1-based pointer to a compensation step; null or absent means no compensation action is defined</td>
            </tr>
            <tr>
              <td>value</td>
              <td>OPTIONAL</td>
              <td>Financial value; minimum: 0; omitted means 0 for total_value validation</td>
            </tr>
            <tr>
              <td>currency</td>
              <td>OPTIONAL</td>
              <td>ISO 4217 currency code for this step's value; when present, MUST match envelope currency</td>
            </tr>
            <tr>
              <td>status</td>
              <td>READ-ONLY</td>
              <td>pending | claimed | completed | failed | compensated | skipped | cancelled</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 MUST NOT be claimed before 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>

    <t>
      The <tt>compensation_index</tt> field, when present on a forward
      step, identifies the single compensation step that rolls back that
      forward step. The value is a 1-based identifier that MUST equal the
      <tt>compensation_index</tt> field of exactly one entry in
      <tt>compensation_steps</tt>; it is not a zero-based JSON array offset.
      The Registry MUST reject envelopes where a non-null
      <tt>compensation_index</tt> does not reference an existing compensation
      step, or where more than one forward step references the same
      compensation step.
    </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>
    <t>
      A compensation step is selected only through a forward step's
      <tt>compensation_index</tt> field. The Registry MUST NOT infer a
      compensation relationship from matching <tt>action_type</tt> values,
      array position, actor identity, or relying party URI.
    </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>1-based; unique within compensation_steps</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>scopes</td>
              <td>REQUIRED</td>
              <td>minItems: 1; uniqueItems: true; each item is an AIP scope string required for this compensation step</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

	   pending_approval --&gt; cancelled
	   approved         --&gt; cancelled
	   executing        --&gt; cancelled
</artwork>

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

    <dl newline="true">
      <dt>pending_approval to approved</dt>
      <dd>Principal signs via Principal Wallet. Registry atomically stores
      <tt>principal_signature</tt>, sets <tt>approved_at</tt>, and transitions
      the envelope to <tt>approved</tt>. Only envelopes currently in
      <tt>pending_approval</tt> can make this transition.</dd>

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

      <dt>pending_approval to expired</dt>
      <dd><tt>approval_window_expires_at</tt> passes before a successful
      approval transition is committed.</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>

      <dt>pending_approval, approved, or executing to cancelled</dt>
      <dd>Parent Engagement Object transitions to terminated. The
      Registry MUST NOT transition Approval Envelopes to cancelled
      solely because the parent Engagement Object transitions to
      completed.</dd>
    </dl>

    <t>
      The <tt>approval_window_expires_at</tt> field is an approval-deadline
      only. It controls whether an envelope is eligible to transition from
      <tt>pending_approval</tt> to <tt>approved</tt>. Once an envelope has
      transitioned to <tt>approved</tt> before that deadline, later passage of
      <tt>approval_window_expires_at</tt> MUST NOT transition the envelope to
      <tt>expired</tt> and MUST NOT be used to reject step claims. Execution
      time limits, if any, MUST be expressed by a separate field or by
      endpoint policy; they MUST NOT be inferred from
      <tt>approval_window_expires_at</tt>.
    </t>

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

    <t>
      The Registry MUST treat approval, rejection, expiry, cancellation, and
      first step claim as mutually exclusive state transitions over the same
      Approval Envelope lifecycle record. Implementations MUST use optimistic
      locking, compare-and-set, a database transaction, or an equivalent
      distributed lock so that exactly one concurrent transition out of
      <tt>pending_approval</tt> can succeed. If multiple Principal Wallets
      submit valid approval signatures concurrently for the same
      <tt>principal_id</tt>, the first committed approval wins. The Registry
      MUST NOT replace <tt>principal_signature</tt>, <tt>approved_at</tt>, or
      the winning state with a later approval request. A later approval or
      rejection request that races with or follows a completed lifecycle
      transition MUST fail with <tt>approval_state_conflict</tt>, except that
      an exact byte-for-byte retry of the already stored approval response MAY
      be treated as idempotent and return the stored envelope.
    </t>

    <t>
      When an envelope transitions to <tt>cancelled</tt>, pending forward
      steps under that envelope MUST transition directly to
      <tt>cancelled</tt>. Claimed forward steps MUST be treated as
      <tt>failed</tt> for compensation-trigger evaluation, then MUST
      transition to <tt>cancelled</tt> after any required compensation
      resolution completes or fails. Completed, failed, compensated, and
      skipped steps retain their existing status for audit purposes. The
      Registry MUST NOT issue new Step Execution Tokens for cancelled
      envelopes or cancelled steps.
    </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>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>Credential Token <tt>aip_scope</tt> contains every scope listed in the step's <tt>scopes</tt> array</li>
      <li>action_hash matches stored value</li>
    </ol>

    <t>
      The Registry MUST NOT evaluate <tt>approval_window_expires_at</tt> as a
      step-claim condition for an envelope already in <tt>approved</tt> or
      <tt>executing</tt> status.
    </t>

    <t>
      If the <tt>triggered_by</tt> check fails because one or more prerequisite
      steps have not reached <tt>"completed"</tt>, the Registry MUST reject
      the claim with <tt>approval_step_prerequisites_unmet</tt> and MUST NOT
      issue a Step Execution Token.
    </t>

    <t>
      If all checks pass, the Registry returns a Step Execution Token
      signed by a JWK listed in
      <tt>active_verification_keys.step_execution</tt> of the
      Registry Trust Record version current at issuance time:
    </t>

    <artwork type="example">
header = {
  "typ": "AIP-SET+JWT",
  "alg": "EdDSA",
  "kid": "&lt;Registry step-execution keyid&gt;",
  "aip_trv": &lt;Registry Trust Record version&gt;
}

payload = {
  "iss": "&lt;Registry ID&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": ["transactions"],
  "aip_chain": ["&lt;compact Principal Token JWT&gt;"],
  "aip_approval_id": "&lt;approval_id&gt;",
  "aip_step_kind": "forward",
  "aip_approval_step": &lt;step_index&gt;
}
</artwork>

    <t>
      A Step Execution Token is not an agent-issued Credential Token. Its
      <tt>iss</tt> is the Registry ID HTTPS URI, its <tt>sub</tt> is the
      claimed step actor AID, and its JOSE <tt>kid</tt> identifies a
      step-execution verification key from the Registry Trust Record. Relying
      Parties MUST validate Step Execution Tokens using the SET validation
      profile in Section 9 before applying Step 10a. The Registry MUST derive
      the SET <tt>aip_scope</tt> value from the claimed step's
      <tt>scopes</tt> array and MUST derive the SET <tt>exp</tt> value using
      the TTL rule in <xref target="step-execution-token"/>.
    </t>

    <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 before the claim deadline. The
      claim deadline is <tt>claimed_at + step_claim_timeout_seconds</tt>,
      where <tt>step_claim_timeout_seconds</tt> is the Registry policy value
      published in <tt>/v1/registry-metadata</tt>. The value MUST be an
      integer from 1 to 600 seconds inclusive.
    </t>
    <t>
      If a claimed step is not completed or failed before that deadline, the
      Registry MUST treat the claim as timed out, transition the step to
      <tt>failed</tt>, and apply the SAGA compensation rules as if the actor
      had explicitly reported step failure.
    </t>

    <t>
      The JSON body profiles for claim, complete, and fail requests and
      responses are defined in <tt>approval-step-endpoints.schema.json</tt>.
      All request bodies in this section MUST use
      <tt>Content-Type: application/json</tt>. The Registry MUST ignore any
      client-supplied completion or failure timestamp for lifecycle purposes;
      <tt>claimed_at</tt>, <tt>completed_at</tt>, and failure timestamps are
      Registry receipt times.
    </t>

    <t>
      <strong>Claim request:</strong> A forward step claim request body MUST
      conform to the <tt>claim_request</tt> profile and contain
      <tt>credential_token</tt> and <tt>action_parameters</tt>. The Registry
      MUST validate the Credential Token using Section 9 with the Registry
      claim endpoint as the Relying Party. The token issuer, token subject,
      and leaf Principal Token subject MUST equal the step actor. The Registry
      MUST recompute the step <tt>action_hash</tt> from
      <tt>action_parameters</tt> using Section 13.7 and MUST reject a mismatch
      with <tt>approval_step_action_mismatch</tt>. The Credential Token
      <tt>aip_scope</tt> set MUST contain every scope in the claimed step's
      <tt>scopes</tt> array; otherwise the Registry MUST reject with
      <tt>insufficient_scope</tt>.
    </t>

    <t>
      On a successful forward claim, the Registry MUST atomically transition
      the forward step from <tt>pending</tt> to <tt>claimed</tt>, set
      <tt>claimed_at</tt>, store the issued SET <tt>jti</tt>, transition the
      envelope from <tt>approved</tt> to <tt>executing</tt> if this is the
      first claimed step, and return a <tt>claim_response</tt> containing the
      compact Step Execution Token. If the same actor repeats the same claim
      request with the same <tt>idempotency_key</tt> before the claim expires,
      the Registry MAY return the stored claim response. A different actor,
      different request body, expired claim, or missing matching idempotency
      record MUST NOT be treated as an idempotent retry.
    </t>

    <t>
      <strong>Complete request:</strong> A complete request body MUST conform
      to the <tt>complete_request</tt> profile and contain
      <tt>step_execution_token</tt>. The Registry MUST validate the SET using
      the Section 9 SET validation profile, verify that the SET
      <tt>aip_approval_id</tt> and step identifier match the request path,
      verify that the SET <tt>sub</tt> equals the step actor, verify that the
      SET <tt>jti</tt> equals the stored <tt>jti</tt> for the active claim,
      and verify that the step or compensation step is currently
      <tt>claimed</tt> and has not passed its claim deadline. If any of these
      checks fail, the Registry MUST reject with
      <tt>approval_step_invalid</tt> or <tt>approval_step_not_claimed</tt> as
      applicable.
    </t>

    <t>
      On a successful forward-step complete request, the Registry MUST
      transition the step to <tt>completed</tt>, set <tt>completed_at</tt>,
      persist any supplied <tt>result_hash</tt> or <tt>evidence_uri</tt> as
      audit metadata, and evaluate envelope completion. If all required
      forward steps are completed and no compensation is pending, the Registry
      MUST transition the envelope to <tt>completed</tt>. Exact retries with
      the same <tt>idempotency_key</tt> MAY return the stored
      <tt>complete_response</tt>; conflicting repeats MUST reject with
      <tt>approval_state_conflict</tt>.
    </t>

    <t>
      <strong>Fail request:</strong> A fail request body MUST conform to the
      <tt>fail_request</tt> profile and contain <tt>step_execution_token</tt>
      and <tt>failure_code</tt>. The Registry MUST perform the same SET,
      actor, path, stored-<tt>jti</tt>, state, and deadline checks required
      for completion. On success, the Registry MUST transition the step to
      <tt>failed</tt>, persist the supplied failure metadata for audit, and
      apply the SAGA compensation rules. Exact retries with the same
      <tt>idempotency_key</tt> MAY return the stored
      <tt>fail_response</tt>; conflicting repeats MUST reject with
      <tt>approval_state_conflict</tt>.
    </t>

    <t>
      <strong>Compensation endpoints:</strong>
      <tt>POST /v1/approvals/{id}/compensation-steps/{n}/claim</tt>,
      <tt>/complete</tt>, and <tt>/fail</tt> use the same request and
      response profiles as forward-step endpoints. The path <tt>{n}</tt> is
      the compensation step's <tt>compensation_index</tt>. Compensation claims
      are valid only when the envelope is <tt>compensating</tt> and the
      compensation step has been triggered by the SAGA rules in Section 13.9.
      SETs issued for compensation claims MUST contain
      <tt>aip_step_kind: "compensation"</tt> and
      <tt>aip_compensation_step</tt>, and MUST NOT contain
      <tt>aip_approval_step</tt>.
    </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>Build the compensation set from forward steps whose status is
      <tt>completed</tt> and whose <tt>compensation_index</tt> is non-null.
      The failed forward step is included in this set only if its status had
      previously reached <tt>completed</tt> before the failure was reported.</li>
      <li>Sort that forward-step set by descending <tt>step_index</tt>.</li>
      <li>For each sorted forward step, trigger the compensation step
      referenced by that forward step's <tt>compensation_index</tt>.</li>
      <li>Notify the orchestrating agent</li>
    </ol>

    <t>
      Compensation steps that are not referenced by any completed forward
      step MUST NOT be triggered. If the failed required step has no completed
      side effects and no completed predecessor has a non-null
      <tt>compensation_index</tt>, the Registry MUST transition the envelope
      directly to <tt>failed</tt> rather than <tt>compensating</tt>. If a
      completed forward step references a compensation step that cannot be
      found, the envelope is invalid and the Registry MUST treat the
      compensation attempt as failed.
    </t>

    <t>
      Rollback ordering is based on the forward steps' descending
      <tt>step_index</tt> values, not on <tt>compensation_index</tt> order. The
      <tt>compensation_index</tt> value is used only to dereference the
      pre-authorised compensation step selected for each completed forward
      step.
    </t>

    <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). The Registry MUST set the failed
      compensation step status to <tt>failed</tt>, set the envelope status to
      <tt>failed</tt>, persist an audit record containing the approval ID,
      compensation index, actor AID, failure timestamp, and failure reason, and
      return or expose <tt>compensation_failed</tt> for the failed transition.
    </t>
    <t>
      The Registry MUST create a principal notification event for the terminal
      compensation failure. If the Approval Envelope contains a
      <tt>notification_uri</tt>, the Registry MUST attempt delivery to that URI.
      If RPNP is supported and a matching active subscription exists, the
      Registry MUST also emit an RPNP event. If no delivery channel is
      configured, the Registry MUST retain the notification event and expose it
      in the Approval Envelope status response until acknowledged by local
      policy. Human remediation of the failed business action remains outside
      AIP, but the failure state, audit record, and notification event are AIP
      protocol obligations.
    </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><tt>principal_id</tt> begins with the exact ASCII prefix
        <tt>did:aip:</tt>; reject with
        <tt>approval_envelope_invalid</tt>. This is a byte-for-byte prefix
        comparison against the first eight characters of the string; values
        whose first eight characters are <tt>did:aip:</tt> identify an agent
        AID and are not valid principal identifiers.</li>
      <li><tt>engagement_id</tt> is present but malformed: reject with
        <tt>approval_envelope_invalid</tt>. If <tt>engagement_id</tt>
        references an Engagement Object that does not exist, reject with
        <tt>engagement_not_found</tt>.</li>
      <li><tt>steps</tt> contains circular dependencies in
        <tt>triggered_by</tt>: reject with
        <tt>approval_envelope_invalid</tt>.</li>
      <li><tt>steps</tt> contains duplicate <tt>step_index</tt> values:
        reject with <tt>approval_envelope_invalid</tt>.</li>
      <li><tt>total_value</tt> != sum of step values, treating omitted
        step <tt>value</tt> fields as <tt>0</tt>: reject with
        <tt>approval_envelope_invalid</tt>.</li>
      <li>Any present step <tt>currency</tt> differs from the envelope
        <tt>currency</tt>: reject with <tt>approval_envelope_invalid</tt>.</li>
      <li><tt>approval_window_expires_at</tt> is in the past: reject with
        <tt>approval_envelope_expired</tt>.</li>
      <li><tt>created_at</tt> is in the future beyond 30-second clock skew:
        reject with <tt>approval_envelope_invalid</tt>.</li>
      <li><tt>approval_window_expires_at</tt> is not strictly after
        <tt>created_at</tt>, or is more than 72 hours after
        <tt>created_at</tt>: reject with
        <tt>approval_envelope_invalid</tt>.</li>
      <li><tt>compensation_index</tt> references a non-existent index:
        reject with <tt>approval_envelope_invalid</tt>.</li>
      <li><tt>compensation_steps</tt> contains duplicate
        <tt>compensation_index</tt> values or values that are not sequential
        starting at <tt>1</tt>: reject with
        <tt>approval_envelope_invalid</tt>.</li>
      <li>More than one forward step references the same non-null
        <tt>compensation_index</tt>: reject with
        <tt>approval_envelope_invalid</tt>.</li>
      <li><tt>creator_signature</tt> does not verify: reject with
        <tt>approval_envelope_invalid</tt>.</li>
      <li><tt>created_by</tt> agent is revoked: reject with
        <tt>agent_revoked</tt>.</li>
      <li><tt>steps</tt> contains more than 20 elements: reject with
        <tt>approval_envelope_invalid</tt>.</li>
      <li><tt>triggered_by</tt> is 0: reject with
        <tt>approval_envelope_invalid</tt>. An absent
        <tt>triggered_by</tt> field is a schema violation because the field is
        REQUIRED by the step schema and is rejected before this semantic
        validation rule is evaluated.</li>
      <li>A forward or compensation step omits <tt>scopes</tt>, includes an
        empty <tt>scopes</tt> array, includes duplicate scope strings, or
        includes a scope that is not active in the synced AIP Scope Catalog:
        reject with <tt>approval_envelope_invalid</tt>.</li>
    </ol>

    <t>
      The Registry MUST also reject envelopes with
      <tt>insufficient_scope</tt> unless the submitting orchestrating agent's
      Credential Token and current Capability Manifest both include the active
      <tt>approvals.create</tt> scope from the synced AIP Scope Catalog.
      Transaction, communication, filesystem, and agent-spawning scopes alone
      do not grant authority to submit Approval Envelopes.
    </t>

    <t>
      <strong>Rule DESTRUCTIVE-1 (Mandatory Approval):</strong> Any operation
      involving a scope whose active AIP Scope Catalog entry has
      <tt>destructive: true</tt> MUST be authorised via a dedicated Approval
      Envelope step or a direct human confirmation ceremony. Registry-mediated
      G1 grants MUST NOT be used for destructive operations without an
      additional out-of-band confirmation.
    </t>
  </section>
</section>
    <section anchor="reputation" xml:base="sections/14-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 5.8.</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>The optional Endorsement Object <tt>notes</tt> field is signed audit context only. It MUST NOT be used as an input to reputation scoring, endorsement weighting, validation, or counter updates.</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. This is a reputation-input recommendation, not a protocol validation gate. Registries and Relying Parties cannot prove that an orchestrator failed to submit an expected Endorsement Object, and absence of such an endorsement MUST NOT cause Approval Envelope validation, Credential Token validation, or step-completion processing to fail.</t>

    <section anchor="endorsement-acceptance-requirements">
      <name>Endorsement Acceptance Requirements</name>
    <t>
      A conformant Registry that accepts endorsements, by implementing
      <tt>POST /v1/endorsements</tt>, MUST enforce the following minimum
      acceptance rules regardless of its reputation scoring algorithm:
    </t>
    <ol type="%d">
      <li>The endorsement signature MUST be verified using
      <tt>signature_kid</tt>. The DID portion of <tt>signature_kid</tt> MUST
      equal <tt>from_aid</tt>, and the key MUST be Ed25519 verification
      material controlled by that AID. If key binding or signature
      verification fails, the Registry MUST reject with
      <tt>endorsement_invalid</tt>.</li>
      <li>The <tt>from_aid</tt> MUST be a registered, non-revoked agent AID in
      the Registry. If not, the Registry MUST reject with
      <tt>unknown_aid</tt> or <tt>agent_revoked</tt>.</li>
      <li>The <tt>to_aid</tt> MUST be a registered agent AID in the Registry.
      If not, the Registry MUST reject with <tt>unknown_aid</tt>.</li>
      <li>The <tt>score</tt> field MUST be in the range 1 through 5 inclusive,
      as an integer or decimal value. Values outside this range MUST be
      rejected with <tt>endorsement_invalid</tt>.</li>
    </ol>
    </section>
  </section>
  <section anchor="reputation-scoring">
    <name>Reputation Scoring</name>
    <t>
      Reputation scoring algorithms, aggregation functions, and score decay
      policies are implementation-defined and are not subject to conformance
      testing. A Registry that implements the Reputation Output optional
      feature MUST expose a reputation score in the range 0.0 through 5.0 at
      <tt>GET /v1/agents/{aid}/reputation</tt> in a response containing at
      minimum:
    </t>
    <ul>
      <li><tt>score</tt>: number, current reputation score in the range 0.0
      through 5.0;</li>
      <li><tt>endorsement_count</tt>: integer, total number of accepted
      endorsements;</li>
      <li><tt>computed_at</tt>: string, ISO 8601 UTC timestamp of last score
      computation.</li>
    </ul>
    <t>A Registry that implements this optional feature 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 aggregate reputation counters are single-resource fields and are not paginated. The <tt>revocation_history</tt> member is a collection and MUST use the cursor pagination rules in <xref target="pagination-registry"/>. A response MUST include no more than the effective <tt>limit</tt> revocation history entries and MUST include a <tt>pagination</tt> object for continuing the history scan. Absence of older history on the current page MUST NOT be interpreted as absence of older revocations when <tt>pagination.has_more</tt> is true.</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="lifecycle-states" xml:base="sections/15-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 21.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>, unless the AID or token is otherwise revoked.
    Registries MUST retain retired public verification keys for at least the
    maximum Credential Token TTL plus clock skew after the key's
    <tt>valid_until</tt> time, so that conforming unexpired tokens remain
    verifiable. Relying Parties MUST perform the expiration preflight in
    Section 9 before historical key lookup so expired tokens return
    <tt>token_expired</tt> even after retired key material is no longer
    retained.
  </t>
  <t>
    Agents whose synced AIP Namespace Catalog entry has
    <tt>requires_task_id: true</tt> MUST have a non-null <tt>task_id</tt>.
    Namespace-specific lifecycle rules, including task-completion revocation
    expectations, are defined by the AIP Namespace Catalog. For the Draft-02
    catalog, ephemeral agents MUST be explicitly revoked on task completion.
    The Registry MUST also persist a <tt>lifecycle_expires_at</tt> deadline
    for each ephemeral agent at registration time. This deadline MUST be no
    later than the earlier of the decoded root Principal Token
    <tt>expires_at</tt> value and the initial Capability Manifest
    <tt>expires_at</tt> value. The Registry MUST automatically issue or
    materialise a <tt>full_revoke</tt> Revocation Object for the ephemeral AID
    with reason <tt>lifecycle_expired</tt> no later than 15 seconds after
    <tt>lifecycle_expires_at</tt> passes if the AID has not already been
    revoked. This auto-revocation requirement is a Registry conformance
    requirement; Relying Parties MUST still perform normal Principal Token,
    Capability Manifest, and revocation validation and MUST NOT rely on
    auto-revocation as the only expiry check.
  </t>
</section>
    <section anchor="principal-chain" xml:base="sections/16-principal-chain.xml">
  <name>Principal Chain</name>
  <t>
    The principal chain is the sequence of delegation relationships that
    connects a root human or organisational principal, identified by a W3C
    DID, to a leaf agent, identified by a <tt>did:aip</tt> AID. Each link in
    the chain is represented by a Principal Token
    (<xref target="principal-token"/>). The chain is embedded in Credential
    Tokens and Step Execution Tokens as the <tt>aip_chain</tt> array.
  </t>
  <t>
    Normative rules governing the principal chain are defined as follows:
  </t>
  <ul>
    <li>Chain structure and depth rules:
    <xref target="delegation-chain"/>.</li>
    <li>Capability scope inheritance across the chain:
    <xref target="scope-inheritance-rules"/>.</li>
    <li>Chain validation algorithm:
    <xref target="validation-step-8"/> Steps 8a through 8l and Post-Checks A,
    B, and C.</li>
    <li>Grant ceremony that establishes the first chain link:
    <xref target="principal-grant"/>.</li>
    <li>A2A identity chaining for multi-agent chains:
    <xref target="delegated-identity-chaining-a2a"/>.</li>
  </ul>
  <t>
    This section serves as the normative index for the principal chain
    concept. Implementations MUST consult the sections above for all
    chain-related requirements.
  </t>
</section>
    <section anchor="registry" numbered="true" xml:base="sections/17-registry.xml">
  <name>Registry Interface</name>
  <t>
    A conformant AIP Registry MUST implement the unconditional HTTP
    endpoints in this section and MUST implement each feature-conditional
    endpoint set for any feature, grant tier, or publication mode it
    advertises or enables.
  </t>

  <section anchor="registry-endpoints" numbered="true">
    <name>Endpoint Inventory</name>
    <t>
      A conformant AIP Registry MUST implement the following unconditional
      HTTP endpoints:
    </t>
    <table>
      <name>Unconditional Registry Endpoints</name>
      <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 Registration Metadata 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>POST</td><td>/v1/agents/{aid}/heartbeat</td><td>Submit signed agent heartbeat</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>Origin AIP CRL document; preferred CRL retrieval URI is published as <tt>endpoints.crl</tt></td></tr>
        <tr><td>PUT</td><td>/v1/agents/{aid}/overlays</td><td>Submit Capability Overlay</td></tr>
        <tr><td>GET</td><td>/v1/agents/{aid}/overlays</td><td>Retrieve current overlay</td></tr>
        <tr><td>GET</td><td>/v1/scopes</td><td>Synced AIP Scope Catalog metadata</td></tr>
        <tr><td>GET</td><td>/v1/namespaces</td><td>Synced AIP Namespace Catalog metadata</td></tr>
        <tr><td>GET</td><td>/v1/registry-metadata</td><td>Retrieve Registry Metadata</td></tr>
        <tr><td>GET</td><td>/v1/registry-trust/current</td><td>Retrieve current Registry Trust Record</td></tr>
        <tr><td>GET</td><td>/v1/registry-trust/{version}</td><td>Retrieve immutable Registry Trust Record by version</td></tr>
        <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 Approval Envelope</td></tr>
        <tr><td>POST</td><td>/v1/approvals/{id}/reject</td><td>Principal rejects Approval Envelope</td></tr>
        <tr><td>POST</td><td>/v1/approvals/{id}/steps/{n}/claim</td><td>Claim step for execution</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</td></tr>
        <tr><td>GET</td><td>/v1/approvals/{id}/steps/{n}</td><td>Retrieve step status for SET 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>
        <tr><td>GET</td><td>/v1/approvals/{id}/compensation-steps/{n}</td><td>Retrieve compensation step status for SET verification</td></tr>
      </tbody>
    </table>

    <t>
      The following endpoint sets are feature-conditional. A Registry MUST
      implement every endpoint in the applicable set when it advertises or
      enables the corresponding feature. A Registry MUST NOT advertise a
      feature in <tt>/v1/registry-metadata</tt>, the Registry Trust
      Record, or other discovery metadata unless it implements the complete
      endpoint set for that feature.
    </t>
    <dl newline="false">
      <dt>Segmented CRL publication</dt>
      <dd><tt>GET /v1/crl/segments/{segment_id}</tt> retrieves a signed CRL
      segment.</dd>

      <dt><tt>grant_tiers_supported</tt> includes <tt>G1</tt></dt>
      <dd><tt>POST /v1/grants</tt> submits a G1 grant, and
      <tt>GET /v1/grants/{grant_id}</tt> retrieves G1 grant status.</dd>

      <dt><tt>grant_tiers_supported</tt> includes <tt>G3</tt></dt>
      <dd><tt>POST /v1/oauth/authorize</tt> is the G3 authorization
      endpoint.</dd>

      <dt>G3 or token exchange supported</dt>
      <dd><tt>POST /v1/oauth/token</tt> is the G3 token endpoint and token
      exchange endpoint, and
      <tt>GET /.well-known/oauth-authorization-server</tt> returns AS
      metadata per <xref target="RFC8414"/>. Registries that support token
      exchange MUST also implement <tt>POST /v1/resources</tt>,
      <tt>GET /v1/resources/{resource_id}</tt>,
      <tt>PUT /v1/resources/{resource_id}</tt>, and
      <tt>DELETE /v1/resources/{resource_id}</tt> as defined in
      <xref target="resource-registration-registry"/>.</dd>

      <dt>Engagement Objects supported</dt>
      <dd><tt>POST /v1/engagements</tt> creates an Engagement Object,
      <tt>GET /v1/engagements/{id}</tt> retrieves it,
      <tt>POST /v1/engagements/{id}/countersign</tt> countersigns a proposed
      Engagement Object, and <tt>PUT /v1/engagements/{id}</tt> appends an
      Engagement change-log update.</dd>

      <dt>RPNP supported</dt>
      <dd><tt>POST /v1/subscriptions</tt> creates an RPNP subscription,
      <tt>GET /v1/subscriptions/{id}</tt> retrieves subscription status, and
      <tt>DELETE /v1/subscriptions/{id}</tt> cancels the subscription.</dd>

      <dt>Reputation Output supported</dt>
      <dd><tt>GET /v1/agents/{aid}/reputation</tt> returns advisory reputation
      data with the response invariants in <xref target="reputation-scoring"/>.
      A Registry that does not implement this feature MUST return HTTP 501 Not
      Implemented for this endpoint.</dd>

      <dt>Endorsement Acceptance supported</dt>
      <dd><tt>POST /v1/endorsements</tt> submits an Endorsement Object and
      applies the acceptance rules in
      <xref target="endorsement-acceptance-requirements"/>. A Registry that
      does not implement this feature MUST return HTTP 501 Not Implemented for
      this endpoint.</dd>
    </dl>
  </section>

  <section anchor="aid-encoding" numbered="true">
    <name>AID URL Encoding</name>
    <t>In all path parameters, the <tt>did:aip:</tt> 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" numbered="true">
    <name>Response Format</name>
    <t>All Registry responses MUST use <tt>Content-Type: application/json</tt>,
    except DID Document responses negotiated by
    <tt>Accept: application/did+json</tt> or
    <tt>Accept: application/did+ld+json</tt> from
    <tt>GET /v1/agents/{aid}</tt>, and CRL responses from
    <tt>GET /v1/crl</tt> or the signed <tt>endpoints.crl</tt> URI, which MUST
    use <tt>Content-Type: application/aip-crl+json</tt>.</t>
    <t>All Registry responses from AIP protocol endpoints MUST include
    <tt>X-AIP-Version</tt> as defined in Section 8.1. If a request version is
    unsupported, the Registry MUST reject the request with
    <tt>unsupported_version</tt> and include <tt>X-AIP-Version</tt> in the
    error response.</t>
    <t>
      Unless an endpoint profile explicitly requires OAuth-native error
      syntax, every Registry error response from an AIP protocol endpoint MUST
      conform to <tt>error-response.schema.json</tt> and the error response
      rules in <xref target="error-response-format"/>.
    </t>
    <t>All timestamps MUST be in ISO 8601 UTC format.</t>
  </section>

  <section anchor="agent-registration-metadata-registry" numbered="true">
    <name>Agent Registration Metadata</name>
    <t>
      Unless the client sends <tt>Accept: application/did+json</tt> or
      <tt>Accept: application/did+ld+json</tt>, <tt>GET /v1/agents/{aid}</tt>
      MUST return <tt>Content-Type: application/json</tt> with an Agent
      Registration Metadata response conforming to
      <tt>agent-registration.schema.json</tt>. If the AID is not registered,
      the Registry MUST return <tt>unknown_aid</tt>.
    </t>
    <t>
      The response MUST include the requested <tt>aid</tt>, the current Agent
      Identity Object as <tt>identity</tt>, the <tt>grant_tier</tt> accepted
      during Registration Envelope validation, <tt>registered_at</tt>,
      <tt>updated_at</tt>, and links for the current public key, current
      Capability Manifest, live revocation status, and the
      <tt>registration_warnings</tt> array. The <tt>identity</tt> field MUST be
      the current stored Agent Identity Object at the time of the most recent
      successful registration or key rotation, and <tt>identity.aid</tt> MUST
      equal the top-level <tt>aid</tt>.
      The <tt>registration_warnings</tt> array MAY be empty. If Registration
      Check 15 accepted a Tier 2 registration without
      <tt>identity.model.attestation_hash</tt>, the array MUST include the
      persisted <tt>model_attestation_missing_tier2</tt> warning until the
      registered Agent Identity Object includes a valid attestation hash.
    </t>
    <t>
      A successful <tt>POST /v1/agents</tt> response MUST use HTTP 201 and MUST
      return the same Agent Registration Metadata representation that a
      subsequent <tt>GET /v1/agents/{aid}</tt> request would return. Any
      registration warning created during validation MUST therefore be visible
      in the registration response and in later metadata reads.
    </t>
    <t>
      Relying Parties use the registered <tt>grant_tier</tt> value for each
      participating <tt>aip_chain</tt> AID during Step 9d Grant Tier
      Conformance validation.
    </t>
  </section>

  <section anchor="agent-key-rotation-registry" numbered="true">
    <name>Agent Key Rotation Endpoint</name>
    <t>
      <tt>PUT /v1/agents/{aid}</tt> is used only for key rotation of an
      existing AID. The request body MUST be a complete Agent Identity Object,
      not a Registration Envelope. The request MUST use
      <tt>Content-Type: application/json</tt> and MUST include a DPoP proof
      for the exact <tt>PUT</tt> request URI. For an ordinary rotation, the
      DPoP proof MUST verify against the current active public key for the
      AID before the rotation is committed. If the path <tt>{aid}</tt> is not
      already registered, the Registry MUST reject with
      <tt>unknown_aid</tt>.
    </t>
    <t>
      The DPoP proof for key rotation MUST include <tt>htu</tt>,
      <tt>htm</tt>, <tt>iat</tt>, and <tt>jti</tt>. The Registry MUST reject a
      missing proof with <tt>dpop_proof_required</tt> and MUST reject an
      invalid, stale, or replayed proof with <tt>invalid_token</tt>. The
      Registry MUST store enough recent DPoP <tt>jti</tt> values for each AID
      to reject replay within the accepted DPoP clock-skew window.
    </t>
    <t>
      The Registry MUST reject the request with <tt>invalid_request</tt> unless
      all of the following conditions hold:
    </t>
    <ol>
      <li>The percent-decoded path <tt>{aid}</tt> equals
      <tt>identity.aid</tt>.</li>
      <li>The submitted <tt>identity.version</tt> equals the currently stored
      identity version plus exactly 1.</li>
      <li>The submitted <tt>identity.public_key</tt> is an Ed25519 JWK
      conforming to the Agent Identity schema, and its <tt>kid</tt> equals
      <tt>identity.aid + "#key-" + identity.version</tt>.</li>
      <li>The submitted object changes only <tt>version</tt>,
      <tt>public_key</tt>, and <tt>previous_key_signature</tt>; all other Agent
      Identity fields MUST be byte-for-byte identical to the currently stored
      Agent Identity Object.</li>
      <li><tt>previous_key_signature</tt> is present, is a non-empty base64url
      string, and verifies using the immediately previous stored public key over
      the complete submitted Agent Identity Object serialized per Section 2.1
      with <tt>previous_key_signature</tt> temporarily set to
      <tt>""</tt>.</li>
    </ol>
    <t>
      The version check and the identity update MUST be performed in one
      atomic compare-and-commit operation against the currently stored identity
      version. If another rotation commits first, or if the submitted
      <tt>identity.version</tt> is not exactly the currently stored version
      plus 1, the Registry MUST reject with
      <tt>identity_version_conflict</tt> (HTTP 409). A Registry MUST NOT
      accept a rotation that skips a version number or overwrites a newer
      stored identity.
    </t>
    <t>
      Exact retries are idempotent. If the submitted Agent Identity Object is
      byte-for-byte identical to the current stored Agent Identity Object, the
      Registry MUST return HTTP 200 with the current Agent Registration
      Metadata and MUST NOT update timestamps, key states, or historical-key
      records. For this exact-retry case, the DPoP proof MAY verify against
      the current active key named by the submitted identity. A different
      request body for the same version is not an idempotent retry and MUST be
      rejected with <tt>identity_version_conflict</tt>.
    </t>
    <t>
      On first successful commit, the Registry MUST atomically store the
      submitted Agent Identity Object as the current identity, mark the
      previous key as retired, retain the previous public verification key for
      historical validation according to Section 19.7, and update the
      registration metadata <tt>updated_at</tt> timestamp. The successful
      response MUST be HTTP 200 with <tt>Content-Type: application/json</tt>
      and a body equal to the Agent Registration Metadata representation that a
      subsequent <tt>GET /v1/agents/{aid}</tt> would return. The Registry MUST
      NOT process key rotation through <tt>POST /v1/agents</tt>.
    </t>
  </section>

  <section anchor="agent-public-key-registry" numbered="true">
    <name>Agent Public-Key Endpoints</name>
    <t>
      A conformant AIP Registry MUST implement
      <tt>GET /v1/agents/{aid}/public-key</tt> and
      <tt>GET /v1/agents/{aid}/public-key/{key-id}</tt>. These endpoints
      provide the key material and validity interval required by Section 9
      Step 3 and Section 9 Step 8d-2.
    </t>
    <t>
      A successful response MUST use HTTP 200 with
      <tt>Content-Type: application/json</tt> and a body conforming to
      <tt>public-key-response.schema.json</tt>. The response MUST include
      <tt>aid</tt>, <tt>key_id</tt>, <tt>kid</tt>, <tt>jwk</tt>,
      <tt>valid_from</tt>, <tt>valid_until</tt>, and <tt>status</tt>.
      The <tt>kid</tt> value MUST equal
      <tt>{aid} + "#" + key_id</tt>, and <tt>jwk.kid</tt> MUST equal
      <tt>kid</tt>. The <tt>jwk</tt> member MUST contain Ed25519 public
      verification material.
    </t>
    <t>
      <tt>GET /v1/agents/{aid}/public-key</tt> returns the current active
      verification key for <tt>{aid}</tt>. For the active key,
      <tt>status</tt> MUST be <tt>"active"</tt>. The
      <tt>valid_until</tt> member MAY be <tt>null</tt> to indicate that the
      key has not yet been retired.
    </t>
    <t>
      <tt>GET /v1/agents/{aid}/public-key/{key-id}</tt> returns the retained
      key version identified by <tt>{key-id}</tt>, where <tt>{key-id}</tt>
      is the JOSE <tt>kid</tt> fragment without the leading <tt>#</tt>.
      Historical responses MUST set <tt>status</tt> to <tt>"retired"</tt>
      when a later key version is active. The Registry MUST retain historical
      keys according to Section 19.4.2.
    </t>
    <t>
      If <tt>{aid}</tt> is not registered, <tt>{key-id}</tt> is malformed,
      the requested key version is not found, or the requested key version is
      older than the Registry's conformant retention window, the Registry
      MUST reject with <tt>unknown_aid</tt> (HTTP 404). Revocation,
      suspension, or deactivation of an AID MUST NOT suppress public-key
      lookup for retained keys; Relying Parties need those keys to verify
      signatures before applying revocation and lifecycle checks.
    </t>
  </section>

  <section anchor="agent-capabilities-registry" numbered="true">
    <name>Agent Capability Manifest Endpoint</name>
    <t>
      A conformant AIP Registry MUST implement
      <tt>GET /v1/agents/{aid}/capabilities</tt>. A successful response MUST
      use HTTP 200 with <tt>Content-Type: application/json</tt> and a body
      conforming to <tt>capability-manifest.schema.json</tt>. The returned
      manifest MUST be the current Registry-stored Capability Manifest for
      <tt>{aid}</tt>, and <tt>manifest.aid</tt> MUST equal the
      percent-decoded path <tt>{aid}</tt>.
    </t>
    <t>
      If <tt>{aid}</tt> is not registered, the Registry MUST reject with
      <tt>unknown_aid</tt> (HTTP 404). If the Registry cannot return a
      stored manifest for a registered AID, or if the stored manifest is
      malformed, has an invalid signature, fails catalog constraint
      validation, or is not bound to <tt>{aid}</tt>, the Registry MUST reject
      with <tt>manifest_invalid</tt>. The Registry MUST return the current
      manifest even if <tt>expires_at</tt> has passed; Relying Parties then
      reject with <tt>manifest_expired</tt> under Section 9 Step 9.
    </t>
    <t>
      A conformant AIP Registry MUST also implement
      <tt>PUT /v1/agents/{aid}/capabilities</tt> to replace the current
      Capability Manifest. The request body MUST conform to
      <tt>capability-manifest.schema.json</tt>, MUST have <tt>aid</tt> equal
      to the percent-decoded path <tt>{aid}</tt>, MUST use a <tt>version</tt>
      value exactly one greater than the Registry's current stored manifest
      version for that AID, and MUST have a valid signature from
      <tt>granted_by</tt>. On success, the Registry MUST store the submitted
      manifest as the new current manifest and return HTTP 200 with the stored
      manifest body. Unknown AIDs return <tt>unknown_aid</tt>; malformed
      manifests, invalid signatures, version conflicts, catalog constraint
      failures, and AID mismatches return <tt>manifest_invalid</tt>; submitted
      manifests whose <tt>expires_at</tt> has already passed return
      <tt>manifest_expired</tt>.
    </t>
  </section>

  <section anchor="agent-revocation-status-registry" numbered="true">
    <name>Agent Revocation Status Endpoint</name>
    <t>
      A conformant AIP Registry MUST implement
      <tt>GET /v1/agents/{aid}/revocation</tt>. This endpoint returns the
      authoritative live revocation status used by Section 9 Step 7 for Tier
      2 validation.
    </t>
    <t>
      If the path <tt>{aid}</tt> does not identify a registered AID in the
      authoritative Registry, the Registry MUST reject with
      <tt>unknown_aid</tt> (HTTP 404). If the AID is registered, the Registry
      MUST return HTTP 200 with a JSON body conforming to
      <tt>revocation-status.schema.json</tt>, including when the AID has no
      active revocations.
    </t>
    <t>
      The response fields are:
    </t>
    <dl newline="false">
      <dt><tt>aid</tt></dt>
      <dd>The registered AID being checked. This value MUST equal the
      percent-decoded path <tt>{aid}</tt>.</dd>

      <dt><tt>checked_at</tt></dt>
      <dd>ISO 8601 UTC timestamp at which the authoritative Registry evaluated
      the revocation state.</dd>

      <dt><tt>status</tt></dt>
      <dd><tt>"active"</tt> when no active revocation applies,
      <tt>"restricted"</tt> when only <tt>scope_revoke</tt> or
      <tt>delegation_revoke</tt> applies, and <tt>"revoked"</tt> when
      <tt>full_revoke</tt> applies to the AID, when <tt>principal_revoke</tt>
      targets the AID, or when <tt>principal_revoke</tt> targets the root
      Principal DID associated with the AID's current registration
      authority.</dd>

      <dt><tt>revoked</tt></dt>
      <dd>Boolean. MUST be <tt>true</tt> when <tt>full_revoke</tt> or
      <tt>principal_revoke</tt> applies directly to the AID or through the
      AID's root Principal DID. Relying Parties MUST reject Credential Tokens
      for the AID when this value is <tt>true</tt>.</dd>

      <dt><tt>delegation_revoked</tt></dt>
      <dd>Boolean. MUST be <tt>true</tt> when an active
      <tt>delegation_revoke</tt> applies. Relying Parties MUST reject
      delegation-chain use rooted at this AID when this value is
      <tt>true</tt>.</dd>

      <dt><tt>scopes_revoked</tt></dt>
      <dd>Array containing the union of active <tt>scope_revoke</tt> scopes
      for the AID. The array is empty when no active scope revocation
      applies.</dd>

      <dt><tt>active_revocations</tt></dt>
      <dd>Array of the signed Revocation Objects that currently affect this
      AID. The array is empty when <tt>status</tt> is
      <tt>"active"</tt>. When a principal-wide <tt>principal_revoke</tt>
      affects the AID, this array MUST include the signed
      <tt>principal_revoke</tt> object whose <tt>target_id</tt> is the
      Principal DID.</dd>
    </dl>
    <t>
      A registered, non-revoked AID therefore returns HTTP 200 with
      <tt>status: "active"</tt>, <tt>revoked: false</tt>,
      <tt>delegation_revoked: false</tt>, an empty
      <tt>scopes_revoked</tt> array, and an empty
      <tt>active_revocations</tt> array.
    </t>
  </section>

  <section anchor="revocation-submission-registry" numbered="true">
    <name>Revocation Submission Endpoint</name>
    <t>
      A conformant AIP Registry MUST implement
      <tt>POST /v1/revocations</tt>. The request body MUST be a Revocation
      Object conforming to <tt>revocation-object.schema.json</tt>. Request
      validation, issuer authorization, idempotency, response behavior, CRL
      update effects, and child-propagation processing are defined by
      <xref target="revocation-submission-validation"/>.
    </t>
  </section>

  <section anchor="crl-response-registry" numbered="true">
    <name>CRL Response</name>
    <t>
      A conformant AIP Registry MUST implement <tt>GET /v1/crl</tt> as the
      canonical origin endpoint for the AIP Certificate Revocation List. The
      endpoint returns the signed CRL document defined in <xref target="crl"/> and
      conforming to <tt>crl.schema.json</tt>. Registries MAY publish an
      absolute CDN or distributed-object URI in the signed Registry Trust
      Record's <tt>signed.endpoints.crl</tt>; the origin endpoint and any
      published distribution URI MUST return the same current CRL payload or a
      byte-for-byte older payload that remains valid before its
      <tt>next_update</tt>.
    </t>
    <t>
      The CRL endpoint is not a generic paginated collection endpoint. Cursor
      pagination MUST NOT be applied to the <tt>revocations</tt> array of a
      signed CRL document because doing so would change the object covered by
      the CRL signature and could cause clients to validate against an
      incomplete revocation set.
    </t>
    <t>
      A Registry whose active CRL is too large to publish as a single
      efficient document MAY publish a segmented CRL. In that mode,
      <tt>GET /v1/crl</tt> returns a signed CRL index document whose
      <tt>signed.publication_mode</tt> is <tt>"index"</tt>, whose
      <tt>signed.revocations</tt> array is empty, and whose
      <tt>signed.segments</tt> array lists signed CRL segment documents. Each
      segment URI, including <tt>/v1/crl/segments/{segment_id}</tt> when used
      by the origin Registry, MUST return
      <tt>Content-Type: application/aip-crl+json</tt> and a signed CRL document whose
      <tt>signed.publication_mode</tt> is <tt>"segment"</tt>.
    </t>
    <t>
      The signed CRL index MUST identify the partitioning scheme with
      <tt>partition_key_alg: "sha-256-target-id-hex"</tt>. Segment ranges are
      inclusive ranges over the lowercase hexadecimal SHA-256 digest of the
      Revocation Object <tt>target_id</tt> string. Segment ranges in one CRL
      index MUST be non-overlapping and together cover every active
      Revocation Object in the indexed CRL. A Relying Party using a segmented
      CRL for bounded-staleness validation MUST retrieve and verify every
      segment whose range contains the partition key for any target identifier
      it must check, including the token's root Principal DID and every AID in
      the validated <tt>aip_chain</tt>.
    </t>
    <t>
      For each referenced segment, the Relying Party MUST verify the segment
      CRL signature against the Registry Trust Record identified by the
      segment's <tt>signed.trust_record_version</tt>, verify that the
      segment's <tt>registry_id</tt>, <tt>crl_id</tt>, <tt>sequence</tt>,
      <tt>issued_at</tt>, and <tt>next_update</tt> match the signed index,
      and verify that the JCS canonicalized segment document hashes to the
      segment reference's <tt>sha256</tt> value. If any required segment is
      unavailable or fails verification, bounded-staleness validation MUST
      fail with <tt>registry_unavailable</tt>.
    </t>
  </section>

  <section anchor="agent-heartbeat-registry" numbered="true">
    <name>Agent Heartbeat Endpoint</name>
    <t>
      A conformant AIP Registry MUST implement
      <tt>POST /v1/agents/{aid}/heartbeat</tt>. This endpoint records
      liveness for the AID identified by the path parameter and is the
      submission mechanism used by the optional Dead Man's Switch in
      Section 21.8.
    </t>
    <t>
      The request MUST include <tt>Authorization: DPoP &lt;token&gt;</tt>,
      where <tt>&lt;token&gt;</tt> is a Credential Token issued by the same
      AID as the path parameter. The Registry MUST validate the Credential
      Token using the Section 9 validation algorithm with the Registry as
      the Relying Party. The token <tt>aud</tt> claim MUST identify either
      the Registry's <tt>registry_id</tt> or the heartbeat endpoint URI.
      The token <tt>aip_scope</tt> MUST include <tt>registry.heartbeat</tt>.
    </t>
    <t>
      The request MUST include a DPoP proof bound to the HTTP method and
      heartbeat URI. If the DPoP proof is absent, reject with
      <tt>dpop_proof_required</tt>. If the proof is malformed or invalid,
      reject with <tt>invalid_token</tt>.
    </t>
    <t>
      After Credential Token validation, the Registry MUST verify that the
      path <tt>{aid}</tt> equals both the Credential Token issuer
      (<tt>iss</tt>) and the leaf Principal Token subject
      (<tt>aip_chain[n-1].sub</tt>). If either value differs from the path
      AID, reject with <tt>invalid_token</tt>. If the token does not contain
      <tt>registry.heartbeat</tt>, or if the current Capability Manifest does
      not grant <tt>registry.heartbeat</tt>, reject with
      <tt>insufficient_scope</tt>.
    </t>
    <t>
      On success, the Registry MUST record <tt>last_heartbeat_at</tt> using
      the Registry's receipt time. Client-supplied body timestamps, if any,
      MUST NOT be used to advance liveness. A successful heartbeat MAY also
      update the agent's <tt>last_active</tt> reputation metadata.
    </t>
  </section>

  <section anchor="approval-endpoints-registry" numbered="true">
    <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 (Principal Wallet call; sets <tt>principal_signature</tt>)</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>
        <tr><td>GET</td><td>/v1/approvals/{id}/compensation-steps/{n}</td><td>Get compensation step status (used by Relying Parties for Step Execution Token verification)</td></tr>
      </tbody>
    </table>

    <t><strong><tt>POST /v1/approvals</tt> validation:</strong> The Registry
    MUST validate the request body as an
    <tt>ApprovalEnvelopeSubmission</tt>, reject any read-only stored-resource
    fields in that body, and perform all checks defined in Section 13.10
    before accepting an Approval Envelope. The Registry MUST return HTTP 201
    with the <tt>StoredApprovalEnvelope</tt>, including the Registry-assigned
    <tt>status: "pending_approval"</tt> and initial step statuses, on
    success.</t>
    <t><strong><tt>POST /v1/approvals/{id}/approve</tt> flow:</strong> This
    endpoint is called by the Principal Wallet after the principal completes
    the signing ceremony. The request body MUST contain the
    <tt>principal_signature</tt> field: a base64url EdDSA signature computed
    by the principal's Principal Wallet over the immutable approval signing input
    defined in Section 13.3. That signing input is the accepted
    <tt>ApprovalEnvelopeSubmission</tt> content plus
    <tt>creator_signature</tt> and <tt>principal_signature: ""</tt>; it
    excludes Registry-managed <tt>status</tt>, <tt>approved_at</tt>, and step
    status/timestamp fields. The signing key MUST be a verification method
    controlled by <tt>principal_id</tt>. The Registry MUST verify this
    signature against the resolved <tt>principal_id</tt> DID Document before
    transitioning the envelope to <tt>approved</tt>.</t>
    <t><strong>Atomicity requirement for approval:</strong> The Registry MUST
    implement <tt>POST /v1/approvals/{id}/approve</tt> and
    <tt>POST /v1/approvals/{id}/reject</tt> as atomic lifecycle transitions.
    Approval MUST be a conditional update from <tt>pending_approval</tt> to
    <tt>approved</tt> and MUST succeed only when the request is committed
    before <tt>approval_window_expires_at</tt>. Rejection MUST be a
    conditional update from <tt>pending_approval</tt> to <tt>rejected</tt>.
    Only one concurrent approval or rejection request for the same Approval
    Envelope can succeed. If another Principal Wallet, expiry job, cancellation cascade,
    approval, or rejection has already changed the envelope state, the Registry
    MUST reject the losing request with <tt>approval_state_conflict</tt> and
    MUST NOT overwrite the stored <tt>principal_signature</tt>,
    <tt>approved_at</tt>, or terminal state. If the envelope is still in
    <tt>pending_approval</tt> and <tt>approval_window_expires_at</tt> has
    passed, the Registry MUST reject an approval request with
    <tt>approval_envelope_expired</tt> and transition or leave the envelope in
    <tt>expired</tt>. A byte-for-byte retry of the winning approval request MAY
    return the stored approved envelope as an idempotent success.</t>
    <t><strong>Atomicity requirement for step claim:</strong> The Registry MUST
    implement step-claim operations atomically, for example 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
    <tt>approval_step_already_claimed</tt>.</t>
    <t><strong>Step endpoint request and response bodies:</strong> Forward-step
    and compensation-step <tt>claim</tt>, <tt>complete</tt>, and
    <tt>fail</tt> request and response bodies MUST conform to the profiles in
    <tt>approval-step-endpoints.schema.json</tt>. Complete and fail requests
    MUST present the compact Step Execution Token issued for the active claim.
    The Registry MUST validate the SET, match it to the request path and
    stored claim <tt>jti</tt>, enforce the claim deadline, and process exact
    idempotent retries as defined in Section 13.8.</t>
    <t><strong>Step Execution Token format:</strong> The Step Execution Token returned by
    <tt>POST /v1/approvals/{approval_id}/steps/{step_id}/claim</tt> or
    <tt>POST /v1/approvals/{approval_id}/compensation/{compensation_step_id}/claim</tt>
    is a Registry-issued JWT with JOSE
    <tt>typ: "AIP-SET+JWT"</tt>, <tt>alg: "EdDSA"</tt>, and a
    <tt>kid</tt> matching a JWK in
    <tt>active_verification_keys.step_execution</tt> of the Registry Trust
    Record version current at issuance time. The protected JOSE header MUST
    also include <tt>aip_trv</tt> equal to that Registry Trust
    Record version. Its payload claims are described in Section 13.8. Its TTL
    MUST comply with the Step Execution Token TTL derivation rule in
    <xref target="step-execution-token"/>.</t>
  </section>

  <section anchor="resource-registration-registry" numbered="true">
    <name>Resource Registration</name>
    <t>
      AIP Registries that support token exchange
      (<xref target="token-exchange-mcp"/>) MUST maintain a resource registry
      mapping target resource URIs to their authorization configuration. Each
      Registered Resource Record MUST conform to
      <tt>resource-record.schema.json</tt> and MUST include:
    </t>
    <dl newline="false">
      <dt><tt>resource_uri</tt></dt>
      <dd>string, REQUIRED. The RFC 9728 resource identifier.</dd>

      <dt><tt>authorization_server</tt></dt>
      <dd>string, REQUIRED. The URI of the authorization server for this
      resource, either the AIP Registry itself or an external authorization
      server.</dd>

      <dt><tt>enterprise_idp_required</tt></dt>
      <dd>boolean, OPTIONAL. When true, token exchange requests targeting this
      resource MUST use the Enterprise IdP Federation Profile
      (<xref target="enterprise-idp-federation-profile"/>). Default:
      <tt>false</tt>.</dd>

      <dt><tt>enterprise_idp_token_endpoint</tt></dt>
      <dd>string, CONDITIONAL. REQUIRED when
      <tt>enterprise_idp_required</tt> is <tt>true</tt>. The Enterprise IdP's
      token endpoint URI.</dd>

      <dt><tt>enterprise_idp_client_id</tt></dt>
      <dd>string, CONDITIONAL. REQUIRED when
      <tt>enterprise_idp_required</tt> is <tt>true</tt>. The Registry's
      registered <tt>client_id</tt> with the Enterprise IdP.</dd>
    </dl>
    <t>
      Resource records are addressed by <tt>resource_id</tt>, the lowercase
      hexadecimal SHA-256 digest of the UTF-8 <tt>resource_uri</tt> value. The
      Registry MUST reject a create request whose path-derived or
      body-supplied <tt>resource_id</tt> does not equal that digest with
      <tt>registration_invalid</tt>. The Registry MUST reject duplicate
      <tt>resource_uri</tt> create requests with HTTP 409 unless the submitted
      record is byte-for-byte identical to the stored record, in which case it
      MAY return the existing record.
    </t>
    <t>
      A Registry MUST NOT accept unauthenticated public writes to the resource
      registry. The registering caller MUST be authenticated as a Registry
      operator, the resource owner, or another party authorized by local
      Registry policy. The Registry MUST validate that
      <tt>resource_uri</tt>, <tt>authorization_server</tt>, and any
      <tt>enterprise_idp_token_endpoint</tt> are absolute HTTPS URIs unless a
      local non-production policy explicitly allows another URI scheme.
    </t>
    <t>
      When <tt>enterprise_idp_required</tt> is <tt>true</tt>, the Registry
      MUST reject the record unless
      <tt>enterprise_idp_federation_supported</tt> is true in Registry
      discovery metadata and both <tt>enterprise_idp_token_endpoint</tt> and
      <tt>enterprise_idp_client_id</tt> are present. Token exchange requests
      whose <tt>resource</tt> parameter resolves to such a record MUST follow
      the Enterprise IdP Federation Profile in
      <xref target="enterprise-idp-federation-profile"/>. Token exchange
      requests for an unknown <tt>resource</tt> value MUST be rejected with
      <tt>invalid_target</tt>.
    </t>
    <t>
      The resource registration endpoints have the following semantics:
      <tt>POST /v1/resources</tt> creates a Registered Resource Record,
      <tt>GET /v1/resources/{resource_id}</tt> retrieves one record,
      <tt>PUT /v1/resources/{resource_id}</tt> replaces one record atomically,
      and <tt>DELETE /v1/resources/{resource_id}</tt> removes the record from
      future token-exchange matching. Delete does not revoke already issued
      enterprise IdP access tokens; those tokens remain governed by the
      enterprise IdP's own lifetime and revocation mechanisms.
    </t>
  </section>

  <section anchor="oauth-registry" numbered="true">
    <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="I-D.ietf-oauth-v2-1"/> with the following requirements:</t>
    <ol>
      <li>The authorization endpoint MUST be published in the Registry's well-known configuration at <tt>/.well-known/oauth-authorization-server</tt> per <xref target="RFC8414"/>.</li>
      <li>PKCE <xref target="RFC7636"/> with <tt>code_challenge_method: "S256"</tt> is REQUIRED for all authorization requests.</li>
      <li>For G3 grant ceremonies, the authorization request MUST include <tt>aip_grant_request</tt> containing the compact-JWS-signed GrantRequest defined in Section 12.2, and the Authorization Server MUST enforce the G3 binding checks in Section 12.10 before issuing an authorization code.</li>
      <li>The <tt>scope</tt> parameter MUST use AIP scope strings from the synced AIP Scope Catalog or accepted local extension policy.</li>
      <li>The token endpoint MUST redeem an authorization code only against the immutable GrantRequest binding stored during authorization. It MUST reject unbound, expired, replayed, or binding-mismatched authorization codes with <tt>grant_request_invalid</tt>.</li>
      <li>For G3 grant ceremonies, the token endpoint response MUST follow the
      G3 token endpoint response mapping in Section 12.10. The
      <tt>access_token</tt> value is a transport alias for the compact
      Principal Token and MUST be byte-for-byte identical to
      <tt>grant_response.principal_token</tt>.</li>
      <li>The Principal Token payload returned by the token endpoint MUST
      include <tt>acr</tt> and <tt>amr</tt> claims reflecting the authentication
      performed.</li>
      <li>The authorization server MUST support the <tt>acr_values</tt> parameter to declare minimum identity-proofing requirements.</li>
      <li>DPoP [RFC9449] MUST be supported on the token endpoint.</li>
    </ol>

    <t>
      If an authorization request is missing <tt>code_challenge</tt>, is
      missing <tt>code_challenge_method</tt>, or has a
      <tt>code_challenge_method</tt> value other than <tt>S256</tt>, the
      Authorization Server MUST reject the request with
      <tt>pkce_required</tt>.
    </t>

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

    <section anchor="well-known-metadata-registry" numbered="true">
      <name>Registry Metadata Additions</name>
      <t>The <tt>/v1/registry-metadata</tt> response MUST also include:</t>
      <dl newline="true" spacing="normal">
        <dt><tt>registry_trust_uri</tt> (string)</dt>
        <dd>URI of the current Registry Trust Record.</dd>
        <dt><tt>grant_tiers_supported</tt> (array)</dt>
        <dd>Supported grant tiers: <tt>["G1", "G2", "G3"]</tt>.</dd>
        <dt><tt>acr_values_supported</tt> (array)</dt>
        <dd>Supported <tt>acr</tt> values.</dd>
        <dt><tt>acr_equivalence_map</tt> (object)</dt>
        <dd>Optional JSON object whose keys are ACR value strings received in <tt>aip_chain[0]</tt> and whose values are arrays of ACR value strings that the key maps to. A key ACR value satisfies a requirement for any ACR value in its array. Registries MUST only declare downward equivalences, where received assurance is equal to or higher than required assurance under a documented ACR equivalence policy. If absent, exact ACR matching is required with no exceptions.</dd>
        <dt><tt>amr_values_supported</tt> (array)</dt>
        <dd>Supported <tt>amr</tt> values.</dd>
        <dt><tt>oauth_authorization_server</tt> (string)</dt>
        <dd>URI of the OAuth AS metadata document; present only if G3 is supported.</dd>
        <dt><tt>endpoints.resources</tt> (string)</dt>
        <dd>Resource registration collection endpoint. REQUIRED when token
        exchange is supported.</dd>
        <dt><tt>identity_proofing_required_for_tier2</tt> (boolean)</dt>
        <dd>Whether this Registry requires G3 for Tier 2 operations. When true, enforced during Registration Check 14c, Validation Step 8 Post-Check C, and Validation Step 9d.</dd>
        <dt><tt>mtls_client_certificate_profile</tt> (string)</dt>
        <dd>Client-certificate binding profile for Tier 3 operations. When Tier 3 is accepted, this value MUST be <tt>"aip-san-uri-v1"</tt>.</dd>
        <dt><tt>mtls_trust_anchors_uri</tt> (string)</dt>
        <dd>HTTPS URI documenting the Tier 3 client-certificate trust anchor set and revocation-checking mechanism. REQUIRED when Tier 3 is accepted.</dd>
        <dt><tt>enterprise_idp_federation_supported</tt> (boolean)</dt>
        <dd>Whether the Registry supports the Enterprise IdP Federation Profile in <xref target="enterprise-idp-federation-profile"/>. SHOULD be present in all Registry Metadata documents. Absence is treated as equivalent to false. Gateways that surface this capability through <tt>/.well-known/oauth-protected-resource</tt> SHOULD ensure the value is consistent with the Registry's declared support in this document. A mismatch between the two documents MUST be treated as a Registry configuration error.</dd>
        <dt><tt>enterprise_idp_token_exchange_grant_types_supported</tt> (array)</dt>
        <dd>Enterprise IdP grant types the Registry can use for secondary exchange. Values SHOULD include RFC 7523 JWT bearer and/or RFC 8693 token exchange when federation is supported.</dd>
        <dt><tt>enterprise_idp_issuers_supported</tt> (array)</dt>
        <dd>OIDC issuer identifiers or issuer patterns accepted for enterprise principal assertions and secondary token exchange, subject to local trust policy.</dd>
        <dt><tt>aip_catalog_uri</tt> (string)</dt>
        <dd>URI of the synced AIP Catalog bundle or mirror.</dd>
        <dt><tt>aip_catalog_version</tt> (string)</dt>
        <dd>Draft-aligned catalog version, e.g. <tt>draft-02</tt>.</dd>
        <dt><tt>aip_catalog_snapshot_id</tt> (string)</dt>
        <dd>Immutable Catalog Snapshot identifier.</dd>
        <dt><tt>aip_catalog_sha256</tt> (string)</dt>
        <dd>Lowercase SHA-256 digest of the exact catalog bundle bytes, formatted as <tt>sha256:&lt;64hex&gt;</tt>.</dd>
        <dt><tt>step_claim_timeout_seconds</tt> (integer)</dt>
        <dd>Registry claim timeout for Approval Envelope steps. MUST be 1-600 seconds inclusive.</dd>
      </dl>
    </section>
  </section>

  <section anchor="scope-map-registry" numbered="true">
    <name>AIP Catalog Sync, Scope Map, and Namespace Map</name>
    <t>
      Concrete AIP scope, scope-family, and namespace registrations are
      defined by an immutable AIP Catalog Snapshot. This specification defines
      the wire grammar, Registry sync requirements, integrity metadata,
      validation semantics, and minimum Draft-02 catalog content. The catalog
      defines the concrete entries and their metadata for each draft snapshot.
    </t>
    <t>
      The canonical list of built-in AIP scopes, scope families, and
      namespaces is the set of entries with <tt>class: "standard"</tt> in the
      applicable Catalog Snapshot. Entries with <tt>class: "standard"</tt> in
      that snapshot are the only built-in AIP registrations for that draft
      line. A Registry, deployer, or Relying Party MUST NOT mint a new
      standard AIP scope, scope family, or namespace by local policy alone;
      interoperable standard entries MUST appear in an applicable AIP Catalog
      Snapshot.
    </t>
    <t>
      The Draft-02 Catalog Snapshot is identified by
      <tt>catalog_version: "draft-02"</tt> and
      <tt>catalog_snapshot_id: "https://provai.dev/aip/catalog/draft-02"</tt>. The
      authoritative Draft-02 Catalog Snapshot is the deterministic
      <tt>dist/catalog.json</tt> release artifact from the separate
      <tt>provai-dev/aip-catalog</tt> repository for the immutable
      Draft-02 release. A conformant Registry for this draft MUST publish, in
      <tt>/v1/registry-metadata</tt>, an HTTPS
      <tt>aip_catalog_uri</tt> from which the exact JSON Catalog Bundle can be
      retrieved and an <tt>aip_catalog_sha256</tt> value containing the
      lowercase SHA-256 digest of the exact UTF-8 bytes retrieved from that
      URI. A mirror is conformant only when it serves bytes whose SHA-256
      digest equals the published <tt>aip_catalog_sha256</tt> value and whose
      JSON content carries <tt>catalog_version: "draft-02"</tt>,
      <tt>aip_draft: "draft-02"</tt>, and
      <tt>catalog_source_uri: "https://github.com/provai-dev/aip-catalog"</tt>.
      The Registry metadata field <tt>aip_catalog_snapshot_id</tt> identifies
      the pinned snapshot; it is not required to be duplicated inside the
      catalog JSON bundle.
    </t>
    <t>
      Draft-02 conformance MUST be based on the pinned Catalog Snapshot
      identified above. A Registry MUST NOT use a <tt>latest</tt>,
      <tt>latest-live</tt>, branch head, mutable tag, or other moving catalog
      input to determine standard Draft-02 scope, scope-family, namespace, or
      security metadata. Changes to any standard entry, including changes to
      <tt>tier</tt>, <tt>destructive</tt>, <tt>requires_dpop</tt>,
      <tt>ttl_max_seconds</tt>, <tt>grant_tier_min</tt>,
      <tt>constraint_schema</tt>, <tt>status</tt>, or
      <tt>class</tt>, require a new <tt>catalog_snapshot_id</tt>, a new
      <tt>aip_catalog_sha256</tt>, and a corresponding AIP draft or protocol
      version update before they affect Draft-02 conformance.
    </t>
    <t>
      The Draft-02 JSON Catalog Bundle MUST be generated by the validation
      and distribution tooling in the <tt>aip-catalog</tt> repository. The
      bundle MUST contain top-level <tt>catalog_name</tt>,
      <tt>catalog_version</tt>, <tt>aip_draft</tt>,
      <tt>catalog_source_uri</tt>, <tt>scopes</tt>,
      <tt>scope_families</tt>, and <tt>namespaces</tt> members. The
      <tt>namespaces</tt> array MUST include standard entries for
      <tt>personal</tt>, <tt>enterprise</tt>, <tt>service</tt>,
      <tt>orchestrator</tt>, and <tt>ephemeral</tt>, and a reserved entry for
      <tt>registry</tt>. The
      <tt>scope_families</tt> array MUST include standard entries for
      <tt>transactions.*</tt> and <tt>communicate.*</tt>. The
      <tt>scopes</tt> array MUST include standard entries for
      <tt>transactions</tt>, <tt>web.forms_submit</tt>,
      <tt>email.send</tt>, <tt>web.download</tt>,
      <tt>spawn_agents.create</tt>, <tt>spawn_agents.manage</tt>, and
      <tt>approvals.create</tt>. A
      missing required standard entry, missing required security or provenance
      metadata field, stale generated bundle, or digest mismatch invalidates
      the Registry's Draft-02 catalog conformance.
    </t>
    <t>
      For standard AIP scope entries in the Draft-02 catalog, every active or
      experimental scope with <tt>destructive: true</tt> MUST also have
      <tt>requires_dpop: true</tt>. The Draft-02 catalog additionally marks
      non-destructive but high-risk external-effect scopes
      <tt>email.send</tt>, <tt>web.download</tt>, and
      <tt>web.forms_submit</tt> with <tt>requires_dpop: true</tt>. A Registry
      mirror MUST NOT weaken these <tt>requires_dpop</tt> values for
      standard scopes. The <tt>requires_dpop</tt> flag is additive: Tier 2
      and Tier 3 operations require DPoP under Section 9 Step 10 regardless
      of this flag, while Tier 1 scopes and endpoints can use the flag or
      endpoint policy to require DPoP.
    </t>
    <t>
      Deployers and Registry operators MAY expose non-standard scopes only as
      explicit extensions. Extension entries intended for cross-Registry
      interoperability MUST be registered in the AIP Catalog. Until accepted
      in an applicable catalog snapshot, such entries are local extensions.
      A deployer MUST NOT create a valid scope merely by including an unknown
      string in a Capability Manifest; the Registry MUST first accept and
      publish the extension entry in its Scope Map.
      Local or community extension scope identifiers MUST use the
      <tt>x.</tt> prefix followed by a namespace label controlled by the
      Registry or deployer, for example <tt>x.example.payments.release</tt>.
      The namespace label MUST either be an active namespace entry in the
      Registry's Namespace Map or a local label documented by the Registry's
      extension policy, and that policy MUST state how control of the label is
      established. The namespace label and all following segments MUST satisfy
      the scope string grammar below. Local extensions MUST NOT shadow,
      override, or weaken any standard catalog scope, scope family, namespace,
      or reserved prefix.
    </t>
    <t>
      Extension scopes are not Tier 1 by default. Every exposed scope entry,
      including local and community extensions, MUST carry explicit values for
      <tt>tier</tt>, <tt>destructive</tt>, <tt>requires_dpop</tt>,
      <tt>ttl_max_seconds</tt>, <tt>grant_tier_min</tt>,
      <tt>constraint_schema</tt>, <tt>status</tt>, <tt>class</tt>,
      <tt>owner</tt>, <tt>source</tt>, and <tt>change_type</tt>. If a
      Registry cannot determine any required security or provenance metadata
      for an extension scope, it MUST NOT
      expose that scope as valid. Registration, grant, or validation attempts
      that depend on such an incomplete scope MUST be rejected with
      <tt>registration_invalid</tt> at registration time,
      <tt>grant_request_invalid</tt> during grant consent, or
      <tt>invalid_scope</tt> at token validation time.
    </t>
    <t>
      The standard <tt>web.forms_submit</tt> scope is classified as Tier 2 in
      the Draft-02 catalog, with <tt>grant_tier_min: "G2"</tt> and
      <tt>ttl_max_seconds: 300</tt>. It does not inherit the generic
      <tt>web.*</tt> Tier 1 default because form submission can create
      external side effects including account changes, data submissions, and
      financial form submissions.
    </t>
    <t>
      AIP does not define a URN namespace for scope identifiers. The canonical
      on-wire AIP scope value is the dot-notation scope string:
    </t>
    <artwork>
  scope_string = dot-separated-scope-name
    </artwork>
    <t>
      Scope strings in the Scope Map MUST match
      <tt>&lt;dot-separated-scope-name&gt;</tt>. This allows digits
      within each dot-separated segment after the first character. The same
      short string form MUST be used in OAuth <tt>scope</tt> parameters and
      within AIP Credential Tokens (<tt>aip_scope</tt> array).
      Catalog entries also include a <tt>uri</tt> member for documentation and
      catalog linking. Standard Draft-02 Catalog entries MUST use HTTPS URIs
      under <tt>https://provai.dev/aip/scopes/</tt>; these HTTPS URIs are not
      the on-wire OAuth scope values.
    </t>
    <t>
      A conformant AIP Registry MUST implement <tt>GET /v1/scopes</tt>
      returning a JSON object with catalog sync metadata and an array of scope
      entries. The response object MUST include <tt>aip_draft</tt>,
      <tt>catalog_version</tt>, <tt>catalog_snapshot_id</tt>,
      <tt>catalog_source_uri</tt>, <tt>catalog_sha256</tt>,
      <tt>synced_at</tt>, <tt>scopes</tt>, and
      <tt>pagination</tt>. It MUST also include
      <tt>extension_policy_uri</tt>. The
      <tt>extension_policy_uri</tt> value MUST be non-null when the response
      includes any <tt>community</tt> scope entry or any locally documented
      extension scope and MAY be null otherwise.
      Pagination MUST follow <xref target="pagination-registry"/> and cursors
      MUST be bound to the advertised <tt>catalog_version</tt>. Each scope
      entry MUST include
      <tt>id</tt>, <tt>uri</tt>, <tt>family</tt>, <tt>description</tt>,
      <tt>tier</tt>, <tt>destructive</tt>, <tt>requires_dpop</tt>,
      <tt>ttl_max_seconds</tt>, <tt>grant_tier_min</tt>,
      <tt>constraint_schema</tt>, <tt>status</tt>, <tt>class</tt>,
      <tt>owner</tt>, <tt>introduced_in</tt>, <tt>updated_in</tt>,
      <tt>change_type</tt>, and <tt>source</tt>.
      The <tt>class</tt> value MUST be one of <tt>standard</tt>,
      <tt>community</tt>, or <tt>reserved</tt>.
    </t>
    <t>
      Principal Wallet consent UIs MUST use the standardized display titles
      in Section 12.3 for standard Draft-02 scopes. For community or local
      extension scopes, the Registry's extension policy MUST provide a
      non-empty human-readable title and summary for each exposed extension
      scope before that scope can be used in a grant ceremony. The display
      title or summary MUST NOT broaden the authorization represented by the
      scope metadata.
    </t>
    <t>
      When <tt>constraint_schema</tt> is non-null, it MUST be a JSON Schema
      Draft 2020-12 fragment associated with the containing
      <tt>catalog_version</tt>. Any change to a non-null
      <tt>constraint_schema</tt> MUST update the scope entry's
      <tt>updated_in</tt> value and MUST be reviewed as a catalog change.
    </t>
    <t>
      A conformant AIP Registry MUST implement <tt>GET /v1/namespaces</tt>
      returning a JSON object with catalog sync metadata and an array of
      namespace entries. The response object MUST include
      <tt>aip_draft</tt>, <tt>catalog_version</tt>,
      <tt>catalog_snapshot_id</tt>, <tt>catalog_source_uri</tt>,
      <tt>catalog_sha256</tt>, <tt>synced_at</tt>, and
      <tt>namespaces</tt>. It MUST also include <tt>pagination</tt>.
      It MUST include <tt>extension_policy_uri</tt> when the response includes
      any <tt>community</tt> namespace entry or any locally documented
      extension namespace and MAY use null otherwise.
      Pagination MUST follow <xref target="pagination-registry"/> and cursors
      MUST be bound to the advertised <tt>catalog_version</tt>. Each namespace
      entry MUST include <tt>id</tt>,
      <tt>description</tt>, <tt>reserved</tt>, <tt>spawnable</tt>,
      <tt>requires_task_id</tt>, <tt>lifecycle_rules</tt>,
      <tt>status</tt>, <tt>class</tt>, <tt>owner</tt>,
      <tt>introduced_in</tt>, <tt>updated_in</tt>, <tt>change_type</tt>, and
      <tt>source</tt>. The <tt>class</tt> value MUST be one of
      <tt>standard</tt>, <tt>community</tt>, or <tt>reserved</tt>.
    </t>
    <t>
      A Registry MUST expose only catalog entries that are present in its
      synced catalog snapshot or in an explicitly documented local or community
      extension policy. A Relying Party MUST treat <tt>active</tt> entries as
      valid. It MAY accept <tt>experimental</tt> entries only when local
      policy allows experimental behavior. It MUST reject <tt>reserved</tt>,
      <tt>removed</tt>, unknown, or conflicting entries with
      <tt>invalid_scope</tt> for scopes or <tt>registration_invalid</tt> for
      registration-time namespace failures.
    </t>
    <t>
      Registries MUST validate submitted Capability Manifests and Capability
      Overlays against each applicable non-null <tt>constraint_schema</tt>
      from the synced Scope Catalog. Registration submissions that fail this
      validation MUST be rejected with <tt>registration_invalid</tt>.
      Capability Overlay submissions that fail this validation MUST be
      rejected with <tt>overlay_exceeds_manifest</tt>. Relying Parties
      performing Step 9 or Step 9b validation MUST apply the same non-null
      <tt>constraint_schema</tt> fragments before treating manifest or overlay
      constraints as authoritative.
    </t>
    <t>
      Registries MUST expose the <tt>constraint_schema</tt> fragments that
      correspond to their advertised <tt>catalog_version</tt>. A Registry
      MUST NOT silently substitute a schema fragment from a different catalog
      version for a standard scope. Local or community extension schema
      changes MUST update the affected entry's <tt>updated_in</tt> value and
      MUST be documented by the Registry's extension policy before the changed
      schema is used for registration or token validation.
    </t>
    <t>
      Scope-family entries in the AIP Catalog define category notation such
      as <tt>transactions.*</tt> and <tt>communicate.*</tt>. Category notation
      is not a literal scope string and MUST NOT appear as a token
      <tt>aip_scope</tt> value unless it is separately registered as a concrete
      scope.
    </t>
  </section>

  <section anchor="capability-overlay-endpoints-registry" numbered="true">
    <name>Capability Overlay Endpoints</name>
    <t>
      <tt>PUT /v1/agents/{aid}/overlays</tt> submits a Capability Overlay for
      the AID in the path. The request body MUST conform to the Capability
      Overlay schema in Section 5.11, and the body <tt>aid</tt> value MUST
      equal the path <tt>{aid}</tt>.
    </t>
    <t>
      The Registry MUST reject overlays whose <tt>issued_by</tt> DID uses
      <tt>did:key</tt> with <tt>overlay_issuer_invalid</tt>. The Registry
      MUST verify the overlay signature over the Section 2.1 signing input
      using the Ed25519 verification method identified by
      <tt>signature_kid</tt>. The DID portion of <tt>signature_kid</tt> MUST
      equal <tt>issued_by</tt>. If the signature is absent, malformed, cannot
      be verified, or verifies under a key not controlled by
      <tt>issued_by</tt>, the Registry MUST reject with
      <tt>overlay_signature_invalid</tt>.
    </t>
    <t>
      For the tuple <tt>(aid, engagement_id, issued_by)</tt>, a submitted
      overlay <tt>version</tt> MUST be strictly greater than the current
      active overlay version for that tuple. If the submitted version is less
      than or equal to the current active version, the Registry MUST reject
      with <tt>overlay_version_conflict</tt>. If the overlay expands the base
      Capability Manifest or fails catalog constraint validation, the Registry
      MUST reject with <tt>overlay_exceeds_manifest</tt>.
    </t>
  </section>

  <section anchor="engagement-endpoints-registry" numbered="true">
    <name>Engagement Endpoints</name>
    <t>A conformant AIP Registry supporting Engagement Objects 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>POST</td><td>/v1/engagements/{id}/countersign</td><td>Countersign proposed 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 authenticate the submitter as the
<tt>hiring_operator</tt> or as a caller authorized by Registry policy, verify
<tt>hiring_operator_signature</tt> and the initial change-log entry
signatures using the Engagement signature verification profile in
<xref target="engagement-objects"/>, validate that all referenced participant
AIDs exist and are not revoked, and assign
<tt>status: "proposed"</tt> with a first change log entry whose
<tt>seq</tt> is <tt>1</tt> and whose <tt>action</tt> is
<tt>engagement_created</tt>. The Registry MUST set <tt>version: 1</tt>
on the stored Engagement Object. A create request that requests
<tt>status: "active"</tt> without a valid
<tt>deploying_principal_signature</tt> MUST be rejected with
<tt>engagement_countersign_required</tt>.</t>

    <t>Countersign validation: <tt>POST /v1/engagements/{id}/countersign</tt>
applies only to Engagement Objects in <tt>proposed</tt> status. The request
body MUST contain <tt>deploying_principal_signature</tt> and a new change log
entry with <tt>action: "engagement_countersigned"</tt>. If
<tt>deploying_principal_signature</tt> is absent, the Registry MUST reject the
request with <tt>engagement_countersign_required</tt>. The Registry MUST verify
that the authenticated submitter is the <tt>deploying_principal</tt>, verify
the countersignature and the change log entry signature using the Engagement
signature verification profile in <xref target="engagement-objects"/>, and
verify the entry <tt>seq</tt> is
exactly <tt>current_max + 1</tt>. If the entry <tt>seq</tt> is not exactly
<tt>current_max + 1</tt>, the Registry MUST reject with
<tt>change_log_sequence_invalid</tt>. On success, the Registry MUST append the
change log entry, store <tt>deploying_principal_signature</tt>, set
<tt>status: "active"</tt>, and set <tt>version</tt> to the appended entry's
<tt>seq</tt> atomically.</t>

    <t>Update validation: The Registry MUST verify the submitter is an
active participant, the change log entry's <tt>seq</tt> is exactly
<tt>current_max + 1</tt>, and the entry signature is valid under the
Engagement signature verification profile in
<xref target="engagement-objects"/>. If the
<tt>seq</tt> value is missing, repeated, skipped, or otherwise not exactly
<tt>current_max + 1</tt>, the Registry MUST reject with
<tt>change_log_sequence_invalid</tt>. The Registry MUST reject modifications
to existing entries with <tt>change_log_immutable</tt>. On success, the
Registry MUST append the entry,
apply the mutation represented by that entry, and set <tt>version</tt> to
the appended entry's <tt>seq</tt> atomically. The Registry MUST reject any
PUT request that changes top-level engagement state without appending exactly
one accepted change-log entry.</t>
  </section>

  <section anchor="rpnp-endpoints-registry" numbered="true">
    <name>RPNP Subscription Endpoints</name>
    <t>A conformant AIP Registry supporting RPNP (<xref target="rpnp"/>) 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 request bodies MUST conform to
<tt>rpnp.schema.json</tt> <tt>subscription_request</tt>, and successful
responses MUST conform to <tt>subscription_response</tt>. Subscription
creation MUST be authenticated using the
<tt>subscriber_did</tt>-bound DPoP profiles defined in <xref target="rpnp-subscription"/>. The
<tt>webhook_uri</tt> MUST use HTTPS. The Registry MUST reject non-HTTPS
URIs with <tt>invalid_webhook_uri</tt>. The Registry MUST apply the RPNP
subscription authorization, scope-filter, and quota checks defined in Section
11.5.2 before creating the subscription. On success, the Registry MUST return
HTTP 201 with the assigned <tt>subscription_id</tt>.</t>
    <t><tt>GET /v1/subscriptions/{id}</tt> and
<tt>DELETE /v1/subscriptions/{id}</tt> MUST use the same
<tt>subscriber_did</tt>-bound authentication profile. The authenticated
<tt>subscriber_did</tt> MUST equal the subscription owner unless Registry
administrator policy explicitly authorizes the caller to inspect or cancel
subscriptions for other subscribers. Otherwise, the Registry MUST reject with
<tt>subscription_auth_required</tt>. <tt>GET</tt> MUST return the current
<tt>subscription_response</tt>. <tt>DELETE</tt> MUST transition the
subscription to <tt>cancelled</tt> and return the updated
<tt>subscription_response</tt>.</t>
  </section>

  <section anchor="registry-key-management" numbered="true">
    <name>Key Management and Rotation</name>
    <t>
      Registry trust evolution is defined in
      <xref target="registry-genesis-registry-key-rotation"/>. Planned rotation
      preserves the same <tt>registry_id</tt> and uses versioned Registry Trust
      Records with overlapping signatures, expiry, and rollback checks.
      Emergency compromise recovery uses explicit re-bootstrap and MUST NOT be
      accepted automatically.
    </t>
  </section>

  <section anchor="pagination-registry" numbered="true">
    <name>Pagination and Large Responses</name>
    <t>
      Registry endpoints that return collections and are not otherwise
      defined as signed snapshot artifacts MUST support cursor pagination.
      This requirement applies to collection-bearing endpoints such as
      <tt>GET /v1/scopes</tt>, <tt>GET /v1/namespaces</tt>, and collection
      members of <tt>GET /v1/agents/{aid}/reputation</tt>. It does not apply
      to single-resource endpoints or to signed CRL documents, which use the
      CRL segmentation rules above.
    </t>
    <t>
      Paginated endpoints MUST accept <tt>limit</tt> and <tt>cursor</tt>
      query parameters. The <tt>limit</tt> value MUST be an integer from 1 to
      1000 inclusive. If <tt>limit</tt> is absent, the default is 100. A
      Registry MAY apply a lower maximum for a specific endpoint, but the
      effective maximum MUST be at least 100. The <tt>cursor</tt> value is
      opaque to clients and MUST be used only with the same endpoint and query
      filter set that produced it. Invalid <tt>limit</tt> or
      <tt>cursor</tt> values MUST be rejected with
      <tt>invalid_request</tt>.
    </t>
    <t>
      A paginated response MUST include the endpoint-specific collection
      member, such as <tt>scopes</tt>, <tt>namespaces</tt>, or
      <tt>revocation_history</tt>, containing no more than the effective
      limit. It MUST also include a <tt>pagination</tt> object with
      <tt>limit</tt>, <tt>next_cursor</tt>, and <tt>has_more</tt>. The
      <tt>next_cursor</tt> value MUST be null when <tt>has_more</tt> is
      false. Servers MUST use a deterministic ordering for each collection and
      MUST bind cursors to a stable collection snapshot. If a cursor refers to
      a snapshot that is no longer available, the Registry MUST reject the
      request with <tt>invalid_request</tt>.
    </t>
  </section>
</section>
    <section anchor="error-handling" xml:base="sections/18-error-handling.xml">
  <name>Error Handling</name>

  <section anchor="error-response-format">
    <name>Error Response Format</name>
    <t>
      Unless a referenced OAuth binding normatively requires OAuth-native
      error syntax, every AIP protocol error response MUST use HTTP status
      code 4xx or 5xx, MUST use
      <tt>Content-Type: application/json</tt>, and MUST carry a body
      conforming to <tt>error-response.schema.json</tt> in the JSON Schema
      Index. Implementations MUST NOT return HTTP 200 for error conditions.
    </t>
    <t>
      The error response body MUST include <tt>error</tt>,
      <tt>error_description</tt>, and <tt>aip_version</tt>. It MAY include
      <tt>error_uri</tt>, <tt>correlation_id</tt>, and <tt>details</tt>.
      The <tt>error</tt> value MUST be the exact registered error-code
      string. The <tt>error_description</tt> value is human-readable
      diagnostics only; clients MUST NOT parse it for protocol decisions.
      Machine-readable metadata MUST be carried in <tt>details</tt> using
      one of the detail object profiles defined below and in the schema.
    </t>
    <t>
      Every AIP-aware error response MUST include an
      <tt>X-AIP-Version</tt> response header as defined in
      <xref target="token-structure"/>. The body <tt>aip_version</tt> value
      MUST equal the <tt>X-AIP-Version</tt> response header. For
      <tt>unsupported_version</tt>, the responder MUST set
      <tt>X-AIP-Version</tt> to a protocol version it supports for that
      endpoint and SHOULD include <tt>X-AIP-Supported-Versions</tt>. The
      <tt>details</tt> object for <tt>unsupported_version</tt> MUST include
      <tt>supported_versions</tt> and SHOULD include the rejected version
      values when they are available.
    </t>
    <t>
      For error responses conforming to this specification,
      <tt>X-AIP-Version</tt> and the body <tt>aip_version</tt> value MUST be
      <tt>"0.3"</tt>, except that a responder rejecting an unsupported version
      uses the supported version it selected for the error response.
    </t>
    <t>EXAMPLE (informative):</t>
    <artwork type="json">
{
  "error": "unsupported_version",
  "error_description": "Unsupported AIP version.",
  "aip_version": "0.3",
  "error_uri": "https://provai.dev/errors/unsupported_version",
  "correlation_id": "err_01HZX7M7N2M6M2T8K8F5Z3Q9Y1",
  "details": {
    "type": "unsupported_version",
    "requested_version": "0.2",
    "header_aip_version": "0.2",
    "token_aip_version": null,
    "supported_versions": ["0.3"]
  }
}
    </artwork>
  </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>invalid_request</tt></dt><dd><t>HTTP 400. Request syntax, query parameter, pagination cursor, or endpoint-specific request parameter is invalid.</t></dd>
      <dt><tt>unsupported_version</tt></dt><dd><t>HTTP 400. <tt>X-AIP-Version</tt> or token <tt>aip_version</tt> is missing, unsupported, or mutually inconsistent.</t></dd>
      <dt><tt>dpop_proof_required</tt></dt><dd><t>HTTP 401. DPoP proof is required for the request but the <tt>DPoP</tt> header is absent. Malformed or invalid DPoP proofs return <tt>invalid_token</tt>.</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 unavailable, signature failed, or AIP Scope Catalog constraint validation failed.</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, signature failed, dependency invalid, or value/hash mismatch.</t></dd>
      <dt><tt>approval_envelope_expired</tt></dt><dd><t>HTTP 403. The Approval Envelope was not approved before <tt>approval_window_expires_at</tt>.</t></dd>
      <dt><tt>approval_not_found</tt></dt><dd><t>HTTP 404. Approval Envelope ID not found.</t></dd>
      <dt><tt>approval_state_conflict</tt></dt><dd><t>HTTP 409. Approval Envelope state changed before the requested approval or rejection transition could be committed.</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_not_claimed</tt></dt><dd><t>HTTP 409. Step Execution Token presented before the Registry has accepted a claim for that step.</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>compensation_failed</tt></dt><dd><t>HTTP 409. An Approval Envelope compensation step failed and the envelope is in terminal failed state requiring out-of-band remediation.</t></dd>
      <dt><tt>engagement_cancelled</tt></dt><dd><t>HTTP 409. The referenced Approval Envelope or step was cancelled because its parent Engagement Object was terminated. The operation cannot proceed.</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, expired, missing from a G3 authorization request, signature failed, inconsistent with OAuth request binding, or cannot be resolved to trusted catalog scope display metadata for consent.</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>grant_tier_insufficient</tt></dt><dd><t>HTTP 403. Registered <tt>grant_tier</tt> is absent, unrecognised, or below the Grant Tier required for the operation's security Tier.</t></dd>
      <dt><tt>registration_invalid</tt></dt><dd><t>HTTP 400. Registration Envelope malformed or failed validation, including missing or invalid <tt>grant_tier</tt>.</t></dd>
      <dt><tt>aid_already_registered</tt></dt><dd><t>HTTP 409. The submitted AID or public key is already associated with a registered, non-revoked AID.</t></dd>
      <dt><tt>unknown_aid</tt></dt><dd><t>HTTP 404. AID not registered in any accessible Registry.</t></dd>
      <dt><tt>identity_version_conflict</tt></dt><dd><t>HTTP 409. Agent Identity key rotation version is stale, skips a version, or conflicts with a committed rotation.</t></dd>
      <dt><tt>revocation_invalid</tt></dt><dd><t>HTTP 400. Revocation Object is malformed, uses a reserved externally submitted reason, has an invalid timestamp, or has an invalid or unverifiable signature.</t></dd>
      <dt><tt>revocation_unauthorized</tt></dt><dd><t>HTTP 403. Revocation issuer is not authorized for the target or revocation type.</t></dd>
      <dt><tt>revocation_conflict</tt></dt><dd><t>HTTP 409. Revocation identifier was previously accepted with different object content.</t></dd>
      <dt><tt>registry_unavailable</tt></dt><dd><t>HTTP 503. Registry or a required DID resolver could not be reached or timed out.</t></dd>
      <dt><tt>rate_limit_exceeded</tt></dt><dd><t>HTTP 429. Rate limit for this operation exceeded; see Section 19.</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. Requested scope is unknown, reserved, removed, retired (including bare <tt>spawn_agents</tt>), conflicting with the synced AIP Scope Catalog, or outside an explicitly documented local/private extension policy.</t></dd>
      <dt><tt>principal_did_method_forbidden</tt></dt><dd><t>HTTP 403. Principal DID method is not permitted for the operation Tier; Tier 2 and Tier 3 require <tt>did:web</tt>.</t></dd>
      <dt><tt>identity_proofing_insufficient</tt></dt><dd><t>HTTP 403. G3 identity proofing is absent or does not satisfy requested <tt>acr_values</tt>.</t></dd>
      <dt><tt>enterprise_policy_denied</tt></dt><dd><t>HTTP 403. The enterprise IdP denied the token exchange request due to Conditional Access, ABAC, or equivalent enterprise policy. The agent's principal lacks sufficient enterprise-level authorization for the requested resource. Context: <xref target="enterprise-idp-error-handling"/>.</t></dd>
      <dt><tt>idp_client_misconfigured</tt></dt><dd><t>HTTP 500. The AIP Registry's enterprise IdP client credentials are invalid or expired. This is a Registry configuration error, not an agent error. Context: <xref target="enterprise-idp-error-handling"/>.</t></dd>
      <dt><tt>invalid_grant</tt></dt><dd><t>HTTP 400. The enterprise IdP rejected the secondary token exchange grant. Context: <xref target="enterprise-idp-error-handling"/>.</t></dd>
      <dt><tt>enterprise_assertion_missing</tt></dt><dd><t>HTTP 403. Tier 3 validation required an <tt>aip_principal_assertion</tt> claim, but the Credential Token did not contain one.</t></dd>
      <dt><tt>enterprise_assertion_invalid</tt></dt><dd><t>HTTP 403. The <tt>aip_principal_assertion</tt> JWT was malformed, could not be verified against the enterprise IdP JWKS, or used an issuer that is not accepted for the deployment.</t></dd>
      <dt><tt>enterprise_assertion_principal_mismatch</tt></dt><dd><t>HTTP 403. The verified <tt>aip_principal_assertion</tt> subject did not match the root Principal Token's <tt>principal.id</tt>.</t></dd>
      <dt><tt>a2a_originator_invalid</tt></dt><dd><t>HTTP 400. The <tt>aip_originator_aid</tt> claim is absent in an A2A delegated context, does not conform to the <tt>did:aip</tt> ABNF, equals the acting agent's own AID, or cannot be traced to the validated delegation chain. Context: <xref target="delegated-identity-chaining-a2a"/>.</t></dd>
      <dt><tt>endorsement_invalid</tt></dt><dd><t>HTTP 400. Endorsement Object validation failed, including invalid signature or out-of-range score. Context: <xref target="reputation-scoring"/>.</t></dd>
      <dt><tt>gateway_config_invalid</tt></dt><dd><t>HTTP 400. A gateway OAuth Protected Resource Metadata document is internally inconsistent or violates AIP gateway metadata constraints. Context: <xref target="aip-gateway-discovery-document"/>.</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 or not using <tt>code_challenge_method</tt> <tt>S256</tt>.</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 or AIP Scope Catalog constraint validation.</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 Engagement Object countersignature.</t></dd>
      <dt><tt>engagement_signature_invalid</tt></dt><dd><t>HTTP 400. Engagement Object top-level signature or change-log entry signature is missing, malformed, bound to the wrong DID, uses an unsupported key, or fails verification.</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 request missing DPoP, using an invalid DPoP proof, or failing to bind <tt>subscriber_did</tt> to the DPoP key or Authorization credential.</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>
      The <tt>details</tt> member is optional unless this section states that
      it is REQUIRED for a specific error. When present, it MUST conform to
      one of the detail object profiles in
      <tt>error-response.schema.json</tt>. A detail object MUST NOT weaken,
      contradict, or replace the registered meaning of the top-level
      <tt>error</tt> value.
    </t>

    <dl newline="false">
      <dt><tt>unsupported_version</tt></dt>
      <dd><t>The response MUST include an
        <tt>unsupported_version</tt> detail object containing
        <tt>supported_versions</tt>. It SHOULD include
        <tt>requested_version</tt>, <tt>header_aip_version</tt>, and
        <tt>token_aip_version</tt> when those values are available.</t></dd>

      <dt><tt>rate_limit_exceeded</tt></dt>
      <dd><t>The response MUST include <tt>Retry-After</tt> per
        <xref target="RFC6585"/> Section 4, the rate limit headers defined
        in Section 19.1, and a <tt>rate_limit</tt> detail object containing
        <tt>operation</tt>, <tt>limit</tt>, and <tt>reset_at</tt>.</t></dd>

      <dt><tt>registry_unavailable</tt></dt>
      <dd><t>The response SHOULD include <tt>Retry-After</tt> per
        <xref target="RFC9110"/> and SHOULD include a
        <tt>registry_dependency</tt> detail object identifying the unavailable
        dependency and whether the condition is retryable.</t></dd>

      <dt><tt>chain_token_expired</tt></dt>
      <dd><t>The response SHOULD include a
        <tt>delegation_depth</tt> detail object identifying the expired chain
        element's <tt>delegation_depth</tt>, and MAY include
        <tt>chain_index</tt>, <tt>principal_token_jti</tt>, and
        <tt>expires_at</tt>.</t></dd>

      <dt><tt>invalid_delegation_depth</tt></dt>
      <dd><t>The response SHOULD include a
        <tt>delegation_depth</tt> detail object containing the submitted
        <tt>delegation_depth</tt> and, when applicable,
        <tt>max_delegation_depth</tt>.</t></dd>

      <dt><tt>invalid_scope</tt> and <tt>insufficient_scope</tt></dt>
      <dd><t>The response SHOULD include a <tt>scope</tt> detail object
        listing the scope or scopes that caused rejection and, when catalog
        metadata influenced the decision, the <tt>catalog_snapshot_id</tt>
        and <tt>catalog_sha256</tt> used.</t></dd>

      <dt><tt>manifest_invalid</tt> and <tt>overlay_exceeds_manifest</tt></dt>
      <dd><t>When rejection is caused by scope-catalog constraint validation,
        the response SHOULD include a <tt>scope</tt> detail object listing
        the failing scopes and catalog snapshot metadata.</t></dd>

      <dt>Conflict errors</dt>
      <dd><t>For <tt>identity_version_conflict</tt>,
        <tt>approval_state_conflict</tt>, <tt>revocation_conflict</tt>,
        <tt>overlay_version_conflict</tt>, and
        <tt>change_log_sequence_invalid</tt>, the response SHOULD include a
        <tt>conflict</tt> detail object identifying the resource and the
        current, submitted, or expected version, sequence, or state when
        available.</t></dd>

      <dt>Approval step errors</dt>
      <dd><t>For <tt>approval_step_prerequisites_unmet</tt>,
        <tt>approval_step_already_claimed</tt>,
        <tt>approval_step_not_claimed</tt>,
        <tt>approval_step_action_mismatch</tt>, and
        <tt>approval_step_invalid</tt>, the response SHOULD include an
        <tt>approval_step</tt> detail object containing
        <tt>approval_id</tt> and <tt>step_id</tt>.</t></dd>

      <dt>Grant request errors</dt>
      <dd><t>For <tt>grant_request_expired</tt>,
        <tt>grant_request_replayed</tt>, <tt>grant_request_invalid</tt>, and
        <tt>grant_nonce_mismatch</tt>, the response SHOULD include a
        <tt>grant</tt> detail object containing
        <tt>grant_request_id</tt> when the request identifier is known.</t></dd>

      <dt>Field validation errors</dt>
      <dd><t>For <tt>invalid_request</tt>, <tt>registration_invalid</tt>,
        <tt>revocation_invalid</tt>, <tt>approval_envelope_invalid</tt>,
        <tt>overlay_signature_invalid</tt>,
        <tt>engagement_countersign_required</tt>,
        <tt>engagement_signature_invalid</tt>,
        <tt>change_log_immutable</tt>, <tt>pkce_required</tt>,
        <tt>invalid_webhook_uri</tt>, and <tt>invalid_target</tt>, the
        response SHOULD include a <tt>field</tt> detail object identifying
        the rejected field or parameter when a single field caused the
        rejection.</t></dd>

      <dt>Subscription errors</dt>
      <dd><t>For <tt>subscription_auth_required</tt>,
        <tt>subscription_scope_forbidden</tt>, and
        <tt>subscription_limit_exceeded</tt>, the response SHOULD include a
        <tt>subscription</tt> detail object when a subscription identifier,
        subscriber DID, or limit value is known.</t></dd>
    </dl>
  </section>
</section>
    <section anchor="rate-limiting" xml:base="sections/19-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><tt>Retry-After</tt></td><td>MUST</td><td>Seconds until the client may retry, or a HTTP-date per [RFC9110]</td></tr>
        <tr><td><tt>X-RateLimit-Limit</tt></td><td>SHOULD</td><td>The request limit for this window</td></tr>
        <tr><td><tt>X-RateLimit-Remaining</tt></td><td>SHOULD</td><td>Remaining requests in this window</td></tr>
        <tr><td><tt>X-RateLimit-Reset</tt></td><td>SHOULD</td><td>Unix timestamp when the window resets</td></tr>
        <tr><td><tt>X-RateLimit-Policy</tt></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 18.1)
      with <tt>error: "rate_limit_exceeded"</tt> and a human-readable
      <tt>error_description</tt> 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 numeric limits below are
      RECOMMENDED default ceilings for unauthenticated or baseline clients.
      Registry operators MAY enforce lower ceilings when observed traffic
      patterns or threat models require stricter throttling, and MAY grant
      higher ceilings to authenticated or verified clients where this section
      allows higher limits.
      Rate-limit conformance is based on implementing the separate buckets,
      enforcing a documented effective limit for each bucket, and returning the
      response format in Section 19.1. The exact numeric thresholds are
      operational policy unless a requirement below uses MUST.
    </t>

    <section anchor="rate-limit-category-r1">
      <name>Category R1 - Registration writes</name>
      <t>
        (<tt>POST /v1/agents</tt>):
      </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>
        (<tt>PUT /v1/agents/{aid}</tt>):
      </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>
        (<tt>POST /v1/revocations</tt>):
      </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
          <tt>propagate_to_children: true</tt> 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>
        (<tt>GET /v1/agents/{aid}/public-key/{key-id}</tt>,
        <tt>GET /v1/agents/{aid}/revocation</tt>):
      </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>
        (<tt>GET /v1/crl</tt> direct-origin reads and the published
        <tt>endpoints.crl</tt> retrieval URI):
      </t>
      <ul>
        <li>
          The published CRL retrieval URI MUST be served from a CDN or
          distributed infrastructure (<xref target="crl"/>). Direct-origin CRL reads
          to <tt>/v1/crl</tt> 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>
        (<tt>POST /v1/endorsements</tt>):
      </t>
      <ul>
        <li>
          Per-from-AID: RECOMMENDED limit of 500 endorsements per hour. This
          prevents an AID from artificially inflating another AID's
          <tt>endorsement_count</tt> through automated submission.
        </li>
        <li>
          Self-endorsement (<tt>from_aid</tt> == <tt>to_aid</tt>) 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>
        (<tt>POST /v1/approvals</tt>, <tt>POST /v1/approvals/{id}/steps/{n}/claim</tt>):
      </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 6.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 <tt>principal_token</tt> in the Registration
        Envelope was issued by a DID that is resolvable and has not been
        subjected to a <tt>full_revoke</tt> or <tt>principal_revoke</tt> RevocationObject
        in the Registry. A revoked principal MUST NOT be permitted to
        register new agents.
      </t>
      <t>
        For the purposes of this check, a <strong>revoked principal</strong> is
        defined as a principal DID (<tt>principal_token.principal.id</tt>)
        against which a <tt>principal_revoke</tt> Revocation Object has been
        submitted to this Registry. The Registry MUST maintain an index of
        principal DIDs associated with <tt>principal_revoke</tt> revocations and
        MUST reject new Registration Envelopes where
        <tt>principal_token.principal.id</tt> matches a DID in this index. A
        <tt>principal_revoke</tt> whose <tt>target_id</tt> is a Principal DID is
        principal-wide. A <tt>principal_revoke</tt> whose <tt>target_id</tt> is a
        specific agent AID is scoped to that AID's authorisation and does not by
        itself block registration of unrelated AIDs for the same principal.
      </t>
    </section>

    <section anchor="registration-flood-from-shared-principals">
      <name>Registration flood from shared principals</name>
      <t>
        Operational guidance (non-normative): a high number of registrations
        under a single principal DID can be an abuse signal. A Registry can
        apply local policy, such as deployer review or out-of-band business
        verification, when registrations from one principal exceed an
        implementation-defined threshold. A value such as 1,000 agents can be
        used as an example review trigger, but this specification does not
        define a protocol-visible proof of legitimate use, an interoperable
        review mechanism, or a conformance threshold for shared-principal
        registration volume.
      </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 the deployer DID is
        self-asserted, unauthenticated to the Registry, or otherwise not
        trusted by local Registry policy.
      </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 <tt>kid</tt> 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 <tt>kid</tt>
        for up to 300 seconds. Repeated validation of tokens
        with the same <tt>kid</tt> 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 the
        historical key retention window defined in Section 19.7. This prevents
        adversaries from constructing tokens with ancient, never-rotated keys
        to force deep history lookups without invalidating any conforming
        unexpired token.
      </t>
      <t>
        Relying Parties perform the Step 2a expiration preflight before
        historical key lookup. Therefore an expired token MUST be rejected with
        <tt>token_expired</tt> before a missing historical key can produce
        <tt>unknown_aid</tt>.
      </t>
    </section>

    <section anchor="kid-validation-at-relying-party">
      <name><tt>kid</tt> validation at the Relying Party</name>
      <t>
        Relying Parties MUST validate that the <tt>kid</tt> in the token header
        matches the pattern:
        <tt>did:aip:&lt;lowercase-namespace&gt;:&lt;32-lowercase-hex&gt;#key-&lt;positive-integer&gt;</tt>
        before performing any Registry lookup (Validation Step 3).
        Malformed <tt>kid</tt> values MUST be rejected with <tt>invalid_token</tt>
        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 19.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 <tt>pending_approval</tt>
        envelopes per principal DID at any time. Envelopes that expire
        transition to <tt>expired</tt> 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 the Registry's
        published <tt>step_claim_timeout_seconds</tt> value MUST be
        automatically failed by the Registry. The timeout interval starts at
        the step's <tt>claimed_at</tt> timestamp. The
        <tt>step_claim_timeout_seconds</tt> value MUST be an integer from 1
        to 600 seconds inclusive and MUST be published in
        <tt>/v1/registry-metadata</tt>.
      </t>
      <t>
        This prevents a claimed step from blocking the workflow indefinitely
        due to a crashed or unresponsive agent. Timeout-induced failure MUST
        trigger the same envelope failure and compensation evaluation as an
        explicit step failure reported by the actor.
      </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
      <tt>Retry-After</tt> header. Clients MUST NOT retry before <tt>Retry-After</tt>
      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 <tt>Retry-After</tt> value from the 429 response acts as a mandatory
      minimum: clients MUST NOT retry before <tt>Retry-After</tt> seconds have
      elapsed, even if <tt>computed_delay + jitter</tt> is smaller. When no
      <tt>Retry-After</tt> 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 anchor="historical-key-retention-requirements">
    <name>Historical Key Retention Requirements</name>
    <t>
      A Registry that accepts key rotation for an AID MUST retain each retired
      public verification key for historical validation until at least:
    </t>
    <artwork type="pseudocode">
retention_not_before =
  retired_key.valid_until
  + max_credential_token_ttl_for_registry
  + accepted_clock_skew
    </artwork>
    <t>
      <tt>max_credential_token_ttl_for_registry</tt> is the largest effective
      Credential Token TTL that the Registry could have accepted for any active
      scope at the time the key was retired, after applying the Tier ceilings in
      <xref target="token-issuance"/> and the synced AIP Scope Catalog. The
      <tt>accepted_clock_skew</tt> value MUST be at least 30 seconds.
      Registries MAY retain retired keys longer than this minimum. The
      RECOMMENDED operational retention window is 90 days after
      <tt>valid_until</tt>, but that window MUST be extended when the formula
      above yields a later time.
    </t>
    <t>
      Historical key retention is a validation-availability rule, not an
      authority extension. Agents MUST stop issuing Credential Tokens with a
      retired private key immediately after a successful rotation, and Relying
      Parties MUST still reject tokens whose <tt>exp</tt>, delegation chain, or
      revocation state fails validation.
    </t>
  </section>
</section>
    <section anchor="versioning" xml:base="sections/20-versioning.xml">
  <name>Versioning and Compatibility</name>
  <t>
    AIP uses separate identifiers for Internet-Draft document revisions and
    wire protocol compatibility. The suffix in an Internet-Draft name, such as
    <tt>-00</tt>, <tt>-01</tt>, or <tt>-02</tt>, is a document revision
    identifier only. It is not an AIP protocol version and MUST NOT appear in
    <tt>aip_version</tt>, <tt>X-AIP-Version</tt>, Credential Tokens,
    GrantRequests, Registry metadata, or other protocol messages.
  </t>
  <t>
    The <tt>aip_version</tt> claim and the <tt>X-AIP-Version</tt> HTTP header
    carry the AIP protocol compatibility version. This Internet-Draft revision
    defines AIP protocol version <tt>0.3</tt>. Multiple Internet-Draft
    revisions MAY define the same protocol version when they contain editorial
    changes, clarifications, examples, schema corrections, or other changes
    that do not intentionally create a new wire compatibility class.
  </t>
  <t>
    AIP protocol versions follow Semantic Versioning compatibility intent,
    represented on the wire as <tt>MAJOR.MINOR</tt>. Before v1.0, MINOR
    versions MAY include breaking changes. A future Internet-Draft revision
    that intentionally introduces breaking wire behavior, incompatible
    validation semantics, or incompatible message schemas MUST allocate a new
    protocol version before publication, for example by moving from
    <tt>0.3</tt> to <tt>0.4</tt>. Backward-compatible clarifications and
    non-breaking errata MAY retain the existing <tt>aip_version</tt>.
  </t>
  <t>
    AIP Catalog snapshot identifiers, such as <tt>draft-02</tt>, are also
    separate from <tt>aip_version</tt>. A Registry MAY report both its AIP
    protocol version and its synced Catalog snapshot, but verifiers MUST NOT
    treat a Catalog snapshot identifier or Internet-Draft revision as a
    substitute for protocol version negotiation.
  </t>
  <table>
    <name>Draft Revision to Protocol Version Mapping</name>
    <thead>
      <tr>
        <th align="left">Internet-Draft revision</th>
        <th align="left">AIP protocol version</th>
        <th align="left">Catalog snapshot</th>
      </tr>
    </thead>
    <tbody>
      <tr><td><tt>draft-singla-agent-identity-protocol-00</tt></td><td><tt>0.1</tt></td><td><tt>draft-00</tt></td></tr>
      <tr><td><tt>draft-singla-agent-identity-protocol-01</tt></td><td><tt>0.2</tt></td><td><tt>draft-01</tt></td></tr>
      <tr><td><tt>draft-singla-agent-identity-protocol-02</tt></td><td><tt>0.3</tt></td><td><tt>draft-02</tt></td></tr>
    </tbody>
  </table>
  <t>
    Catalog snapshots are immutable for conformance purposes. A Registry
    claiming Draft-02 conformance MUST advertise
    <tt>aip_catalog_version: "draft-02"</tt>,
    <tt>aip_catalog_snapshot_id: "https://provai.dev/aip/catalog/draft-02"</tt>, and a
    pinned <tt>aip_catalog_sha256</tt> digest as defined in
    <xref target="scope-map-registry"/>. A change to any standard Catalog
    entry or standard Catalog security metadata requires a new Catalog
    Snapshot identifier and MUST NOT be silently introduced under the existing
    Draft-02 snapshot.
  </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> (<xref target="scope-map-registry"/>).</li>
    <li>Registration Envelopes MUST include <tt>grant_tier</tt>.</li>
    <li>Principal DID Documents for Tier 2 agents MUST include an <tt>AIPRegistry</tt> service entry.</li>
    <li>The <tt>/v1/registry-metadata</tt> response MUST include <tt>registry_id</tt>, <tt>registry_trust_uri</tt>, <tt>registry_name</tt>, and <tt>endpoints</tt>. Registry Trust Records are versioned separately and MUST support sequential updates.</li>
    <li>Section 19.6 (Graduated Backoff Requirements): the backoff formula
    has been corrected to use exponential backoff with a base interval of 1
    second, a multiplier of 2, and a maximum interval of 3600 seconds, replacing
    the previous linear backoff formula. This change ensures that Registry
    lookup storms are dampened under sustained failure conditions.</li>
  </ul>
  <t>
    Implementations MUST NOT silently accept tokens from unsupported
    versions without logging a version warning.
  </t>
  <t>
    A Relying Party or Registry conforming only to protocol version
    <tt>0.3</tt> MUST reject any request, Credential Token, Step Execution
    Token, GrantRequest, or other AIP protocol message whose
    <tt>aip_version</tt> or <tt>X-AIP-Version</tt> value is absent, not
    <tt>"0.3"</tt>, or mutually inconsistent, unless the implementation has
    explicitly implemented that other protocol version. The error response MUST
    be <tt>unsupported_version</tt> and SHOULD include
    <tt>X-AIP-Supported-Versions</tt> and the unsupported value in the
    <tt>details</tt> object. Implementations MUST NOT treat an unknown future
    version, including a numerically greater value such as <tt>0.4</tt>, as
    forward-compatible by default.
  </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, G2, or G3</td><td>Any non-<tt>did:aip</tt> W3C DID method; see requirements below</td></tr>
        <tr><td>2</td><td>Real-time</td><td>REQUIRED</td><td>NOT REQUIRED</td><td>G2 or G3; see requirements below</td><td><tt>did:web</tt>; see requirements below</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>; see requirements below</td></tr>
      </tbody>
    </table>
    <dl newline="false">
      <dt>Tier 1 Principal DID method</dt>
      <dd><t>For Tier 1, the principal MAY use any W3C DID method except <tt>did:aip</tt>. Principals using <tt>did:key</tt> are permitted for Tier 1 only.</t></dd>
      <dt>Principal DID method restrictions</dt>
      <dd><t><tt>did:aip</tt> is NEVER a valid Principal DID method for any Tier. The <tt>principal.id</tt> field MUST NOT use the <tt>did:aip</tt> method. Principals using any DID method other than <tt>did:web</tt> for Tier 2 or Tier 3 agents MUST be rejected with <tt>principal_did_method_forbidden</tt>. For Tier 2 and Tier 3, the <tt>did:web</tt> DID Document MUST contain the <tt>AIPRegistry</tt> service entry required by Section 7.3.</t></dd>
      <dt>Tier 2 Registry identity-proofing policy</dt>
      <dd><t>If <tt>identity_proofing_required_for_tier2</tt> is <tt>true</tt> in Registry metadata, Tier 2 permits only <tt>G3</tt> for that Registry.</t></dd>
    </dl>
    <t>
      The table defines minimum Grant Tier conformance. A higher-assurance
      Grant Tier MAY satisfy a lower security Tier, but a lower-assurance
      Grant Tier MUST NOT satisfy a higher security Tier. Relying Parties
      enforce this relationship for each participating <tt>aip_chain</tt> AID
      at runtime in Section 9, Step 9d.
    </t>
  </section>
</section>
    <section anchor="security" numbered="true" xml:base="sections/21-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>

      <dt>TS-10: Step Execution Token Misuse</dt>
      <dd>An adversary forges, replays, or re-targets a Step Execution Token
        to execute an Approval Envelope step outside the authorized actor,
        audience, or step context. Mitigation: SET validation profile,
        Registry Trust Record step-execution keys, Step 10a verification,
        DPoP binding, and per-step status checks.</dd>

      <dt>TS-11: Approval Lifecycle Race</dt>
      <dd>Concurrent approval, rejection, expiry, cancellation, or claim
        operations produce inconsistent Approval Envelope state. Mitigation:
        atomic lifecycle transitions, compare-and-set or equivalent locking,
        terminal-state rules, and <tt>approval_state_conflict</tt>.</dd>

      <dt>TS-12: Orchestrator Cascade</dt>
      <dd>A compromised or over-privileged orchestrator uses one approval or
        delegation to trigger a larger cascade of dependent actions than the
        principal intended. Mitigation: pre-declared Approval Envelope steps,
        per-step actor and action-hash verification, step count limits,
        dedicated destructive-scope approval, and SAGA compensation rules.</dd>

      <dt>TS-13: Overlay Injection</dt>
      <dd>An adversary injects or replays a Capability Overlay to expand or
        mis-scope an agent's effective capabilities. Mitigation: Overlay
        signatures, issuer validation, monotonic overlay versions, CO-1
        attenuation-only validation, engagement scoping, and catalog
        constraint validation.</dd>

      <dt>TS-14: Engagement Tampering</dt>
      <dd>An adversary modifies Engagement Object participants, lifecycle
        state, approval gates, or change-log entries to alter authorization
        context. Mitigation: required countersignatures, append-only signed
        change logs, strict sequence validation, participant authorization
        checks, and termination cascade rules.</dd>

      <dt>TS-15: Model Substitution</dt>
      <dd>An agent deployer registers with a trusted model identifier but
        executes a different model binary or version manifest. Mitigation:
        <tt>model.attestation_hash</tt> SHOULD be present for Tier 2
        deployments and MUST be present for Tier 3 deployments. The Registry
        SHOULD accept a Tier 2 registration without
        <tt>model.attestation_hash</tt> only when it persists and returns the
        structured <tt>model_attestation_missing_tier2</tt> registration
        warning defined in Section 6.2. The Registry MUST reject a Tier 3
        registration when <tt>model.attestation_hash</tt> is absent.</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>
          </tbody>
        </table>

    <t>
      This specification does not define an AIP key-exchange or encryption
      handshake. X25519 and other key-agreement mechanisms are reserved for a
      future extension and are not mandatory-to-implement in Draft-02.
      Implementations MUST NOT treat support for any key-agreement algorithm
      as a condition for conformance to this specification.
    </t>

    <t>
      This specification reserves ES256 (ECDSA P-256) and RS256 (RSA-PKCS1),
      as defined by <xref target="RFC7518"/>, for possible future extensions
      but does not define registration, resolution, rotation, AID derivation,
      or DPoP binding rules for those suites in protocol version
      <tt>0.3</tt>. Credential Tokens, Principal Tokens, Step Execution
      Tokens, DPoP proofs, Agent Identity keys, and Registry public-key
      responses conforming to this specification MUST use EdDSA over Ed25519.
      Implementations MUST reject <tt>ES256</tt>, <tt>RS256</tt>, and other
      algorithm values for those artifacts.
    </t>

    <t>
      Prohibited: none, HS256/384/512, RS512, MD5, SHA-1.
      The alg header MUST be explicitly specified. A DID verification
      method used to sign or verify a Credential Token, Principal Token, Step
      Execution Token, or DPoP proof MUST be an Ed25519 signing verification
      method. X25519 keys are key-agreement keys and MUST NOT be used for
      signing.
    </t>
  </section>

  <section anchor="dpop" numbered="true">
  <name>Proof-of-Possession (DPoP)</name>
    <t>
      DPoP is REQUIRED for every Tier 2 or Tier 3 operation. It is also
      REQUIRED when any requested Tier 1 scope has
      <tt>requires_dpop: true</tt> in the synced AIP Scope Catalog, or when
      the target endpoint requires DPoP independently of scope metadata. DPoP proofs MUST use
      EdDSA.
    </t>
    <table>
      <name>DPoP Applicability</name>
      <thead>
        <tr>
          <th align="left">Operation or request class</th>
          <th align="left">DPoP requirement</th>
        </tr>
      </thead>
      <tbody>
        <tr><td>Tier 1 operation with no DPoP-required scope and no endpoint policy</td><td>Not required by AIP.</td></tr>
        <tr><td>Tier 1 operation containing any scope whose synced Catalog entry has <tt>requires_dpop: true</tt></td><td>Required.</td></tr>
        <tr><td>Tier 1 Registry endpoint that independently requires DPoP, including key rotation, heartbeat submission, and RPNP subscription authentication</td><td>Required.</td></tr>
        <tr><td>Tier 2 operation</td><td>Required for every request.</td></tr>
        <tr><td>Tier 3 operation</td><td>Required for every request, in addition to mTLS.</td></tr>
      </tbody>
    </table>
    <t>
      Endpoint-level DPoP requirements are independent of the operation's
      security Tier. For example, <tt>PUT /v1/agents/{aid}</tt> key rotation
      and <tt>POST /v1/agents/{aid}/heartbeat</tt> require DPoP even when the
      affected agent was originally registered only for Tier 1 scopes.
    </t>
    <t>
      For standard AIP scopes, the Draft-02 catalog marks all destructive
      scopes and selected high-risk external-effect scopes as DPoP-required.
      This includes deletion scopes, filesystem mutation and execution scopes,
      transactions, communication scopes, spawn-agent scopes, and the
      non-destructive but high-risk <tt>email.send</tt>,
      <tt>web.download</tt>, and <tt>web.forms_submit</tt> scopes.
      The <tt>web.forms_submit</tt> scope is Tier 2 in the Draft-02 catalog
      because form submissions can create external account, data, or financial
      effects.
    </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, no query or fragment&gt;",
  "iat": "&lt;Unix timestamp&gt;",
  "ath": "&lt;BASE64URL(SHA-256(token))&gt;"
}
</artwork>

    <t>
      Relying Party validation: Verify alg is EdDSA, verify htm/htu/iat,
      check jti replay, verify ath, verify signature, and verify the DPoP
      public key binding for the presented token type. For an agent-issued
      Credential Token, <tt>jwk.kid</tt> MUST match the Credential Token
      protected header <tt>kid</tt>. For a Step Execution Token,
      <tt>jwk.kid</tt> MUST identify Ed25519 public key material controlled
      by the SET <tt>sub</tt> actor AID; it MUST NOT be the Registry
      step-execution key that signed the SET.
    </t>
    <t>
      The <tt>htu</tt> claim is the DPoP target URI binding. It MUST match
      the absolute target URI of the current request, excluding query and
      fragment components and using the normalization rules in
      <xref target="RFC9449"/>. A proof captured for one Relying Party,
      Registry, host, path, or method is not transferable to another target:
      validation MUST fail on <tt>htu</tt> or <tt>htm</tt> before replay-cache
      state is considered.
    </t>
    <t>
      The <tt>ath</tt> claim binds the proof to the presented token. Its
      value MUST be the unpadded base64url encoding of the SHA-256 hash of
      the exact compact Credential Token or Step Execution Token value from
      the <tt>Authorization</tt> header, excluding the authorization scheme,
      surrounding whitespace, and DPoP proof. When DPoP is required, the
      authorization scheme is <tt>DPoP</tt>.
    </t>
    <t>
      DPoP proof <tt>jti</tt> replay state is verifier-local. A verifier MUST
      maintain a DPoP replay cache separate from the Credential Token replay
      cache and keyed by <tt>(kid, jti)</tt>. The cache window MUST be at
      least the accepted DPoP proof age window: 300 seconds before receipt
      plus 30 seconds of future clock skew, unless a server-provided DPoP
      nonce is required under <xref target="RFC9449"/>. When nonce validation
      is used, the replay cache MUST retain entries for at least the nonce
      lifetime. AIP does not require a global or cross-Registry DPoP replay
      cache because cross-target replay is rejected by <tt>htu</tt>,
      <tt>htm</tt>, and <tt>ath</tt> validation.
    </t>
  </section>

  <section anchor="key-management" numbered="true">
  <name>Key Management</name>
    <t>
      Private keys MUST NOT be stored in plaintext at rest,
      transmitted in protocol messages, or included in logs.
      Private keys SHOULD be stored in an HSM, secure enclave,
      OS-level keychain, or an equivalent protected key store where
      available.
    </t>

    <t>
      A private key satisfies the at-rest protection requirement when at least
      one of the following conditions is met:
    </t>

    <ol>
      <li>The key is generated and used inside an HSM, secure enclave,
      operating-system keychain, cloud KMS, or comparable protected key store
      that prevents ordinary application processes from reading the raw private
      key material.</li>
      <li>The key is stored only as ciphertext produced by envelope encryption
      using AES-256-GCM, or an equivalent authenticated-encryption scheme with
      at least 128-bit security strength, unique nonce or IV use per
      encryption key, integrity authentication of the ciphertext, and
      associated data that binds the ciphertext to the AID, key identifier,
      key version, and intended key purpose.</li>
    </ol>

    <t>
      A protected key store or encryption design is equivalent for this
      specification only if it enforces access control for key use and key
      administration, separates key-encryption keys from encrypted private-key
      blobs, supports key-encryption-key rotation without exposing plaintext
      private keys, records administrative key export or deletion events, and
      does not depend on hard-coded wrapping keys or wrapping keys stored in
      the same plaintext database row, file, or object as the encrypted
      private key. Backup copies of private keys MUST satisfy the same
      controls as primary copies.
    </t>

    <t>
      The protected-key-store recommendation is a local implementation
      conformance requirement, not a wire-verifiable protocol condition.
      Registries and Relying Parties cannot determine from Credential Tokens
      or Registry API requests whether an implementation uses an HSM, secure
      enclave, keychain, or equivalent mechanism. They MUST NOT treat
      protected-key-store use as a Registry acceptance check or Credential
      Token validation precondition. Deployment profiles MAY require external
      audit, attestation, or certification of key-storage controls using the
      audit evidence profile below.
    </t>

    <t>
      <strong>Deployment key-management audit evidence:</strong> A deployment
      profile that requires audit evidence for local key-management controls
      MUST define an audit interval and MUST collect, at minimum, the following
      evidence without recording private key material, seed material, or
      plaintext recovery secrets:
    </t>

    <ol>
      <li>Key inventory records identifying each AID, key identifier,
      <tt>identity.version</tt>, key creation time, key activation time, key
      retirement time when applicable, and the protected storage boundary used
      for the key.</li>
      <li>Control evidence that private keys are not stored in plaintext at
      rest, transmitted in protocol messages, or included in logs. Acceptable
      evidence includes configuration attestations, HSM or secure-enclave
      policy exports, operating-system keychain policy exports, or signed
      operator attestations bound to a deployment profile.</li>
      <li>Rotation records for every key rotation, including the old key
      identifier, new key identifier, submitted Agent Identity version,
      <tt>previous_key_signature</tt> verification result, Registry acceptance
      time, and the time at which issuance with the retiring private key
      stopped.</li>
      <li>Compromise-response records for every
      <tt>key_compromised</tt> event, including the Revocation Object
      identifier, affected AID, time of revocation submission, and delegation
      re-establishment records if a replacement AID is registered.</li>
      <li>For sub-agent delegation where the parent generates the child key,
      provisioning and deletion records containing the parent AID, child AID,
      provisioning time, secure channel or key-wrapping mechanism identifier,
      deletion time for any parent-held child private-key copy, and the local
      process or operator identity that performed the deletion.</li>
    </ol>

    <t>
      Audit records for <tt>key_compromised</tt> events MUST NOT include
      verbatim Credential Token or Step Execution Token payloads. Token
      metadata (AID, <tt>jti</tt>, scope list, <tt>iat</tt>, <tt>exp</tt>) is
      sufficient for key-compromise timeline reconstruction and is the maximum
      permissible audit record content for token-related events.
    </t>

    <t>
      Audit evidence MAY be reviewed by deployment operators, external
      auditors, or certification programs, but it is not exchanged as part of
      Credential Token validation. A Registry or Relying Party MUST NOT reject
      an otherwise valid protocol message solely because this audit evidence is
      absent from the wire message. Unless a deployment claims the Deployment
      Key-Management Audit Profile in <xref target="optional-feature-profiles"/>,
      compliance with these local key-management controls is self-attested by
      the implementation's conformance claim.
    </t>

    <t>
      <strong>Key rotation:</strong> Generate a new keypair, increment
      version, include <tt>previous_key_signature</tt> signed by the retiring
      key, and submit the new public key to the Registry. The Registry MUST
      retain the retiring public verification key according to the historical
      key retention rule in Section 19.7. The agent MUST stop issuing new
      tokens with the retiring private key after rotation succeeds.
    </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. The issuer AID is the replay
      domain: an agent MUST NOT reuse a <tt>jti</tt> across different
      delegation chains, principals, grants, scopes, or audiences.
    </t>

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

  <section anchor="mtls-certificate-profile" numbered="true">
  <name>Tier 3 mTLS Certificate Profile</name>
    <t>
      Tier 3 operations require mutual TLS with an X.509 client certificate
      that binds the TLS client to the effective actor AID. The binding MUST
      use the certificate <tt>subjectAltName</tt> extension, not the subject
      DN or common name.
    </t>

    <ol>
      <li>The Relying Party MUST validate the client certificate path per
      <xref target="RFC5280"/> to a trust anchor explicitly configured for the
      target Registry, resource server, or deployment profile. The public Web
      PKI MUST NOT be treated as sufficient for AIP actor binding unless that
      CA set is explicitly configured as the deployment's Tier 3 client-certificate
      trust anchor set.</li>
      <li>The certificate MUST be within its validity interval, MUST permit
      digital signatures in <tt>keyUsage</tt> when <tt>keyUsage</tt> is
      present, and MUST contain the <tt>id-kp-clientAuth</tt> extended key
      usage when <tt>extendedKeyUsage</tt> is present.</li>
      <li>The certificate MUST contain exactly one URI
      <tt>subjectAltName</tt> value that matches the <tt>did:aip</tt> AID
      syntax. That URI SAN value is the certificate actor AID. The Relying
      Party MUST compare it byte-for-byte to the effective actor AID after
      ordinary URI SAN string extraction; it MUST NOT use subject DN, common
      name, email SAN, DNS SAN, or organization fields for actor binding.</li>
      <li>If the client certificate is absent, the Relying Party MUST reject
      with <tt>mtls_required</tt>. If path validation fails, required
      key-usage or EKU checks fail, the AIP URI SAN is absent, more than one
      AIP URI SAN is present, or the AIP URI SAN does not equal the effective
      actor AID, the Relying Party MUST reject with
      <tt>invalid_token</tt>.</li>
      <li>The Relying Party MUST perform certificate revocation checking using
      OCSP per <xref target="RFC6960"/> or a deployment-profile mechanism that
      provides at least equivalent freshness and issuer authorization. An
      equivalent deployment-profile mechanism MUST, at minimum, bind status to
      the certificate issuer or delegated status authority, provide signed or
      otherwise authenticated certificate status, define a maximum status
      freshness interval no weaker than the OCSP policy it replaces, fail
      closed on unavailable or indeterminate status, and be documented in the
      deployment's <tt>mtls_trust_anchors_uri</tt> material. A revoked client
      certificate MUST be rejected with
      <tt>agent_revoked</tt>. An unavailable, stale, or indeterminate
      certificate revocation status MUST be rejected with
      <tt>invalid_token</tt>.</li>
    </ol>

    <t>
      A Registry or deployment that accepts Tier 3 registrations or advertises
      Tier 3 operation support MUST document the client-certificate trust
      anchor set and revocation-checking mechanism. If this information is
      published through Registry Metadata, the fields are
      <tt>mtls_client_certificate_profile</tt> and
      <tt>mtls_trust_anchors_uri</tt>.
    </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 <tt>task_id</tt>. The Registry
      MUST auto-revoke ephemeral agents when their
      <tt>lifecycle_expires_at</tt> deadline passes, as defined in
      Section 15. A Registry that leaves an ephemeral AID active after that
      deadline is non-conformant, even if Relying Parties would separately
      reject expired Principal Tokens or Capability Manifests during
      validation.
    </t>

    <t>
      Parent agents that generate child-agent private keys during
      sub-agent delegation MUST delete any retained copy after successful
      provisioning. This requirement is not wire-verifiable by Registries
      or Relying Parties; it is an implementation conformance requirement
      for the parent agent's key-management boundary. Deployment profiles that
      audit this requirement MUST use the sub-agent provisioning and deletion
      evidence defined in <xref target="key-management"/>.
    </t>
  </section>

  <section anchor="registry-security" numbered="true">
  <name>Registry Security</name>
    <t>
      All write operations MUST be authenticated.
      Revocation <tt>issued_by</tt>, <tt>kid</tt>, issuer authorization, and
      signature verification MUST follow the ordered Revocation Submission
      Validation algorithm in <xref target="revocation-submission-validation"/>.
    </t>

    <t>
      Registry read endpoint availability is an operational service-level
      objective, not a wire-protocol conformance condition. Deployment
      profiles MAY define measurable availability targets, measurement
      intervals, exclusions, evidence, and remedies. This specification does
      not define conformance failure solely by an uptime percentage. 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 <tt>full_revoke</tt> for agents that fail to submit
      an authenticated heartbeat to
      <tt>POST /v1/agents/{aid}/heartbeat</tt> within a configured window
      (RECOMMENDED: 24 hours). The heartbeat authentication and liveness
      timestamp rules are defined in <xref target="agent-heartbeat-registry"/>.
    </t>

    <t>
      Registries that enable the Dead Man's Switch for an AID MUST measure
      the timeout from the Registry-recorded <tt>last_heartbeat_at</tt> value
      or, if no heartbeat has been accepted, from the AID registration time.
      When issuing an automatic revocation for heartbeat timeout, the Registry
      SHOULD use revocation reason <tt>heartbeat_timeout</tt>.
    </t>

    <t>
      <strong>Timing attack mitigation:</strong> Short
      <tt>ttl_max_seconds</tt> values for sensitive scopes in the synced AIP
      Scope Catalog limit exploitation windows.
    </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 or Step Execution
      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="appendix-schemas-content" numbered="true" xml:base="sections/appendix-schemas.xml">
  <name>JSON Schema and Catalog Artifact Index</name>
  <t>
    The following JSON Schema definitions are normatively referenced
    throughout this specification. They are part of the archived publication
    package for this draft in JSON Schema Draft 2020-12 format under
    <tt>schemas/</tt>. The Draft-02 AIP Catalog Bundle is maintained as the
    deterministic <tt>dist/catalog.json</tt> release artifact in the separate
    <eref target="https://github.com/provai-dev/aip-catalog">aip-catalog</eref>
    repository. The working repository at <eref target="https://github.com/provai-dev/aip-rfc">aip-rfc</eref> is a
    convenience mirror; a branch head, mutable tag, or <tt>/latest</tt>
    schema URI is not a normative artifact.
  </t>
  <t>
    For this draft, the immutable schema identifier for each listed schema is
    the <tt>$id</tt> value ending in <tt>/draft-02</tt>. Implementations
    claiming Draft-02 conformance MUST validate against those
    <tt>/draft-02</tt> identifiers. If a local filename, repository URL, or
    mirror URL conflicts with the schema identified by the <tt>$id</tt>, the
    <tt>$id</tt> and the archived publication package are authoritative.
  </t>

  <dl newline="false">
    <dt>agent-identity.schema.json</dt>
    <dd>Defines the Agent Identity Object structure (Section 5.2).</dd>

    <dt>agent-registration.schema.json</dt>
    <dd>Defines the default <tt>GET /v1/agents/{aid}</tt> Agent Registration
      Metadata response (<xref target="agent-registration-metadata-registry"/>).</dd>

    <dt>aip-registry.schema.json</dt>
    <dd>Defines the Registry Metadata returned by
      <tt>GET /v1/registry-metadata</tt>
      (<xref target="well-known-metadata-registry"/>).</dd>

    <dt>aip-gateway-configuration.schema.json</dt>
    <dd>Defines the AIP gateway OAuth Protected Resource Metadata returned by
      <tt>GET /.well-known/oauth-protected-resource</tt>
      (<xref target="aip-gateway-discovery-document"/>).</dd>

    <dt>public-key-response.schema.json</dt>
    <dd>Defines the Public-Key response returned by
      <tt>GET /v1/agents/{aid}/public-key</tt> and
      <tt>GET /v1/agents/{aid}/public-key/{key-id}</tt>
      (<xref target="agent-public-key-registry"/>).</dd>

    <dt>capability-manifest.schema.json</dt>
    <dd>Defines the Capability Manifest structure (Section 5.3).</dd>

    <dt>credential-token.schema.json</dt>
    <dd>Defines the Credential Token JWT payload structure (Section 5.4).</dd>

    <dt>step-execution-token.schema.json</dt>
    <dd>Defines the Step Execution Token JWT payload structure (Sections
      5.4.1 and 13.8).</dd>

    <dt>principal-token.schema.json</dt>
    <dd>Defines the Principal Token JWT payload structure (Section 5.5).</dd>

    <dt>registration-envelope.schema.json</dt>
    <dd>Defines the Registration Envelope request body structure (Section
      5.6).</dd>

    <dt>resource-record.schema.json</dt>
    <dd>Defines the Registered Resource Record used by the Registry resource
      registration endpoints (<xref target="resource-registration-registry"/>).</dd>

    <dt>registry-trust-record.schema.json</dt>
    <dd>Defines the Registry Trust Record structure used for Registry trust
      bootstrapping and continuity (Section 7.3).</dd>

    <dt>revocation-object.schema.json</dt>
    <dd>Defines the Revocation Object structure (Section 5.7).</dd>

    <dt>revocation-status.schema.json</dt>
    <dd>Defines the live Revocation Status response for
      <tt>GET /v1/agents/{aid}/revocation</tt>
      (<xref target="agent-revocation-status-registry"/>).</dd>

    <dt>crl.schema.json</dt>
    <dd>Defines the signed AIP CRL document returned by <tt>GET /v1/crl</tt>
      and signed <tt>endpoints.crl</tt> distribution URIs
      (<xref target="crl"/> and <xref target="crl-response-registry"/>).</dd>

    <dt>endorsement.schema.json</dt>
    <dd>Defines the Endorsement Object structure (Section 5.8).</dd>

    <dt>error-response.schema.json</dt>
    <dd>Defines the standard AIP JSON error response body
      (<xref target="error-response-format"/>).</dd>

    <dt>approval-envelope.schema.json</dt>
    <dd>Defines the Approval Envelope, Step, and Compensation Step structures
      (Section 13).</dd>

    <dt>approval-step-endpoints.schema.json</dt>
    <dd>Defines request and response body profiles for Approval Envelope
      forward-step and compensation-step claim, complete, and fail endpoints
      (<xref target="step-claim"/> and
      <xref target="approval-endpoints-registry"/>).</dd>

    <dt>capability-overlay.schema.json</dt>
    <dd>Defines the Capability Overlay structure (Section 5.11).</dd>

    <dt>engagement-object.schema.json</dt>
    <dd>Defines the Engagement Object structure including Participant,
      Approval Gate, and Change Log Entry (Section 5.12).</dd>

    <dt>grant-request.schema.json</dt>
    <dd>Defines the Grant Request structure (Section 12.2).</dd>

    <dt>grant-response.schema.json</dt>
    <dd>Defines the Grant Response structure (Section 12.4).</dd>

    <dt>rpnp.schema.json</dt>
    <dd>Defines Registry Push Notification Protocol subscription request,
      subscription response/status, and push payload structures
      (<xref target="rpnp"/>).</dd>
  </dl>

  <t>
    All schema files conform to JSON Schema Draft 2020-12. Implementations MUST
    validate objects against these schemas. Validation failures MUST result in
    rejection.
  </t>
  <t>
    The Draft-02 AIP Catalog Bundle is the immutable catalog artifact
    identified by <tt>catalog_snapshot_id: "https://provai.dev/aip/catalog/draft-02"</tt>.
    Its authoritative bytes are the bytes of the immutable
    <tt>dist/catalog.json</tt> release artifact in the
    <tt>provai-dev/aip-catalog</tt> repository, or a mirror whose SHA-256
    digest exactly matches the
    <tt>aip_catalog_sha256</tt> value published by the Registry. Catalog
    retrieval, mirroring, and digest validation rules are defined in
    <xref target="scope-map-registry"/>. A Registry MUST NOT use a mutable
    catalog source to determine Draft-02 conformance.
  </t>
</section>
    <section anchor="conformance" numbered="true" xml:base="sections/22-conformance.xml">
  <name>Conformance</name>

  <t>
    An implementation claiming conformance to this specification MUST identify
    each conformance class and optional feature profile it implements. A
    conformance claim MUST state the AIP protocol compatibility version
    (<tt>0.3</tt> for this specification), the implemented class names from
    this section, any optional profiles, and the AIP Catalog Snapshot
    identifier and digest used for catalog-bound validation.
  </t>

  <t>
    A conformant implementation MUST satisfy every MUST and REQUIRED
    requirement that applies to each claimed class or profile. A SHOULD or
    RECOMMENDED requirement applies unless the implementation documents a
    specific reason for deviation and the deviation does not violate any MUST
    requirement. Optional profiles are not required for a base class claim, but
    an implementation that claims an optional profile MUST satisfy all
    requirements listed for that profile.
  </t>

  <section anchor="common-conformance" numbered="true">
    <name>Common Requirements</name>
    <t>
      Every conformance class MUST implement the conventions, identifiers,
      algorithms, and serialization rules in <xref target="conventions"/>,
      <xref target="terminology"/>, and <xref target="core-identity"/> that are
      needed for the messages it sends, receives, signs, or validates.
      Implementations that process JSON schemas MUST use the Draft-02 schema
      identifiers listed in <xref target="appendix-schemas-content"/>.
      Implementations that send or receive AIP-aware HTTP messages MUST
      implement the version-header rules in <xref target="token-structure"/>
      and <xref target="versioning"/>. Implementations that return AIP error
      responses MUST use <xref target="error-handling"/>.
    </t>
  </section>

  <section anchor="conformance-classes" numbered="true">
    <name>Conformance Classes</name>
    <table>
      <name>AIP Conformance Classes</name>
      <thead>
        <tr>
          <th align="left">Class</th>
          <th align="left">Implementation role</th>
          <th align="left">Required requirement set</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>AIP Registry</td>
          <td>Authoritative registry, resolver, and metadata service</td>
          <td>Registration processing in <xref target="registration-protocol"/>,
          <tt>did:aip</tt> resolution and Registry trust records in
          <xref target="agent-resolution"/>, revocation submission and CRL
          publication in <xref target="revocation"/> and
          <xref target="revocation-submission-registry"/>, unconditional
          Registry endpoints and catalog sync in <xref target="registry"/>,
          rate limiting in <xref target="rate-limiting"/>, versioning in
          <xref target="versioning"/>, Registry security requirements in
          <xref target="security"/>, and Token Payload Non-Retention. A
          conformant Registry MUST NOT persist Credential Token or Step
          Execution Token payloads, meaning the full JWT payload object, in any
          log, database, or audit record beyond completion of the validation
          algorithm for that token. A Registry MAY retain token metadata (AID,
          <tt>jti</tt>, <tt>aip_scope</tt> list, <tt>exp</tt> timestamp, and
          validation outcome) for audit purposes. This requirement can be
          demonstrated through audit log schema inspection showing no payload
          field in stored validation records.</td>
        </tr>
        <tr>
          <td>AIP Relying Party</td>
          <td>Service or agent that accepts AIP Credential Tokens or Step
          Execution Tokens</td>
          <td>Credential Token validation in
          <xref target="credential-token-validation"/>, Tier conformance in
          <xref target="tier-conformance"/>, revocation checking in
          <xref target="revocation-checking"/>, DPoP and mTLS checks when
          required by <xref target="security"/>, catalog-bound scope
          validation in <xref target="scope-map-registry"/>, and applicable
          rate-limit client behavior in <xref target="rate-limiting"/>.</td>
        </tr>
        <tr>
          <td>AIP Agent Issuer</td>
          <td>Agent runtime or signer that issues Credential Tokens for its
          own AID</td>
          <td>Credential Token structure and issuance rules in
          <xref target="token-structure"/>, token lifetime and refresh rules
          in <xref target="token-issuance"/> and <xref target="token-refresh"/>,
          delegation-chain rules in <xref target="delegation-rules"/>, DPoP
          proof construction when required by <xref target="security"/>, and
          private-key handling requirements in <xref target="security"/>.</td>
        </tr>
        <tr>
          <td>AIP Agent Deployer</td>
          <td>Party that constructs GrantRequests, agent identity material,
          Capability Manifests, and Registration Envelopes</td>
          <td>Agent Identity and Capability Manifest construction in
          <xref target="resource-model"/>, AIP-GRANT initiation and
          GrantResponse validation in <xref target="principal-grant"/>, and
          Registration Envelope submission requirements in
          <xref target="registration-protocol"/>.</td>
        </tr>
        <tr>
          <td>Principal Wallet</td>
          <td>Principal-controlled grant, consent, and root Principal Token
          signing component</td>
          <td>A Principal Wallet is conformant if it satisfies all of the
          following: (1) it holds the principal's DID private key in a manner
          consistent with the key-protection requirements in
          <xref target="key-management"/>; (2) it implements the AIP-GRANT
          consent ceremony (<xref target="principal-grant"/>) for at least one
          grant tier, G1, G2, or G3; (3) for scopes whose AIP Scope Catalog
          entry has <tt>destructive: true</tt>, it MUST display a mandatory
          two-step confirmation flow requiring explicit principal
          acknowledgement before signing the GrantRequest, including at
          minimum display of the destructive scope's Catalog description and a
          distinct user action separate from initial grant submission; (4) it
          MUST NOT sign a GrantRequest whose requested scopes are denied by
          local principal policy or whose display metadata cannot be resolved
          from the trusted AIP Scope Catalog; and (5) its conformance claim
          MUST identify which grant tiers are implemented and whether Tier 3
          mTLS client certificates are supported.</td>
        </tr>
        <tr>
          <td>AIP OAuth Authorization Server</td>
          <td>OAuth AS used for G3 grant ceremonies or token exchange</td>
          <td>OAuth metadata and endpoint requirements in
          <xref target="oauth-registry"/>, G3 binding checks in
          <xref target="g3-grant"/>, token exchange rules in
          <xref target="token-exchange-mcp"/>, and applicable OAuth error
          handling required by the referenced OAuth specifications.</td>
        </tr>
        <tr>
          <td>AIP RPNP Subscriber</td>
          <td>Webhook receiver for Registry Push Notification Protocol
          deliveries</td>
          <td>Subscription request authentication, HMAC verification, JWT
          payload verification, timestamp tolerance, replay handling, and
          fallback behavior in <xref target="rpnp"/>.</td>
        </tr>
      </tbody>
    </table>
  </section>

  <section anchor="optional-feature-profiles" numbered="true">
    <name>Optional Feature Profiles</name>
    <table>
      <name>AIP Optional Feature Profiles</name>
      <thead>
        <tr>
          <th align="left">Profile</th>
          <th align="left">Claiming implementation</th>
          <th align="left">Additional required requirement set</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>G3 Grant Profile</td>
          <td>AIP Registry, AIP OAuth Authorization Server, Principal Wallet,
          or Agent Deployer</td>
          <td>G3 ceremony, PKCE, OAuth binding, authorization-code binding,
          and identity-proofing claim requirements in
          <xref target="g3-grant"/> and <xref target="oauth-registry"/>.</td>
        </tr>
        <tr>
          <td>Approval Envelope Registry Profile</td>
          <td>AIP Registry</td>
          <td>Approval Envelope storage, lifecycle, step claim, complete,
          fail, compensation, SET issuance, and idempotency requirements in
          <xref target="approval-envelopes"/> and the corresponding endpoints
          in <xref target="registry"/>.</td>
        </tr>
        <tr>
          <td>Approval Envelope Relying Party Profile</td>
          <td>AIP Relying Party</td>
          <td>Step Execution Token validation and Approval Envelope step
          verification in <xref target="credential-token-validation"/> Step
          10a and the SET validation profile.</td>
        </tr>
        <tr>
          <td>Engagement Object Profile</td>
          <td>AIP Registry, Agent Deployer, or Relying Party</td>
          <td>Engagement Object, Capability Overlay, participant lifecycle,
          countersignature, and engagement-gate requirements in
          <xref target="engagement-objects"/>,
          <xref target="capability-overlays"/>, and
          <xref target="engagement-endpoints-registry"/>.</td>
        </tr>
        <tr>
          <td>RPNP Registry Profile</td>
          <td>AIP Registry</td>
          <td>RPNP subscription, delivery, retry, degraded-state, and
          endpoint requirements in <xref target="rpnp"/> and
          <xref target="rpnp-endpoints-registry"/>.</td>
        </tr>
        <tr>
          <td>Dead Man's Switch Profile</td>
          <td>AIP Registry or Agent Issuer</td>
          <td>Heartbeat endpoint, authenticated liveness, timeout, and
          revocation behavior in <xref target="registry-security"/> and
          <xref target="registry"/>.</td>
        </tr>
        <tr>
          <td>Deployment Key-Management Audit Profile</td>
          <td>Principal Wallet, Agent Issuer, Agent Deployer, or deployment
          operator</td>
          <td>Protected-key-store evidence, key inventory, rotation,
          compromise-response, and sub-agent provisioning/deletion evidence in
          <xref target="key-management"/>. This profile is not a wire
          validation profile and does not change Registry or Relying Party
          acceptance rules.</td>
        </tr>
        <tr>
          <td>Endorsement Acceptance (Optional Feature)</td>
          <td>AIP Registry</td>
          <td>A Registry that implements <tt>POST /v1/endorsements</tt> MUST
          enforce the minimum acceptance rules in
          <xref target="endorsement-acceptance-requirements"/>. Reputation
          scoring beyond these minimums is implementation-defined and not
          subject to conformance testing. A Registry that does not implement
          the endorsement endpoint is still a conformant Registry; it MUST
          respond to <tt>POST /v1/endorsements</tt> with HTTP 501 Not
          Implemented.</td>
        </tr>
        <tr>
          <td>Reputation Output (Optional Feature)</td>
          <td>AIP Registry</td>
          <td>A Registry that implements
          <tt>GET /v1/agents/{aid}/reputation</tt> MUST return the required
          fields and numeric range invariants in
          <xref target="reputation-scoring"/>. The conformance surface for this
          profile is limited to endpoint availability, required fields, response
          types, pagination of <tt>revocation_history</tt>, and the score range
          0.0 through 5.0. The scoring algorithm, weighting, aggregation, and
          decay policy remain implementation-defined.</td>
        </tr>
      </tbody>
    </table>
  </section>

  <section anchor="conformance-testing-methodology" numbered="true">
    <name>Conformance Testing Methodology</name>
    <t>
      A conformance claim MUST be supported by test evidence for every claimed
      class and optional feature profile. At minimum, the implementation MUST
      execute schema-validation tests for every JSON object it emits or
      accepts, positive and negative tests for each applicable validation step
      in <xref target="credential-token-validation"/>, and endpoint behavior
      tests for every Registry, Relying Party, wallet, deployer, or subscriber
      endpoint included in the claim.
    </t>
    <t>
      Test evidence MUST include the AIP protocol version, Catalog Snapshot
      identifier and digest, implementation version, tested conformance classes,
      optional profiles, test execution timestamp, and pass/fail status for each
      applicable MUST-level requirement. A failed MUST-level test invalidates
      the corresponding conformance claim until corrected or until the
      implementation removes that class or profile from its claim.
    </t>
    <t>
      Until a separate AIP test-vector document is published, implementations
      MAY self-attest by publishing a machine-readable conformance claim and a
      reproducible test report. Self-attestation does not create certification
      by this specification, but it is the minimum evidence required for a
      conformant claim.
    </t>
  </section>

  <section anchor="conformance-claim-format" numbered="true">
    <name>Conformance Claim Content</name>
    <t>
      A conformance claim SHOULD be machine-readable. When published as JSON,
      it SHOULD include at least: <tt>aip_version</tt>, <tt>draft</tt>,
      <tt>classes</tt>, <tt>profiles</tt>, <tt>catalog_snapshot_id</tt>,
      <tt>catalog_sha256</tt>, implementation identifier,
      <tt>test_report_uri</tt>, test execution timestamp, and contact or
      audit URI. A Registry MAY publish this information in or alongside
      <tt>/v1/registry-metadata</tt>. Absence of a conformance claim MUST
      NOT be interpreted as conformance to any class or optional profile.
    </t>
  </section>
</section>
    <section anchor="iana" numbered="true" xml:base="sections/22-iana.xml">
  <name>IANA Considerations</name>
  <t>
    This document requests IANA registration only of the media types in
    <xref target="iana-media-types"/>. AIP uses existing registered mechanisms
    for HTTP authorization and discovery: the Bearer and DPoP authentication
    schemes, OAuth Authorization Server Metadata, and OAuth Protected Resource
    Metadata. The <tt>did:aip</tt> DID method, AIP scope identifiers, AID
    namespaces, grant tier values, and AIP error code values are not IANA
    registries in this version of the specification; their change control is
    described in the subsections below.
  </t>

  <section anchor="iana-did-method" numbered="true">
  <name>Non-IANA DID Method Registry</name>
    <t>
      The W3C DID Method Registry is not an IANA registry. This document
      requests no IANA action for the <tt>did:aip</tt> DID method. If the
      <tt>did:aip</tt> method is submitted to the W3C DID Method Registry,
      the following registration information applies.
    </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>Controller</td>
            <td>The principal or deployer that controls the registered Agent
            Identity Object and the current private key corresponding to the
            AID public key.</td>
          </tr>
          <tr>
            <td>DID Syntax</td>
            <td><tt>did:aip:&lt;namespace&gt;:&lt;32-lowercase-hex-agent-id&gt;</tt>;
            the complete ABNF is defined in <xref target="aid-syntax"/>.</td>
          </tr>
          <tr>
            <td>Create</td>
            <td>Create is performed through <tt>POST /v1/agents</tt> using a
            Registration Envelope validated by <xref target="registration-protocol"/>.</td>
          </tr>
          <tr>
            <td>Read</td>
            <td>Read is performed through DID resolution against the
            authoritative Registry and the <tt>GET /v1/agents/{aid}</tt> and
            public-key endpoints defined in <xref target="registry"/>.</td>
          </tr>
          <tr>
            <td>Update</td>
            <td>The DID identifier is immutable. The only protocol-defined
            update operation is key rotation through <tt>PUT /v1/agents/{aid}</tt>
            with previous-key authorization as defined in
            <xref target="agent-key-rotation-registry"/>.</td>
          </tr>
          <tr>
            <td>Deactivate</td>
            <td>Deactivate is represented by accepted Revocation Objects and
            DID Document deactivation metadata as defined in
            <xref target="revocation"/> and <xref target="agent-resolution"/>.</td>
          </tr>
          <tr>
            <td>Security Considerations</td>
            <td>AIDs are derived from Ed25519 public key material, key rotation
            is versioned and previous-key signed, historical keys are retained
            for validation, and revocation is checked according to the Tier
            rules in this specification.</td>
          </tr>
          <tr>
            <td>Privacy Considerations</td>
            <td>AID resolution exposes agent metadata necessary for validation.
            Registries MUST follow the token payload non-retention and
            privacy-preserving audit requirements in
            <xref target="security"/> and <xref target="conformance"/>.</td>
          </tr>
        </tbody>
      </table>
  </section>

  <section anchor="iana-existing-mechanisms" numbered="true">
  <name>Existing HTTP and Discovery Registrations</name>
    <t>
      AIP Credential Tokens and Step Execution Tokens are presented using the
      registered Bearer HTTP authentication scheme <xref target="RFC6750"/> for
      requests that do not require proof-of-possession, and using the
      registered DPoP HTTP authentication scheme <xref target="RFC9449"/> for
      DPoP-bound requests.
      AIP Registries that act as OAuth Authorization Servers publish
      authorization-server metadata using the registered
      <tt>oauth-authorization-server</tt> well-known URI suffix defined by
      <xref target="RFC8414"/>. AIP-protected gateways publish protected
      resource metadata using the registered
      <tt>oauth-protected-resource</tt> well-known URI suffix defined by
      <xref target="RFC9728"/>.
    </t>
    <t>
      This document does not define a new HTTP authentication scheme and does
      not define a new Well-Known URI. Registry Metadata is published at the
      ordinary Registry API endpoint <tt>/v1/registry-metadata</tt> relative to
      the Registry's <tt>registry_id</tt> origin.
    </t>
  </section>

  <section anchor="iana-scope-namespace" numbered="true">
  <name>AIP Scope Identifiers</name>
    <t>
      AIP scope identifiers are OAuth scope-token compatible dot-notation
      strings defined by the applicable AIP Catalog Snapshot. This document
      does not define a URN namespace for AIP scope values.
    </t>

    <t>
      Concrete AIP scope registrations are maintained in the external
      AIP Catalog for the applicable draft snapshot. This document does not
      request IANA maintenance of individual AIP scope values.
    </t>
  </section>

  <section anchor="iana-namespace-catalog" numbered="true">
  <name>AID Namespace Catalog</name>
    <t>
      Concrete <tt>did:aip</tt> namespace registrations are maintained in the
      external AIP Catalog for the applicable draft snapshot. IANA is not
      requested to maintain individual AIP namespace values in this document.
    </t>
  </section>

  <section anchor="iana-grant-tiers" numbered="true">
  <name>AIP Grant Tier Values</name>
    <t>
      Grant tier values are protocol values defined by this specification,
      not an IANA registry. This document requests no IANA action for grant
      tier values. Future changes to these values require a specification
      update that defines the new value, its registration and authorization
      behavior, and its conformance impact.
    </t>

                <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>AIP Error Codes</name>
    <t>
      AIP error codes are protocol values defined by this specification, not
      an IANA registry. This document requests no IANA action for AIP error
      code values. Future changes to these values require a specification
      update that defines the code, the default HTTP status, the applicable
      endpoint or validation step, and the corresponding error response
      requirements.
    </t>

        <dl newline="true" spacing="normal">
          <dt><tt>invalid_token</tt> (HTTP 401)</dt>
          <dd>Token malformed, invalid signature, or invalid claims.</dd>
          <dt><tt>token_expired</tt> (HTTP 401)</dt>
          <dd>Token <tt>exp</tt> is in the past.</dd>
          <dt><tt>token_replayed</tt> (HTTP 401)</dt>
          <dd>Token <tt>jti</tt> seen before within validity window.</dd>
          <dt><tt>invalid_request</tt> (HTTP 400)</dt>
          <dd>Request syntax, query parameter, pagination cursor, or endpoint-specific request parameter is invalid.</dd>
          <dt><tt>unsupported_version</tt> (HTTP 400)</dt>
          <dd><tt>X-AIP-Version</tt> or token <tt>aip_version</tt> is missing, unsupported, or mutually inconsistent.</dd>
          <dt><tt>dpop_proof_required</tt> (HTTP 401)</dt>
          <dd>DPoP proof is required for the request but the <tt>DPoP</tt> header is absent. Malformed or invalid DPoP proofs return <tt>invalid_token</tt>.</dd>
          <dt><tt>agent_revoked</tt> (HTTP 403)</dt>
          <dd>The AID has been revoked.</dd>
          <dt><tt>insufficient_scope</tt> (HTTP 403)</dt>
          <dd>Operation not within granted scopes.</dd>
          <dt><tt>invalid_delegation_depth</tt> (HTTP 403)</dt>
          <dd><tt>delegation_depth</tt> mismatch or exceeds <tt>max_delegation_depth</tt>.</dd>
          <dt><tt>chain_token_expired</tt> (HTTP 403)</dt>
          <dd>Principal Token in <tt>aip_chain</tt> expired.</dd>
          <dt><tt>delegation_chain_invalid</tt> (HTTP 403)</dt>
          <dd>Structural error in delegation chain.</dd>
          <dt><tt>manifest_invalid</tt> (HTTP 403)</dt>
          <dd>Capability Manifest unavailable, signature failed, or AIP Scope Catalog constraint validation failed.</dd>
          <dt><tt>manifest_expired</tt> (HTTP 403)</dt>
          <dd>Capability Manifest <tt>expires_at</tt> passed.</dd>
          <dt><tt>approval_envelope_invalid</tt> (HTTP 400)</dt>
          <dd>Approval Envelope malformed, signature failed, dependency invalid, or value/hash mismatch.</dd>
          <dt><tt>approval_envelope_expired</tt> (HTTP 403)</dt>
          <dd>The Approval Envelope was not approved before <tt>approval_window_expires_at</tt>.</dd>
          <dt><tt>approval_not_found</tt> (HTTP 404)</dt>
          <dd>Approval Envelope ID not found.</dd>
          <dt><tt>approval_state_conflict</tt> (HTTP 409)</dt>
          <dd>Approval Envelope state changed before the requested approval or rejection transition could be committed.</dd>
          <dt><tt>approval_step_prerequisites_unmet</tt> (HTTP 403)</dt>
          <dd><tt>triggered_by</tt> step not yet completed.</dd>
          <dt><tt>approval_step_already_claimed</tt> (HTTP 409)</dt>
          <dd>Step already claimed by another actor.</dd>
          <dt><tt>approval_step_not_claimed</tt> (HTTP 409)</dt>
          <dd>Step Execution Token presented before the Registry has accepted a claim for that step.</dd>
          <dt><tt>approval_step_action_mismatch</tt> (HTTP 403)</dt>
          <dd>Presented <tt>action_parameters</tt> hash does not match stored <tt>action_hash</tt>.</dd>
          <dt><tt>approval_step_invalid</tt> (HTTP 403)</dt>
          <dd>Step Execution Token verification against Registry failed.</dd>
          <dt><tt>compensation_failed</tt> (HTTP 409)</dt>
          <dd>An Approval Envelope compensation step failed and the envelope is in terminal failed state requiring out-of-band remediation.</dd>
          <dt><tt>engagement_cancelled</tt> (HTTP 409)</dt>
          <dd>The referenced Approval Envelope or step was cancelled because its parent Engagement Object was terminated. The operation cannot proceed.</dd>
          <dt><tt>grant_request_expired</tt> (HTTP 400)</dt>
          <dd>AIP-GRANT <tt>request_expires_at</tt> passed.</dd>
          <dt><tt>grant_request_replayed</tt> (HTTP 400)</dt>
          <dd>AIP-GRANT <tt>grant_request_id</tt> seen before.</dd>
          <dt><tt>grant_request_invalid</tt> (HTTP 400)</dt>
          <dd>GrantRequest malformed, expired, missing from a G3 authorization request, signature failed, inconsistent with OAuth request binding, or cannot be resolved to trusted catalog scope display metadata for consent.</dd>
          <dt><tt>grant_rejected_by_principal</tt> (HTTP 403)</dt>
          <dd>Principal declined the grant.</dd>
          <dt><tt>grant_nonce_mismatch</tt> (HTTP 400)</dt>
          <dd>GrantResponse nonce does not match.</dd>
          <dt><tt>grant_tier_insufficient</tt> (HTTP 403)</dt>
          <dd>Registered <tt>grant_tier</tt> is absent, unrecognised, or below the Grant Tier required for the operation's security Tier.</dd>
          <dt><tt>registration_invalid</tt> (HTTP 400)</dt>
          <dd>Registration Envelope malformed or failed validation, including missing or invalid <tt>grant_tier</tt>.</dd>
          <dt><tt>aid_already_registered</tt> (HTTP 409)</dt>
          <dd>The submitted AID or public key is already associated with a registered, non-revoked AID.</dd>
          <dt><tt>unknown_aid</tt> (HTTP 404)</dt>
          <dd>AID not registered in any accessible Registry.</dd>
          <dt><tt>identity_version_conflict</tt> (HTTP 409)</dt>
          <dd>Agent Identity key rotation version is stale, skips a version, or conflicts with a committed rotation.</dd>
          <dt><tt>revocation_invalid</tt> (HTTP 400)</dt>
          <dd>Revocation Object is malformed, uses a reserved externally submitted reason, has an invalid timestamp, or has an invalid or unverifiable signature.</dd>
          <dt><tt>revocation_unauthorized</tt> (HTTP 403)</dt>
          <dd>Revocation issuer is not authorized for the target or revocation type.</dd>
          <dt><tt>revocation_conflict</tt> (HTTP 409)</dt>
          <dd>Revocation identifier was previously accepted with different object content.</dd>
          <dt><tt>registry_unavailable</tt> (HTTP 503)</dt>
          <dd>Registry or a required DID resolver could not be reached or timed out.</dd>
          <dt><tt>rate_limit_exceeded</tt> (HTTP 429)</dt>
          <dd>Rate limit for this operation exceeded; see Section 19.</dd>
          <dt><tt>mtls_required</tt> (HTTP 403)</dt>
          <dd>Tier 3 operation without mTLS.</dd>
          <dt><tt>invalid_scope</tt> (HTTP 400)</dt>
          <dd>Requested scope is unknown, reserved, removed, retired (including bare <tt>spawn_agents</tt>), conflicting with the synced AIP Scope Catalog, or outside an explicitly documented local/private extension policy.</dd>
          <dt><tt>principal_did_method_forbidden</tt> (HTTP 403)</dt>
          <dd>Principal DID method is not permitted for the operation Tier; Tier 2 and Tier 3 require <tt>did:web</tt>.</dd>
          <dt><tt>identity_proofing_insufficient</tt> (HTTP 403)</dt>
          <dd>G3 identity proofing is absent or does not satisfy requested <tt>acr_values</tt>.</dd>
          <dt><tt>enterprise_policy_denied</tt> (HTTP 403)</dt>
          <dd>The enterprise IdP denied the token exchange request due to Conditional Access, ABAC, or equivalent enterprise policy.</dd>
          <dt><tt>idp_client_misconfigured</tt> (HTTP 500)</dt>
          <dd>The AIP Registry's enterprise IdP client credentials are invalid or expired.</dd>
          <dt><tt>invalid_grant</tt> (HTTP 400)</dt>
          <dd>The enterprise IdP rejected the secondary token exchange grant.</dd>
          <dt><tt>a2a_originator_invalid</tt> (HTTP 400)</dt>
          <dd><tt>aip_originator_aid</tt> is absent in an A2A delegated context, malformed, self-referential, or not bound to the validated delegation chain.</dd>
          <dt><tt>endorsement_invalid</tt> (HTTP 400)</dt>
          <dd>Endorsement Object validation failed, including invalid signature or out-of-range score.</dd>
          <dt><tt>gateway_config_invalid</tt> (HTTP 400)</dt>
          <dd>A gateway OAuth Protected Resource Metadata document is internally inconsistent or violates AIP gateway metadata constraints.</dd>
          <dt><tt>enterprise_assertion_missing</tt> (HTTP 403)</dt>
          <dd>Tier 3 Credential Token does not contain the required <tt>aip_principal_assertion</tt> claim.</dd>
          <dt><tt>enterprise_assertion_invalid</tt> (HTTP 403)</dt>
          <dd><tt>aip_principal_assertion</tt> is malformed, has an untrusted issuer, or fails enterprise IdP signature verification.</dd>
          <dt><tt>enterprise_assertion_principal_mismatch</tt> (HTTP 403)</dt>
          <dd><tt>aip_principal_assertion.sub</tt> does not match the root Principal Token principal identifier in <tt>aip_chain[0]</tt>.</dd>
          <dt><tt>grant_not_found</tt> (HTTP 404)</dt>
          <dd>G1 <tt>grant_id</tt> not found or expired.</dd>
          <dt><tt>grant_deployer_mismatch</tt> (HTTP 403)</dt>
          <dd>G1 <tt>grant_id</tt> does not match deployer.</dd>
          <dt><tt>pkce_required</tt> (HTTP 400)</dt>
          <dd>G3 authorization request missing PKCE or not using <tt>code_challenge_method</tt> <tt>S256</tt>.</dd>
          <dt><tt>registry_untrusted</tt> (HTTP 403)</dt>
          <dd>Registry does not match principal DID-Document-declared Registry.</dd>
          <dt><tt>overlay_exceeds_manifest</tt> (HTTP 400)</dt>
          <dd>Overlay violates CO-1 attenuation rule or AIP Scope Catalog constraint validation.</dd>
          <dt><tt>overlay_issuer_invalid</tt> (HTTP 400)</dt>
          <dd>Overlay issuer uses <tt>did:key</tt>.</dd>
          <dt><tt>overlay_version_conflict</tt> (HTTP 409)</dt>
          <dd>Overlay version not strictly increasing.</dd>
          <dt><tt>overlay_signature_invalid</tt> (HTTP 400)</dt>
          <dd>Overlay signature verification failure.</dd>
          <dt><tt>engagement_terminated</tt> (HTTP 403)</dt>
          <dd>Engagement has been terminated or completed.</dd>
          <dt><tt>engagement_suspended</tt> (HTTP 403)</dt>
          <dd>Engagement is currently suspended.</dd>
          <dt><tt>engagement_participant_removed</tt> (HTTP 403)</dt>
          <dd>Agent removed from engagement.</dd>
          <dt><tt>engagement_gate_pending</tt> (HTTP 403)</dt>
          <dd>Required approval gate not yet approved.</dd>
          <dt><tt>engagement_not_found</tt> (HTTP 404)</dt>
          <dd>Engagement ID not found.</dd>
          <dt><tt>engagement_countersign_required</tt> (HTTP 400)</dt>
          <dd>Missing required Engagement Object countersignature.</dd>
          <dt><tt>engagement_signature_invalid</tt> (HTTP 400)</dt>
          <dd>Engagement Object signature missing, malformed, bound to the wrong DID, or failed verification.</dd>
          <dt><tt>change_log_immutable</tt> (HTTP 400)</dt>
          <dd>Attempt to modify change log entry.</dd>
          <dt><tt>change_log_sequence_invalid</tt> (HTTP 400)</dt>
          <dd>Out-of-sequence change log append.</dd>
          <dt><tt>subscription_auth_required</tt> (HTTP 401)</dt>
          <dd>RPNP subscription request missing DPoP, using an invalid DPoP proof, or failing to bind <tt>subscriber_did</tt> to the DPoP key or Authorization credential.</dd>
          <dt><tt>subscription_scope_forbidden</tt> (HTTP 403)</dt>
          <dd><tt>scope_filter: "all"</tt> rejected by Registry policy.</dd>
          <dt><tt>invalid_webhook_uri</tt> (HTTP 400)</dt>
          <dd>Webhook URI not HTTPS.</dd>
          <dt><tt>subscription_limit_exceeded</tt> (HTTP 429)</dt>
          <dd>RPNP subscription limit reached.</dd>
          <dt><tt>invalid_target</tt> (HTTP 400)</dt>
          <dd>Token exchange resource not registered.</dd>
        </dl>
  </section>

  <section anchor="iana-media-types" numbered="true">
  <name>Media Types</name>
    <t>
      IANA is requested to register the following media types in the
      "Media Types" registry using the registration procedure defined by
      <xref target="RFC6838"/>.
    </t>

                <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>
            <tr>
              <td>application/aip-set+jwt</td>
              <td>AIP Step Execution Token (JWT format)</td>
            </tr>
            <tr>
              <td>application/aip-crl+json</td>
              <td>AIP Certificate Revocation List (signed JSON format)</td>
            </tr>
          </tbody>
        </table>

    <t>
      The typ header value for AIP Credential Tokens
      is <tt>AIP+JWT</tt> per RFC 7515. The typ header value for AIP Step
      Execution Tokens is <tt>AIP-SET+JWT</tt>.
    </t>

    <section anchor="iana-media-type-aip-jwt" numbered="true">
    <name>application/aip+jwt</name>
      <dl newline="true">
        <dt>Type name:</dt>
        <dd>application</dd>
        <dt>Subtype name:</dt>
        <dd>aip+jwt</dd>
        <dt>Required parameters:</dt>
        <dd>N/A</dd>
        <dt>Optional parameters:</dt>
        <dd>N/A</dd>
        <dt>Encoding considerations:</dt>
        <dd>7bit; values use compact JWT/JWS serialization.</dd>
        <dt>Security considerations:</dt>
        <dd>See <xref target="security"/> and the security considerations of
          <xref target="RFC7515"/> and <xref target="RFC7519"/>. AIP
          Credential Tokens are authorization credentials and can be bearer
          credentials unless bound to proof-of-possession material.</dd>
        <dt>Interoperability considerations:</dt>
        <dd>Implementations need the AIP Credential Token profile and
          validation rules in <xref target="credential-tokens"/> and
          <xref target="credential-token-validation"/>.</dd>
        <dt>Published specification:</dt>
        <dd>This document.</dd>
        <dt>Applications that use this media type:</dt>
        <dd>AIP agents, Registries, relying parties, and OAuth authorization
          servers that exchange or validate AIP Credential Tokens.</dd>
        <dt>Fragment identifier considerations:</dt>
        <dd>The syntax and semantics of fragment identifiers are not defined
          for this media type.</dd>
        <dt>Additional information:</dt>
        <dd>Magic number(s): N/A; file extension(s): N/A; Macintosh file type
          code(s): N/A.</dd>
        <dt>Person and email address to contact for further information:</dt>
        <dd>Paras Singla &lt;paras.singla@inviscel.com&gt;</dd>
        <dt>Intended usage:</dt>
        <dd>COMMON</dd>
        <dt>Restrictions on usage:</dt>
        <dd>None</dd>
        <dt>Author:</dt>
        <dd>Paras Singla &lt;paras.singla@inviscel.com&gt;</dd>
        <dt>Change controller:</dt>
        <dd>IETF</dd>
      </dl>
    </section>

    <section anchor="iana-media-type-aip-set-jwt" numbered="true">
    <name>application/aip-set+jwt</name>
      <dl newline="true">
        <dt>Type name:</dt>
        <dd>application</dd>
        <dt>Subtype name:</dt>
        <dd>aip-set+jwt</dd>
        <dt>Required parameters:</dt>
        <dd>N/A</dd>
        <dt>Optional parameters:</dt>
        <dd>N/A</dd>
        <dt>Encoding considerations:</dt>
        <dd>7bit; values use compact JWT/JWS serialization.</dd>
        <dt>Security considerations:</dt>
        <dd>See <xref target="security"/> and the security considerations of
          <xref target="RFC7515"/> and <xref target="RFC7519"/>. AIP Step
          Execution Tokens authorize individual approval-envelope steps and
          require audience, actor, step, status, expiry, and proof-of-possession
          validation before use.</dd>
        <dt>Interoperability considerations:</dt>
        <dd>Implementations need the Step Execution Token validation profile
          in <xref target="set-validation-profile"/>.</dd>
        <dt>Published specification:</dt>
        <dd>This document.</dd>
        <dt>Applications that use this media type:</dt>
        <dd>AIP Registries, agents, and relying parties that claim or execute
          approval-envelope steps.</dd>
        <dt>Fragment identifier considerations:</dt>
        <dd>The syntax and semantics of fragment identifiers are not defined
          for this media type.</dd>
        <dt>Additional information:</dt>
        <dd>Magic number(s): N/A; file extension(s): N/A; Macintosh file type
          code(s): N/A.</dd>
        <dt>Person and email address to contact for further information:</dt>
        <dd>Paras Singla &lt;paras.singla@inviscel.com&gt;</dd>
        <dt>Intended usage:</dt>
        <dd>COMMON</dd>
        <dt>Restrictions on usage:</dt>
        <dd>None</dd>
        <dt>Author:</dt>
        <dd>Paras Singla &lt;paras.singla@inviscel.com&gt;</dd>
        <dt>Change controller:</dt>
        <dd>IETF</dd>
      </dl>
    </section>

    <section anchor="iana-media-type-aip-crl-json" numbered="true">
    <name>application/aip-crl+json</name>
      <dl newline="true">
        <dt>Type name:</dt>
        <dd>application</dd>
        <dt>Subtype name:</dt>
        <dd>aip-crl+json</dd>
        <dt>Required parameters:</dt>
        <dd>N/A</dd>
        <dt>Optional parameters:</dt>
        <dd>N/A</dd>
        <dt>Encoding considerations:</dt>
        <dd>8bit; values are JSON encoded using UTF-8.</dd>
        <dt>Security considerations:</dt>
        <dd>See <xref target="security"/> and <xref target="revocation"/>.
          AIP CRL documents carry revocation status and require signature,
          freshness, issuer, and pagination validation before relying parties
          use them for authorization decisions.</dd>
        <dt>Interoperability considerations:</dt>
        <dd>Implementations need the CRL structure and revocation-status
          validation rules in <xref target="revocation"/>.</dd>
        <dt>Published specification:</dt>
        <dd>This document.</dd>
        <dt>Applications that use this media type:</dt>
        <dd>AIP Registries and relying parties that publish, fetch, cache, or
          verify revocation lists.</dd>
        <dt>Fragment identifier considerations:</dt>
        <dd>The syntax and semantics of fragment identifiers are not defined
          for this media type.</dd>
        <dt>Additional information:</dt>
        <dd>Magic number(s): N/A; file extension(s): N/A; Macintosh file type
          code(s): N/A.</dd>
        <dt>Person and email address to contact for further information:</dt>
        <dd>Paras Singla &lt;paras.singla@inviscel.com&gt;</dd>
        <dt>Intended usage:</dt>
        <dd>COMMON</dd>
        <dt>Restrictions on usage:</dt>
        <dd>None</dd>
        <dt>Author:</dt>
        <dd>Paras Singla &lt;paras.singla@inviscel.com&gt;</dd>
        <dt>Change controller:</dt>
        <dd>IETF</dd>
      </dl>
    </section>
  </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="RFC6750">
    <front><title>The OAuth 2.0 Authorization Framework: Bearer Token Usage</title><author><organization>Jones, M. and D. Hardt</organization></author><date month="October" year="2012"/></front>
    <seriesInfo name="RFC" value="6750"/>
    <seriesInfo name="DOI" value="10.17487/RFC6750"/>
  </reference>
  <reference anchor="RFC6838">
    <front><title>Media Type Specifications and Registration Procedures</title><author><organization>Freed, N., Klensin, J., and T. Hansen</organization></author><date month="January" year="2013"/></front>
    <seriesInfo name="BCP" value="13"/>
    <seriesInfo name="RFC" value="6838"/>
    <seriesInfo name="DOI" value="10.17487/RFC6838"/>
  </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="RFC7523">
    <front><title>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants</title><author><organization>Jones, M., Campbell, B., and C. Mortimore</organization></author><date month="April" year="2015"/></front>
    <seriesInfo name="RFC" value="7523"/>
  </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="I-D.ietf-oauth-v2-1">
    <front><title>The OAuth 2.1 Authorization Framework</title><author><organization>Hardt, D., Parecki, A., and T. Lodderstedt</organization></author><date month="March" year="2026"/></front>
    <seriesInfo name="Internet-Draft" value="draft-ietf-oauth-v2-1-15"/>
  </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="RFC9728">
    <front><title>OAuth 2.0 Protected Resource Metadata</title><author><organization>Jones, M. B., Hunt, P., and A. Parecki</organization></author><date month="April" year="2025"/></front>
    <seriesInfo name="RFC" value="9728"/>
    <seriesInfo name="DOI" value="10.17487/RFC9728"/>
  </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="MCP-STATELESS">
    <front><title>MCP Stateless Transport Specification (draft)</title><author><organization>Anthropic</organization></author><date year="2026"/></front>
  </reference>
  <reference anchor="MS-ENTRAAGENTID" target="https://learn.microsoft.com/en-us/entra/agent-id/agent-identities">
    <front><title>Overview of agent identities in Microsoft Entra</title><author><organization>Microsoft</organization></author><date month="April" year="2026"/></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>
  <reference anchor="RFC5011">
    <front><title>Automated Updates of DNS Security (DNSSEC) Trust Anchors</title><author><organization>StJohns, M.</organization></author><date month="September" year="2007"/></front>
    <seriesInfo name="RFC" value="5011"/>
    <seriesInfo name="DOI" value="10.17487/RFC5011"/>
  </reference>
  <reference anchor="TUF">
    <front><title>The Update Framework Specification</title><author><organization>The Update Framework Authors</organization></author><date year="2024"/></front>
    <seriesInfo name="Version" value="1.0.26"/>
    <seriesInfo name="URI" value="https://theupdateframework.github.io/specification/latest/"/>
  </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-implementation-considerations" numbered="false">
      <name>Appendix A: Implementation Considerations (Non-Normative)</name>
      <t>
        AIP implementations may integrate the Enterprise IdP Federation
        Profile (<xref target="enterprise-idp-federation-profile"/>) with
        enterprise identity providers by registering as an OAuth client with
        the enterprise IdP and using the RFC 7523 JWT Bearer grant or RFC 8693
        Token Exchange grant type as described in
        <xref target="enterprise-idp-federation-profile"/>.
      </t>
      <t>
        For stateless or horizontally-scaled deployments, the shared token
        cache backend requirement is defined normatively in
        <xref target="token-cache-requirements-stateless"/>. Development
        deployments can use any single-instance cache store consistent with
        the definition of a non-production deployment in
        <xref target="terminology"/>.
      </t>
      <t>
        Specific implementation guides for individual products or platforms
        are published separately by their respective maintainers and are not
        part of this specification.
      </t>
    </section>
  </back>

</rfc>
