| Internet-Draft | AIP | April 2026 |
| Singla | Expires 19 October 2026 | [Page] |
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.¶
This document specifies the core identity model (did:aip), principal token structure, credential token format, registration protocol, delegation rules, chained approval envelopes for multi-step workflows, capability overlays for context-scoped attenuation, and the validation algorithm that all relying parties MUST implement.¶
This note is to be removed before publishing as an RFC.¶
Discussion of this document is taking place on the AIP specification repository at https://github.com/provai-dev/aip-spec. Source text and issue tracking can be found there.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 19 October 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
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.¶
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.¶
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.¶
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.¶
Existing deployments typically operate in one of two modes: no
access, or full access. AIP introduces fine-grained capability
declarations. A principal can grant email.read without
email.send, or transactions with a
max_daily_total constraint.¶
AIP is built on five design principles:¶
Neutral and open. The spec is released under CC0 and the
did:aip DID method is registered with the W3C.¶
Build on existing standards. AIP builds on W3C DID, JWT [RFC7519], JWK [RFC7517], DPoP [RFC9449], and CRL patterns from [RFC5280].¶
Human sovereignty. Every agent action must trace to a human or authorised organisational principal.¶
Security proportional to risk. AIP defines three security tiers matching overhead to risk level.¶
Deterministic verification. The Validation Algorithm is fully deterministic: two independent implementations executing the same steps on the same token will reach the same result.¶
Zero Trust Architecture. AIP implements the zero trust principles defined in [SP-800-207] and satisfies NIST SP 800-63-4 [SP-800-63-4] AAL2 for agent-to-service interactions. No agent is implicitly trusted by virtue of its origin, network location, or prior successful interaction. Every Credential Token must be validated against the Registry on every interaction. Short-lived Credential Tokens (Section 7.2) bound by a mandatory TTL limit the blast radius of any credential compromise. Revocation is checked in real time for Tier 2 sensitive operations (Section 17.3). DPoP proof-of-possession (Section 17.3) ensures that possession of a valid Credential Token is insufficient for impersonation without the corresponding private key material, satisfying the anti-replay requirement of [SP-800-207] Section 3.¶
Integration with Model Context Protocol (MCP). AIP provides the agent identity, authorization, and delegation layer that MCP server implementations require but do not define. An AIP-authenticated agent can obtain scoped access tokens for MCP servers via the token exchange mechanism defined in Section 7.5. AIP does not replace MCP; it provides the identity substrate on which MCP authorization decisions are made. See Section 7.5 (Token Exchange) and Section 15.5 (OAuth Authorization Server) for the concrete integration path.¶
Least Privilege. Agents are granted only the specific capabilities required for their declared purpose, expressed as signed Capability Manifests (Section 8). Capability grants are always additive from principal to agent and never exceed the granting principal's own capability set. A child agent must not be granted capabilities that the delegating parent does not itself hold (Rule D-1, Section 8.2). The max_delegation_depth field (Section 8.1) bounds the depth of sub-agent hierarchies, limiting transitive capability propagation. Capability constraints allow fine-grained least-privilege expression within each capability category, consistent with [SP-800-207] Section 3 (Tenets 5 and 6).¶
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.¶
Relationship to MCP. The Model Context Protocol [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.¶
Out of scope - Prompt injection prevention. AIP is an identity and
authorisation protocol. Whether the content an agent processes
contains adversarial instructions is an application-layer and
model-layer concern outside this specification's scope. AIP
mitigates the persistence window of a successful prompt injection
attack: a compromised agent may be immediately revoked via
full_revoke (Section 9.1), and revocation propagates to all
child agents within the TTL window defined in Section 7.2. AIP does
not prevent the initial injection - it limits the attacker's
persistence.¶
AIP is structured as six ordered layers:¶
+------------------------------------------------------+ | Layer 6 - Reputation Trust over time | +------------------------------------------------------+ | Layer 5 - Revocation Standard kill switch | +------------------------------------------------------+ | Layer 4 - Credential Token & 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 | +------------------------------------------------------+
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
This document 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.¶
JSON is used throughout this document as defined in [RFC8259]. URIs are used as defined in [RFC3986]. ABNF notation is used as defined in [RFC5234], including its core rules (ALPHA, DIGIT, HEXDIG, SP, etc.) as defined in RFC5234 Appendix B.1. HTTP status codes, headers, and semantics are as defined in [RFC9110].¶
All Ed25519 signatures are computed per [RFC8032]. DPoP proofs are constructed per [RFC9449].¶
Several objects in this specification are signed outside the JWT framework. For those objects, the following canonical JSON serialization procedure MUST be used when computing or verifying signatures, and when computing Action Hashes (Section 4.6.7):¶
"signature" field to the empty string "". The
"signature" field MUST be included in the serialisation at
its lexicographically correct position with value "".¶
This procedure is equivalent to the JSON Canonicalization Scheme (JCS) defined in [RFC8785]. Implementations SHOULD use an RFC8785-conformant library to ensure correctness. When this procedure is used to compute Action Hashes (Section 4.6.7), implementations MUST use an RFC8785-conformant library; custom serializer implementations MUST NOT be used for that purpose.¶
EXAMPLE (informative):¶
{"z": 1, "a": 2, "m": [3,1,2]} → {"a":2,"m":[3,1,2],"z":1}
¶
NOTE: This canonical serialization applies to Capability Manifests, Agent Identity Objects, Revocation Objects, Endorsement Objects, GrantRequest Objects (when signed), and Approval Envelopes. It does NOT apply to JWT-format objects (Credential Tokens, Principal Tokens), which use standard JWS compact serialization per [RFC7515].¶
The following terms are used throughout this specification:¶
A globally unique, persistent identifier for an agent conforming to the did:aip DID method defined in Section 6. An AID is a W3C DID.¶
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).¶
A signed JWT payload encoding one delegation link in the AIP principal chain. Principal Tokens are embedded as compact-serialised JWTs in the aip_chain array of a Credential Token. Defined in Section 4.2.4.¶
full_revoke) or in part (scope_revoke
or delegation_revoke). Defined in Section 9.1.¶
Every AIP agent has a globally unique, persistent identifier conforming to the W3C Decentralized Identifier (DID) standard [W3C-DID]. The AIP DID method is did:aip, registered with the W3C DID method
registry.¶
An Agent Identity Descriptor (AID) conforms to the following ABNF:¶
AID = "did:aip:" namespace ":" agent-id namespace = ALPHA *(ALPHA / DIGIT / "-") agent-id = 32*HEXDIG ALPHA = %x41-5A / %x61-7A DIGIT = %x30-39 HEXDIG = DIGIT / "a" / "b" / "c" / "d" / "e" / "f"¶
Example: did:aip:personal:9f3a1c82b4e6d7f0a2b5c8e1d4f7a0b3¶
namespace: A lowercase alphanumeric string (ALPHA followed
by any combination of ALPHA, DIGIT, or hyphens) identifying the agent
type. Registered namespaces include: personal,
enterprise, service, orchestrator,
ephemeral. Custom namespaces MAY be registered.¶
agent-id: A 32-character hexadecimal string (lowercase)
computed as the SHA-256 hash of the agent's public Ed25519 key
material (the JWK x value base64url-decoded).¶
An AID is derived deterministically from the agent's Ed25519 public key:¶
kty="OKP",
crv="Ed25519", and the public key value in the x
field (base64url-encoded).¶
SHA-256(base64url_decode(x)) to obtain a 32-byte
hash.¶
agent-id.¶
namespace to form the complete
AID: did:aip:<namespace>:<agent-id>.¶
This derivation is deterministic and cryptographically self-verifying: possession of the private key is both necessary and sufficient to claim ownership of the AID.¶
The following namespaces are registered:¶
personal:enterprise:service:orchestrator:An agent that spawns and manages sub-agents. Typically has
spawn_agents.create and spawn_agents.manage
scopes.¶
ephemeral:task_id in their Principal Token and MUST
have a limited delegation_valid_for_seconds (typically <
1 hour).¶
Custom namespaces MAY be registered with the W3C and the AIP registry. Namespaces are immutable once registered.¶
The Agent Identity Object is the base document for an agent. It includes the AID, public key material, and metadata. The schema is defined in Section 4.2.1 (see also agent-identity.schema.json in the schemas directory).¶
Key fields:¶
aid: The agent's AID (REQUIRED)¶
name: Human-readable agent name (REQUIRED)¶
type: The namespace component of the AID (REQUIRED, MUST
match)¶
model: AI model information including provider and
model_id (REQUIRED)¶
public_key: JWK format Ed25519 public key (REQUIRED)¶
created_at: ISO 8601 timestamp (REQUIRED)¶
version: Identity version number for key rotation
(REQUIRED, minimum 1)¶
previous_key_signature: EdDSA signature by previous key
when rotating (REQUIRED if version > 1)¶
An AID MUST be globally unique. Once an agent is registered in a Registry, its AID is immutable. An AID cannot be reused or transferred. Two agents with identical public keys will derive identical AIDs, so the cryptographic relationship is deterministic.¶
This section defines the core data structures in AIP: JSON Schema representations of agents, principals, capabilities, tokens, and enrollment objects. All objects conform to the canonical JSON serialization rules of Section 2.1.¶
Agent Identities (AIDs) are W3C Decentralized Identifiers [W3C-DID] using the did:aip method. The ABNF grammar uses core rules from [RFC5234] Appendix B.1 (DIGIT) and defines two additional terminal rules:¶
aip-did = "did:aip:" namespace ":" unique-id
namespace = LOALPHA *( LOALPHA / DIGIT )
*( "-" 1*( LOALPHA / DIGIT ) )
unique-id = 32LOHEXDIG
LOALPHA = %x61-7A ; a-z only, lowercase
LOHEXDIG = DIGIT / "a" / "b" / "c" / "d" / "e" / "f"
; lowercase hex digit only
¶
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.¶
The following namespace values are defined:¶
| Namespace | Description |
|---|---|
| personal | An agent acting for a single human principal |
| enterprise | An agent acting within an organisational deployment |
| service | A persistent agent providing a capability as a service |
| ephemeral | An agent created for a single task; revoked on completion |
| orchestrator | An agent whose primary function is spawning child agents |
| registry | An AIP Registry instance; at most one active AID per deployment |
A registry-namespace AID MUST NOT be registered via the standard POST /v1/agents endpoint. It MUST be created exclusively via the Registry Genesis procedure (Section 6.3).¶
Compound typed identifiers use prefixed UUID v4 values:¶
| Object Type | Prefix | Example |
|---|---|---|
| Capability Manifest | cm: | cm:550e8400-e29b-41d4-a716-... |
| Revocation Object | rev: | rev:6ba7b810-9dad-11d1-80b4-... |
| Endorsement Object | end: | end:6ba7b811-9dad-11d1-80b4-... |
| Grant Request | gr: | gr:550e8401-e29b-41d4-a716-... |
| Approval Envelope | apr: | apr:550e8402-e29b-41d4-a716-... |
The Agent Identity Object is the canonical signed JSON document that establishes an agent's identity. Canonical signing field order: aid, name, type, model, created_at, version, public_key, previous_key_signature.¶
Type: string. Required: REQUIRED. Constraints: MUST match did:aip ABNF; pattern is lowercase namespace plus 32 lowercase hexadecimal characters.¶
Type: string. Required: REQUIRED. Constraints: minLength: 1, maxLength: 64.¶
Type: string. Required: REQUIRED. Constraints: MUST exactly match namespace component of aid; pattern is lowercase alphanumeric with optional hyphen-separated segments.¶
Type: object. Required: REQUIRED. Constraints: See sub-fields below.¶
Type: string. Required: REQUIRED. Constraints: minLength: 1, maxLength: 64.¶
Type: string. Required: REQUIRED. Constraints: minLength: 1, maxLength: 128.¶
Type: string. Required: OPTIONAL. Constraints: pattern ^sha256:[0-9a-f]{64}$.¶
Type: string. Required: REQUIRED. Constraints: ISO 8601 UTC; format: date-time; immutable after registration.¶
Type: integer. Required: REQUIRED. Constraints: minimum: 1; MUST increment by exactly 1 on key rotation.¶
Type: object. Required: REQUIRED. Constraints: JWK per [RFC7517]; Ed25519 (kty=OKP, crv=Ed25519) per [RFC8037].¶
Type: string. Required: REQUIRED. Constraints: const: "OKP".¶
Type: string. Required: REQUIRED. Constraints: const: "Ed25519".¶
Type: string. Required: REQUIRED. Constraints: pattern ^[A-Za-z0-9_-]{43}$ (32 bytes base64url, no padding).¶
Type: string. Required: REQUIRED. Constraints: DID URL using the agent AID followed by #key-N where N is a positive integer.¶
Type: string. Required: OPTIONAL (version=1); REQUIRED (version>=2). Constraints: base64url EdDSA signature of new object (with this field set to ""); pattern ^[A-Za-z0-9_-]+$.¶
Normative requirements:¶
The Capability Manifest is a versioned, signed JSON document that declares the specific permissions granted to an agent. Canonical signing field order: manifest_id, aid, granted_by, version, issued_at, expires_at, capabilities, signature.¶
| Field | Type | Required | Constraints |
|---|---|---|---|
| manifest_id | string | REQUIRED | pattern: ^cm:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ |
| aid | string | REQUIRED | MUST match did:aip ABNF |
| granted_by | string | REQUIRED | MUST be a valid W3C DID; pattern: any valid DID method and suffix |
| version | integer | REQUIRED | minimum: 1; MUST increment on every update including scope_revoke |
| issued_at | string | REQUIRED | ISO 8601 UTC; format: date-time |
| expires_at | string | REQUIRED | ISO 8601 UTC; MUST be after issued_at |
| capabilities | object | REQUIRED | See Section 4.3 for all sub-fields |
| signature | string | REQUIRED | base64url EdDSA signature; pattern: ^[A-Za-z0-9_-]+$ |
A new manifest_id MUST be generated on every update, including scope_revoke operations. Relying Parties MUST verify the manifest signature field using the public key of the granted_by DID before trusting any capability declared within it.¶
An AIP Credential Token is a compact JWT with the following format:¶
aip-token = JWT-header "." JWT-payload "." JWT-signature
¶
The token MUST be a valid JWT as defined by [RFC7519].¶
JWT Header fields:¶
| Field | Requirement | Value / Constraints |
|---|---|---|
| typ | MUST | "AIP+JWT" |
| alg | MUST | "EdDSA" (REQUIRED); "ES256" (OPTIONAL); "RS256" (OPTIONAL, legacy enterprise only) |
| kid | MUST | DID URL identifying the signing key; MUST match the kid in the signing agent's Agent Identity |
Credential Token Payload fields:¶
| Field | Type | Required | Constraints |
|---|---|---|---|
| aip_version | string | REQUIRED | MUST be "0.3" for this spec |
| iss | string | REQUIRED | MUST match did:aip ABNF; MUST equal sub of last aip_chain element |
| sub | string | REQUIRED | MUST match did:aip ABNF; MUST equal iss for non-delegated tokens |
| aud | string/array | REQUIRED | Single string or array; MUST include the Relying Party's identifier |
| iat | integer | REQUIRED | Unix timestamp; MUST NOT be in the future (30-second clock skew tolerance) |
| exp | integer | REQUIRED | Unix timestamp; MUST be strictly greater than iat; TTL limits per Section 7.2 |
| jti | string | REQUIRED | UUID v4 canonical lowercase |
| aip_scope | array | REQUIRED | minItems: 1; uniqueItems: true; each item matches ^[a-z_]+([.][a-z_]+)*$ |
| aip_chain | array | REQUIRED | minItems: 1; maxItems: 11; each element is a compact-serialised signed Principal Token JWT |
| aip_registry | string | OPTIONAL | URI of AIP Registry |
| aip_approval_id | string | OPTIONAL | pattern: ^apr:[0-9a-f]{8}-...$; REQUIRED when token is a step-claim token |
| aip_approval_step | integer | OPTIONAL | Positive integer (>= 1); REQUIRED when aip_approval_id is present |
| aip_engagement_id | string | OPTIONAL | pattern: ^eng:[0-9a-f]{8}-...$; present when token is scoped to an Engagement Object |
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.¶
| Field | Type | Required | Constraints |
|---|---|---|---|
| iss | string | REQUIRED | W3C DID or did:aip AID; for delegation_depth 0: MUST equal principal.id; for delegation_depth > 0: MUST equal delegated_by |
| sub | string | REQUIRED | MUST match did:aip ABNF |
| principal | object | REQUIRED | See sub-fields below |
| principal.type | string | REQUIRED | enum: ["human", "organisation"] |
| principal.id | string | REQUIRED | W3C DID; MUST NOT use did:aip method; MUST be byte-for-byte identical across all chain elements |
| delegated_by | string/null | REQUIRED | null when delegation_depth is 0; MUST be a did:aip AID when delegation_depth > 0 |
| delegation_depth | integer | REQUIRED | minimum: 0, maximum: 10; MUST equal the array index of this token in aip_chain |
| max_delegation_depth | integer | REQUIRED | minimum: 0, maximum: 10; default: 3 when absent; only the value from aip_chain[0] governs the chain |
| issued_at | string | REQUIRED | ISO 8601 UTC; format: date-time |
| expires_at | string | REQUIRED | ISO 8601 UTC; MUST be strictly after issued_at |
| purpose | string | OPTIONAL | maxLength: 128 |
| task_id | string/null | OPTIONAL; REQUIRED for ephemeral agents | minLength: 1, maxLength: 256 when non-null |
| scope | array | REQUIRED | minItems: 1; uniqueItems: true |
| acr | string | OPTIONAL; REQUIRED when grant ceremony is G3 | Authentication context class reference |
| amr | array | OPTIONAL; REQUIRED when grant ceremony is G3 | Authentication method reference values per [RFC8176] |
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:.¶
The Registration Envelope is the request body submitted to POST /v1/agents to register a new AIP agent.¶
| Field | Type | Required | Constraints |
|---|---|---|---|
| identity | object | REQUIRED | MUST conform to agent-identity schema; version MUST be 1; previous_key_signature MUST NOT be present |
| capability_manifest | object | REQUIRED | MUST conform to capability-manifest schema; version MUST be 1; aid MUST equal identity.aid |
| principal_token | string | REQUIRED | Compact JWT (header.payload.signature); delegation_depth MUST be 0; delegated_by MUST be null |
| grant_tier | string | REQUIRED | enum: ["G1", "G2", "G3"]; MUST be consistent with principal_token's delegation_depth and the scopes in capability_manifest |
Revocation is performed by submitting a signed Revocation Object to POST /v1/revocations. Canonical signing field order: revocation_id, target_aid, type, issued_by, reason, timestamp, propagate_to_children, scopes_revoked, signature.¶
| Field | Type | Required | Constraints |
|---|---|---|---|
| revocation_id | string | REQUIRED | pattern: ^rev:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ |
| target_aid | string | REQUIRED | MUST match did:aip ABNF |
| type | string | REQUIRED | enum: ["full_revoke", "scope_revoke", "delegation_revoke", "principal_revoke"] |
| issued_by | string | REQUIRED | W3C DID; in target's chain |
| reason | string | REQUIRED | enum of defined reasons |
| timestamp | string | REQUIRED | ISO 8601 UTC |
| propagate_to_children | boolean | OPTIONAL | default: false |
| scopes_revoked | array | REQUIRED when scope_revoke; MUST NOT otherwise | minItems: 1 |
| signature | string | REQUIRED | base64url EdDSA |
Revocation Types:¶
Any Relying Party or agent MAY submit a signed Endorsement Object to the Registry after a completed interaction. Canonical signing field order: endorsement_id, from_aid, to_aid, task_id, outcome, notes, timestamp, signature.¶
| Field | Type | Required | Constraints |
|---|---|---|---|
| endorsement_id | string | REQUIRED | pattern: ^end:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ |
| from_aid | string | REQUIRED | MUST NOT equal to_aid |
| to_aid | string | REQUIRED | MUST NOT equal from_aid |
| task_id | string | REQUIRED | minLength: 1, maxLength: 256 |
| outcome | string | REQUIRED | enum: ["success", "partial", "failure"] |
| notes | string/null | OPTIONAL | maxLength: 512 |
| timestamp | string | REQUIRED | ISO 8601 UTC |
| signature | string | REQUIRED | base64url EdDSA of from_aid |
The Registry MUST verify every submitted Endorsement Object signature. Only success and partial outcomes increment endorsement_count. failure increments incident_count.¶
Capability capabilities sub-fields are defined in full in schemas/v0.3/capability-manifest.schema.json.¶
Defined Scope Identifiers:¶
email.read email.write email.send
email.delete calendar.read calendar.write
calendar.delete filesystem.read filesystem.write
filesystem.execute filesystem.delete web.browse
web.forms_submit web.download transactions
communicate.whatsapp communicate.telegram communicate.sms
communicate.voice spawn_agents.create spawn_agents.manage
¶
NOTE: The bare scope spawn_agents is retired as of v0.3 and is NOT a defined scope identifier. Validators MUST reject tokens containing spawn_agents without a sub-scope qualifier with invalid_scope. spawn_agents.create and spawn_agents.manage are Tier 2 scopes with a maximum TTL of 300 seconds; DPoP is REQUIRED.¶
Where this document references transactions.* or communicate.*, this means the bare capability key OR any scope beginning with that prefix OR capabilities.transactions.enabled: true / capabilities.communicate.enabled: true in the Capability Manifest.¶
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.¶
Rule D-1. A delegated agent MUST NOT grant scopes or looser constraint values than its own Capability Manifest contains.¶
Rule D-2. A delegated agent MUST NOT issue a Principal Token with max_delegation_depth greater than its remaining depth (max_delegation_depth - delegation_depth).¶
Rule D-3. Implementations MUST reject any Credential Token where the delegation_depth of any chain token exceeds the root token's max_delegation_depth.¶
Rule D-4. The root Principal Token (index 0) MUST have delegation_depth = 0. Each subsequent token at index i MUST have delegation_depth = i. No gaps, skips, or repeated values are permitted.¶
Rule D-5. Each delegation chain token MUST be signed by the private key of the delegated_by AID (or root principal for depth 0).¶
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.¶
Rule CO-1 (Attenuation Only): 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.¶
| Field | Type | Required | Constraints |
|---|---|---|---|
| overlay_id | string | REQUIRED | Pattern: ^co:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ |
| aid | string | REQUIRED | The target agent's AID |
| engagement_id | string | OPTIONAL | Pattern: ^eng:...$; links to Engagement Object (Section 4.8) |
| issued_by | string | REQUIRED | DID of the overlay issuer; MUST be did:web or did:aip (MUST NOT be did:key) |
| overlay_type | string | REQUIRED | MUST be "restrict" |
| issued_at | string | REQUIRED | ISO 8601 UTC timestamp |
| expires_at | string | REQUIRED | ISO 8601 UTC; MUST be strictly after issued_at |
| version | integer | REQUIRED | Positive integer; monotonically increasing per (aid, engagement_id, issued_by) tuple |
| constraints | object | REQUIRED | Uses the same schema as Capability Manifest capabilities sub-object |
| signature | string | REQUIRED | Base64url EdDSA signature by issued_by over JCS canonical JSON |
Overlay Rules:¶
Effective Capability Computation:¶
effective = base_manifest.capabilities
for each active_overlay in applicable_overlays:
effective = intersect(effective, active_overlay.constraints)
¶
Where intersect applies per-field: boolean fields use AND (false wins); numeric limits use min(base, overlay); path arrays use set intersection; scope set to false removes that scope.¶
An Engagement Object is a mutable Registry resource that models a multi-party engagement. It is the parent container for Capability Overlays (Section 4.7) scoped via engagement_id, Approval Envelopes (Section 4.6) scoped via engagement_id, and participant roster and approval gate state.¶
| Field | Type | Required | Constraints |
|---|---|---|---|
| engagement_id | string | REQUIRED | Pattern: ^eng:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ |
| title | string | REQUIRED | maxLength: 256 |
| hiring_operator | string | REQUIRED | DID; MUST be did:web or did:aip |
| deploying_principal | string | REQUIRED | DID of the deploying principal |
| created_at | string | REQUIRED | ISO 8601 UTC |
| expires_at | string | REQUIRED | ISO 8601 UTC; MUST be after created_at |
| status | string | REQUIRED | One of: "proposed", "active", "suspended", "completed", "terminated" |
| participants | array | REQUIRED | Array of Participant objects |
| approval_gates | array | OPTIONAL | Array of Approval Gate objects |
| change_log | array | REQUIRED | Append-only array of Change Log Entry objects |
| hiring_operator_signature | string | REQUIRED | Base64url EdDSA signature by hiring_operator |
| deploying_principal_signature | string | REQUIRED | Countersignature by deploying_principal |
| version | integer | REQUIRED | Monotonically increasing |
Participant object fields: aid (REQUIRED), role (REQUIRED, maxLength: 64), capability_overlay_id (OPTIONAL), added_at (REQUIRED, ISO 8601), added_by (REQUIRED, DID), removed_at (OPTIONAL, ISO 8601).¶
Approval Gate object fields: gate_id (REQUIRED, pattern: ^gate:[a-z0-9_-]+$), name (REQUIRED, maxLength: 128), required_approver (REQUIRED, DID), trigger (REQUIRED, string), status (REQUIRED, one of: "pending", "approved", "rejected"), approved_at (OPTIONAL, ISO 8601).¶
Change Log Entry fields: seq (REQUIRED, monotonically increasing from 1), timestamp (REQUIRED, ISO 8601), action (REQUIRED), actor (REQUIRED, DID), payload (OPTIONAL, object), signature (REQUIRED, Base64url EdDSA by actor over JCS canonical JSON excluding signature).¶
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.¶
The Registry MUST reject any request that modifies or deletes an existing change log entry. Only appends are permitted.¶
Engagement Lifecycle:¶
proposed --> active --> completed
|
+--> suspended --> active (resumed)
|
+--> terminated
¶
Engagement Lifecycle Transitions:¶
Termination Cascade: When an Engagement is terminated:¶
An agent is registered by submitting a Registration Envelope to `POST /v1/agents`. The fields are defined in Section 4.2.5.¶
The `principal_token` MUST have `delegation_depth` equal to 0 and `delegated_by` equal to null. The `sub` field of the decoded principal token payload MUST equal `identity.aid`.¶
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.¶
The Registry MUST record the `delegated_by` field from the decoded `principal_token` payload (or the principal's DID if `delegated_by` is null) in the parent-child delegation index, for use in revocation propagation (Section 9.3).¶
All AIP error responses MUST use the format defined in Section 13.1. Implementations MUST NOT return HTTP 200 for error conditions.¶
6. Agent Resolution¶
6.1. DID Resolution¶
Every AID MUST have a corresponding W3C DID Document resolvable by any standard DID resolver. The DID Document is derived deterministically from the Agent Identity stored in the Registry.¶
The Registry MUST generate and serve DID Documents from GET /v1/agents/{aid} when the
Accept: application/did+ld+json or Accept: application/did+json header is present.¶
AIP implementations MUST support DID resolution for at minimum did:key and did:web
DID methods. Resolved DID Documents MAY be cached for a maximum of 300 seconds. If resolution fails or times
out, implementations MUST treat the result as registry_unavailable and MUST reject the operation.¶
If the AID has been revoked, the Registry MUST return deactivated: true in
didDocumentMetadata, per W3C-DID Section 7.1.2.¶
6.2. DID Document Structure¶
A conformant did:aip DID Document MUST include @context, id,
verificationMethod, authentication, and controller. See the canonical example in the
repository at examples/latest/did-document.json.¶
A principal's DID Document that authorises agents for Tier 2 operations MUST include an AIPRegistry
service entry in its service array:¶
{
"id": "did:web:acme.com#aip-registry",
"type": "AIPRegistry",
"serviceEndpoint": "https://registry.acme.com"
}
¶
The serviceEndpoint MUST be an HTTPS URI pointing to the base URL of the authoritative AIP Registry for
agents delegated by this principal. The type MUST be exactly "AIPRegistry" (case-sensitive).¶
For principals using did:key: an AIPRegistry service entry cannot be declared
(DID Key documents have no service array). Per Section 4.5, did:key principals MUST NOT authorise
Tier 2 operations.¶
For principals using did:web or did:aip: the AIPRegistry service entry is REQUIRED when
the principal authorises any agent with Tier 2 scopes. It is OPTIONAL for principals authorising only Tier 1 agents.¶
The CRUD operations for did:aip are:¶
| Operation | Mechanism |
| Create |
POST /v1/agents with Registration Envelope |
| Read |
GET /v1/agents/{aid} with DID Accept header |
| Update |
PUT /v1/agents/{aid} for key rotation only |
| Deactivate |
POST /v1/revocations with full_revoke object |
6.3. Registry Genesis¶
Registry Genesis is the one-time initialisation procedure by which a new AIP Registry creates and persists its own
did:aip Agent Identity (the Registry AID). It MUST be performed before the Registry serves any requests.¶
6.3.1. Key Generation¶
The Registry MUST generate a fresh Ed25519 keypair at first boot. The private key MUST be stored encrypted at rest (AES-256-GCM or equivalent). The public key is used to construct the Registry's DID Document and to sign CRL documents (Section 9.2) and Step Execution Tokens (Section 4.6.8).¶
6.3.2. AID Construction¶
The Registry MUST construct its AID using the registry namespace (Section 4.1):¶
did:aip:registry:<32-hex-character unique-id>
¶
The unique-id MUST be generated as a cryptographically random 32-character lowercase hexadecimal string.
At most one Registry AID MUST be active per Registry deployment at any time.¶
6.3.3. Self-Registration Exemption¶
The Registry AID MUST NOT be created via POST /v1/agents. Instead, the Registry MUST persist its AID and
keypair directly to its own data store during genesis. The following Registration Envelope checks (Section 5.2) are
explicitly inapplicable to the Registry AID:¶
principal_token validation) — the Registry has no human principal and MUST
NOT carry a principal delegation chain.¶
6.3.4. Well-Known Publication¶
The Registry MUST publish its DID Document and public key at:¶
GET /.well-known/aip-registry¶
The response MUST be a JSON object containing:¶
| Field | Type | Required | Description |
|---|---|---|---|
registry_aid
|
string | REQUIRED | The Registry's did:aip AID |
registry_name
|
string | REQUIRED | Human-readable Registry name (maxLength: 128) |
aip_version
|
string | REQUIRED | The aip_version this Registry conforms to |
public_key
|
object | REQUIRED | JWK representation of the Registry's Ed25519 public key |
endpoints
|
object | REQUIRED | Map of service names to relative or absolute URI paths |
signature
|
string | REQUIRED | Base64url EdDSA signature of the canonical JSON (RFC 8785 JCS) of the document excluding the signature field |
The endpoints object MUST include at minimum:¶
| Key | Description |
|---|---|
agents
|
Base path for agent endpoints (e.g., /v1/agents) |
crl
|
CRL endpoint path (e.g., /v1/crl) |
revocations
|
Revocation submission path (e.g., /v1/revocations) |
Self-signature verification: Relying Parties MUST verify the signature field against the public_key
in the same document. This ensures the well-known document has not been tampered with in transit. The signature is
computed over the RFC 8785 JCS canonical serialisation of the document with the signature field removed.¶
First-contact bootstrapping: On first contact with a Registry, Relying Parties MUST:¶
/.well-known/aip-registry over HTTPS.¶
public_key and registry_aid locally.¶
Key rotation for Registry keys is an administrative operation that requires out-of-band communication to Relying Parties. A future AIP will define the Registry key rotation protocol.¶
Validators bootstrapping trust in a Registry MUST fetch and pin the Registry's public key from this endpoint before processing any Step Execution Tokens or CRL documents signed by that Registry. Cached values MUST NOT be used beyond 300 seconds without revalidation.¶
6.3.5. Single-Instance Constraint¶
Each Registry deployment MUST have exactly one active Registry AID. Provisioning a second Registry AID within the same deployment MUST be treated as a fatal configuration error. Horizontal scaling and high-availability deployments MUST share a single Registry AID and keypair (stored in a shared secrets manager).¶
6.3.6. Registry Key Rotation (Interim Procedure)¶
Full in-band Registry key rotation is deferred to a future minor version. Until that protocol is defined, the following interim procedure applies to operators who must rotate the Registry key (e.g., in response to suspected key compromise).¶
/.well-known/aip-registry document whose public_key does not match the pinned key MUST treat this as a potential MITM attack. They MUST NOT use the new key automatically. They MUST halt Registry-dependent operations and require out-of-band verification that the key change is legitimate before re-pinning.¶
Validators bootstrapping trust in a Registry MUST fetch and pin the Registry's public key from this endpoint before processing any Step Execution Tokens or CRL documents signed by that Registry. Cached values MUST NOT be used beyond 300 seconds without revalidation.¶
An AIP Credential Token is transmitted as an HTTP Authorization header:¶
EXAMPLE (informative):¶
Authorization: AIP <token> X-AIP-Version: 0.3¶
For interactions requiring Proof-of-Possession:¶
EXAMPLE (informative):¶
Authorization: AIP <token> DPoP: <dpop-proof> X-AIP-Version: 0.3¶
The X-AIP-Version HTTP header MUST carry the full protocol
version string, identical to the aip_version JWT claim
(Section 4.2.3). For this specification, the value MUST be
"0.3". Implementations MUST reject requests where the
X-AIP-Version header carries a version value that does not
match a supported aip_version.¶
Implementations MUST enforce the following maximum token lifetimes:¶
| Scope Category | Maximum TTL |
|---|---|
All standard scopes (email.*, calendar.*, web.*, etc.) |
3600 seconds (1 hour) |
transactions.* or communicate.*
|
300 seconds (5 minutes) |
filesystem.execute
|
300 seconds (5 minutes) |
spawn_agents.create or spawn_agents.manage
|
300 seconds (5 minutes) |
When a token contains scopes from multiple categories, the most
restrictive TTL applies. Zero-duration and negative-duration tokens
(where exp <= iat) MUST be rejected.¶
REMINDER: A token's Tier is determined by its highest-risk scope (Section 3). A token containing one Tier 2 scope and any number of Tier 1 scopes is a Tier 2 token in its entirety. Implementations MUST NOT derive Tier from the majority of scopes or from the first scope in the array.¶
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 (aip_chain) remain valid. There is no refresh token
in AIP - the agent's signing key IS the refresh credential.¶
An agent self-refresh involves: issuing a new Credential Token with a
new jti, a fresh iat, and a new exp within TTL limits.
The aip_chain content remains unchanged until the Principal Tokens
within it expire.¶
Agents MUST NOT re-use the same jti when issuing a fresh token.
Each issued Credential Token MUST have a unique jti.¶
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:¶
| Token TTL Category | Begin refresh when remaining TTL <= |
|---|---|
| Standard (3600s) | 300 seconds (5 minutes) |
| Sensitive (300s) | 30 seconds |
Implementations MUST NOT wait for a token rejection
(token_expired) before refreshing. Waiting for rejection
creates a gap in execution continuity and may leave in-progress Tier 2
operations without valid authority.¶
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 iat to exp window.¶
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.¶
When a Principal Token in the aip_chain expires, the Credential
Token becomes structurally invalid at validation Step 8h regardless
of the Credential Token's own exp. This is because the delegation
authority itself has lapsed.¶
When a delegation chain expires:¶
aip_chain (even if the agent's own exp is still in the future).¶
Relying Parties that receive a token where Step 8h fails MUST return
chain_token_expired. Agents receiving this error MUST treat it as
delegation_chain_refresh_required - they must re-establish their
delegation rather than merely refreshing their Credential Token.¶
Anticipatory chain refresh: Agents SHOULD monitor the
expires_at timestamps of all Principal Tokens in their
aip_chain. 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.¶
Approval Envelopes (Section 4.6) are specifically designed to decouple human approval timing from token TTL constraints. The following rules govern their interaction:¶
approval_window_expires_at is independent of any Credential Token TTL. Envelopes may remain in pending_approval status for hours while normal TTLs of 300s or 3600s apply only at execution time.¶
An AIP agent MAY exchange its Credential Token for a scoped access token targeting a specific MCP server or OAuth-protected resource. The exchange is performed at the Registry's token endpoint (Section 15.5).¶
The agent sends an RFC 8693 token exchange request:¶
POST /v1/oauth/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded DPoP: <DPoP proof JWT> grant_type=urn:ietf:params:oauth:grant-type:token-exchange &subject_token=<AIP Credential Token> &subject_token_type=urn:ietf:params:oauth:token-type:jwt &resource=https://mcp-server.example.com/ &scope=urn:aip:scope:email.read urn:aip:scope:calendar.read¶
Normative requirements:¶
grant_type MUST be urn:ietf:params:oauth:grant-type:token-exchange.¶
subject_token MUST be a valid AIP Credential Token.¶
subject_token_type MUST be urn:ietf:params:oauth:token-type:jwt.¶
resource MUST be present per RFC 8707 [RFC8707], identifying the target resource server.¶
scope MUST use AIP scope URIs from the urn:aip:scope: namespace (Section 15.6). The requested scopes MUST be a subset of the Credential Token's aip_scope.¶
The Registry (as OAuth AS) MUST:¶
subject_token using the full validation algorithm (Section 7.3).¶
scope is a subset of the subject_token's aip_scope (attenuation only, never expansion).¶
resource is a registered MCP server or resource. Fail: invalid_target.¶
Issue an access token with:¶
EXAMPLE (informative):¶
{
"access_token": "<JWT>",
"token_type": "DPoP",
"expires_in": 300,
"scope": "urn:aip:scope:email.read urn:aip:scope:calendar.read",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token"
}
¶
The Credential Token Validation Algorithm is the normative heart of AIP. A Relying Party MUST execute the following steps in the order presented. Validation is deterministic: two independent implementations executing the same steps on the same token with the same Registry state MUST reach the same outcome.¶
The Relying Party MUST reject the token at the first step that fails. Steps marked 6a, 6b, 9e, and 10a are conditional sub-steps introduced in v0.3; they are part of the numbered step at which they appear and do not change the base step numbering.¶
Parse the Authorization header value as a JWT per [RFC7519]. If parsing fails or the token is malformed,
reject with invalid_token.¶
Verify the JWT header contains:¶
typ: MUST be "AIP+JWT"¶
alg: MUST be one of the approved algorithms per Section
17.2 (EdDSA REQUIRED; ES256 and RS256 optional per Section
16.1)¶
kid: MUST be present and MUST be a DID URL in the form
did:aip:<namespace>:<32-hex>#key-<n>¶
If any check fails, reject with invalid_token.¶
Extract the kid 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 kid. The Registry
MUST return the public key material along with its validity period
(valid_from and valid_until timestamps).¶
If the kid is not found or the key is not valid at the time
of the JWT's iat claim, reject with unknown_aid.¶
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 invalid_token.¶
Validate the following claims from the decoded JWT payload:¶
iat MUST NOT be in the future. Allow a 30-second clock
skew tolerance. If iat is in the future (beyond skew), reject
with invalid_token.¶
exp MUST be strictly greater than iat (zero-duration
tokens are not permitted). If exp <= iat, reject with
invalid_token.¶
exp MUST be in the future (current time must be <
exp). If exp is in the past, reject with
token_expired.¶
aud claim MUST match the Relying Party's identifier.
If aud is a string, it MUST match exactly. If aud
is an array, the Relying Party's identifier MUST be present in the
array. If no match, reject with invalid_token.¶
jti MUST be a UUID v4 in canonical form (lowercase,
hyphenated). The Relying Party MUST maintain a replay cache keyed by
(iss, jti) for each token's validity window. If (iss,
jti) has been seen before, reject with token_replayed.
After the token expires, the replay cache entry MAY be discarded.¶
aip_version MUST be present and MUST be "0.3" for
tokens conforming to this specification. If aip_version is
absent or unrecognised, reject with invalid_token and
include the received aip_version in the error description
to aid debugging.¶
Verify that the token's lifetime does not exceed the maximum permitted by its scopes. Compute lifetime = exp - iat (in seconds).
Compare against the TTL limits defined in Section 7.2:¶
Standard scopes (email.*, calendar.*, web.*): max 3600 seconds
Sensitive scopes (transactions.*, communicate.*,
filesystem.execute,
spawn_agents.create/manage): max 300 seconds
When a token contains scopes from multiple
categories, the most restrictive limit applies.
¶
If lifetime exceeds the limit, reject with invalid_token.¶
REMINDER: A token's Tier is determined by its highest-risk scope (Section 3). A token containing one Tier 2 scope and nine Tier 1 scopes is a Tier 2 token in its entirety. Do not derive Tier from the majority or first scope.¶
This step is REQUIRED for Tier 2 operations and RECOMMENDED for Tier 1. It verifies that the Registry from which the Relying Party has been fetching data matches the Registry declared by the principal's DID Document.¶
principal_id from aip_chain[0].iss (the
root principal's DID). This is the authorising human or
organisational principal.¶
principal_id using the DID method's own resolution
mechanism (e.g., did:web resolution per [W3C-DID],
did:key per [W3C-DID]). The resolution MUST be
independent of any agent-provided data. Use the DID method's
canonical resolver.¶
service array. Locate
an entry with type: "AIPRegistry".¶
serviceEndpoint URI from that service entry.
This is the authoritative Registry URI for this principal.¶
serviceEndpoint URI. Perform origin
comparison per [RFC6454] (scheme + host + port must
match).¶
aip_registry is present in the Credential Token,
verify it matches the DID-Document-declared Registry endpoint. If
mismatch, reject with registry_untrusted.¶
AIPRegistry
service entry and the token contains Tier 2 scopes, reject with
registry_untrusted.¶
For Tier 1 operations: this step is RECOMMENDED. Registry trust anchoring prevents MitM Registry substitution but adds latency (additional DID resolution). Tier 1 operators MAY skip this step if they accept the risk that the Registry might be MitM'd with a loss of up to 15-minute revocation staleness.¶
If DID resolution fails and the token contains Tier 2 scopes, reject with registry_unavailable.¶
This step applies only if aip_engagement_id is present in the
Credential Token payload.¶
GET /v1/engagements/{aip_engagement_id}.¶
status field. If "active", continue.
If "completed" or "terminated", reject with engagement_terminated.
If "suspended", reject with engagement_suspended.¶
sub (agent AID) appears in the
Engagement's participants array as an active participant.
If sub is not in the participants array or has been marked
as removed, reject with engagement_participant_removed.¶
approval_gates with pending
gates that guard the current operation, verify the token's operation
is not gated (or that the gate has been approved). If a required
gate is still pending, reject with engagement_gate_pending.¶
Query the Registry for revocation status of the agent identified by
iss (extracted from the kid). The revocation check
method depends on the token's Tier:¶
full_revoke, scope_revoke, or
principal_revoke), reject with agent_revoked.¶
GET /v1/agents/{iss}/revocation
and verify no active revocation applies. If the agent is revoked,
reject with agent_revoked. If the Registry is unreachable,
reject with registry_unavailable.¶
Validate the Principal Token delegation chain in aip_chain.
This array contains one or more compact-serialised JWTs, each
conforming to the Principal Token schema (Section 4.2.4).¶
For each Principal Token at index i (from 0 to
n-1 where n is the length of aip_chain):¶
i MUST be a valid, well-formed JWT
conforming to the Principal Token schema (Section 4.2.4). If
parsing or schema validation fails, reject with
delegation_chain_invalid.¶
delegation_depth claim in token i MUST equal
exactly i. (The array is 0-indexed; aip_chain[0]
has delegation_depth: 0, etc.) If mismatch, reject with
invalid_delegation_depth.¶
delegation_depth of token i MUST NOT exceed
the max_delegation_depth value declared in token 0 (the
root token). If max_delegation_depth is absent from token
0, the default is 3. If i exceeds this limit, reject with
invalid_delegation_depth.¶
i, extract the iss claim:
For i = 0, iss MUST equal principal.id. Verify signature.
For i > 0, iss MUST equal delegated_by. Verify signature.
If verification fails, reject with delegation_chain_invalid.¶
i > 0, verify that
delegated_by[i] equals sub[i-1] (the sub
of the previous token). This ensures the chain is continuous: each
agent is delegated by the previous agent in the chain. If mismatch,
reject with delegation_chain_invalid.¶
sub AID in the chain (at all indices), verify it
is not revoked using the same Tier-specific revocation check as Step
7. If any agent in the chain is revoked, reject with
agent_revoked.¶
i and j
with i != j, reject with delegation_chain_invalid.¶
i, verify expires_at > issued_at
and expires_at is in the future. If either check fails, reject with
chain_token_expired.¶
principal.id 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 principal.id, reject with
delegation_chain_invalid.¶
principal.id from aip_chain[0] MUST NOT begin
with did:aip:. Principals are humans or organisations, never
agents. If principal.id uses the did:aip method,
reject with delegation_chain_invalid.¶
After validating all elements in the chain, verify that iss
(the issuer of the Credential Token, from the JWT header kid)
MUST equal aip_chain[n-1].sub (the sub 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 delegation_chain_invalid.¶
For single-element chains (n = 1), verify that iss MUST
equal sub. This confirms a non-delegated token issued
directly by the principal's agent. If mismatch, reject with
delegation_chain_invalid.¶
Verify that the agent's requested scopes are permitted by its Capability Manifest.¶
iss from the Registry.
If unavailable, reject with manifest_invalid.¶
manifest_invalid.¶
expires_at is in the future. If expired, reject with manifest_expired.¶
For all agents: verify each scope in aip_scope is present in the Capability Manifest.¶
If a Capability Overlay exists, verify scope constraints permit the requested operation.¶
REMINDER: A token's Tier is determined by its highest-risk scope (Section 3). A token containing one Tier 2 scope is a Tier 2 token.¶
If aip_scope contains any of the following scopes, DPoP
(Demonstration of Proof-of-Possession) MUST be verified:¶
transactions or any scope with prefix transactions.¶
communicate.whatsapp, communicate.telegram,
communicate.sms, communicate.voice¶
filesystem.execute¶
spawn_agents.create, spawn_agents.manage¶
If any of these scopes is present, the HTTP request MUST include a
DPoP header containing a Demonstration of Proof-of-Possession
proof JWT per [RFC9449]. Verify:¶
htm (HTTP method) claim matches the HTTP method of
the request.¶
htu (HTTP URI) claim matches the request URI.¶
jti has not been seen before (DPoP-specific replay
cache, separate from Credential Token jti cache, keyed by
(kid, jti)).¶
jwk claim matches the agent's key
material (used to issue the Credential Token).¶
If DPoP validation fails at any step, reject with
dpop_proof_required (if proof is missing) or
invalid_token (if proof is malformed or invalid).¶
This step applies only if both aip_approval_id and
aip_approval_step are present in the Credential Token (the
Step Execution Token case, Section 4.6.8).¶
The Relying Party MUST call the Registry endpoint GET
/v1/approvals/{aip_approval_id}/steps/{n} where {n} is
the value of aip_approval_step verbatim (an integer >= 1, per
Section 4.6.4; MUST NOT be decremented or adjusted). Verify:¶
status field MUST
be "claimed" (not "pending", "completed",
etc.). If status is "pending", the step has not been claimed
and cannot execute. Reject with approval_step_invalid.¶
actor field MUST
equal the Credential Token's sub (the agent AID). Reject if
mismatch with approval_step_invalid.¶
relying_party_uri MUST match the host of the current HTTP
request (origin comparison per [RFC6454]: scheme +
host + port). Reject if mismatch with approval_step_invalid.¶
action_hash field. If mismatch, reject with
approval_step_invalid. This ensures the approving principal
authorised the exact action being executed.¶
This step applies only to Tier 3 enterprise deployments, which are declared and documented in the Registry's /.well-known/aip-registry
endpoint (Section 15.1).¶
For Tier 3 operations:¶
iss). If mismatch or certificate is
absent, reject with invalid_token.¶
agent_revoked.¶
If either check fails, reject with the appropriate error code
(invalid_token or agent_revoked).¶
If all preceding steps pass without rejection, the Relying Party MUST accept the token and grant the requested access.¶
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 aip_chain array
of the Credential Token, with each Principal Token in the chain
representing one delegation hop from principal to agent.¶
Every Credential Token MUST include a verifiable principal chain
linking the acting agent to its root principal via the
aip_chain array. The root principal MUST be a human or
organizational entity identified by a W3C Decentralized Identifier
(DID) that does NOT use the did:aip method. For example:
did:web, did:key, or proprietary DID methods are
acceptable.¶
The maximum delegation depth is a hard constraint of 10 levels. This means an agent may not delegate to a sub-agent if doing so would create a chain longer than 10 Principal Tokens (0-indexed from 0 to 9, or 1-indexed as depths 1 to 10).¶
The default value of max_delegation_depth MUST NOT exceed 3.
When a Principal Token does not explicitly set
max_delegation_depth, 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
max_delegation_depth to a value between 3 and 10.¶
Scope inheritance is the mechanism by which child agents are constrained to operate within the bounds of their parent's authorization. Five core rules (D-1 through D-5) govern this relationship; they are defined in Section 4.4 of the AIP specification.¶
Scope Inheritance Rule: For each scope s
granted to a child agent, s MUST be present in the parent's
Capability Manifest AND all constraint values for s in the
child's manifest MUST be no more permissive (<=) than the corresponding
values in the parent's manifest.¶
For numeric limits (e.g., max_single_transaction,
max_daily_value), the child value MUST be <= the parent value.
For boolean enables or enum values, the child constraint MUST NOT be
more permissive than the parent (e.g., child cannot set a flag
true if parent sets it false).¶
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.¶
Relying Parties MUST independently verify this rule during validation at Step 9c of the Credential Token validation algorithm (see Section 9.11). This ensures that even if a Registry incorrectly permits a violating delegation, Relying Parties will catch it and reject the token.¶
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.¶
Ancestor Manifest Fetch Limits: Implementations MUST
NOT fetch more ancestor manifests than the max_delegation_depth
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.¶
Ancestor Manifest Caching for Tier 1: For Tier 1 operations (low-risk scopes with bounded-staleness threat model), ancestor manifests MAY be cached for a maximum of 60 seconds. This cache is per-agent and per-manifest, and MUST respect the 60-second TTL. After 60 seconds, a fresh fetch is required.¶
No-Cache Requirement for Tier 2: For Tier 2 operations (high-risk scopes with real-time threat model), the no-cache requirement in the Credential Token validation algorithm (see Section 9.11) 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.¶
Unavailable Manifests: 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 manifest_invalid.
Partial delegation chains are not acceptable; either all manifests in
the chain are available and valid, or the token is rejected.¶
The AIP-GRANT ceremony provides a standardised protocol for principals to authorise AI agents. AIP-GRANT is analogous to the OAuth 2.0 Authorization Code Flow, adapted for the agent identity use case. Two independent implementations following this section MUST produce interoperable grant interactions.¶
The AIP-GRANT ceremony involves three actors:¶
The basic flow:¶
Agent Deployer Principal Wallet AIP Registry
| | |
|-- GrantRequest -----> | |
| (capabilities, | |
| purpose, nonce) | |
| [Display consent UI] |
| [Human reviews & signs] |
| | |
|<-- GrantResponse --- | |
| (signed PrincipalToken) |
| | |
|-- Registration Envelope ---------------------> |
| (identity + manifest + |
| principal_token) |
|<-- AID --------------------------------------- |
¶
The GrantRequest is a JSON object constructed by the Agent Deployer. It MUST be signed by the deployer's Ed25519 key when transmitted over the Web Redirect or QR Code bindings. The nonce MUST be cryptographically random and MUST contain at least 128 bits of entropy.¶
| Field | Type | Required | Constraints |
|---|---|---|---|
| grant_request_id | string | REQUIRED | pattern: gr:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12} |
| aip_version | string | REQUIRED | MUST be "0.3" |
| agent_name | string | REQUIRED | maxLength: 64; displayed in consent UI |
| agent_type | string | REQUIRED | registered namespace value |
| model.provider | string | REQUIRED | displayed in consent UI |
| model.model_id | string | REQUIRED | displayed in consent UI |
| requested_capabilities | object | REQUIRED | Capability Manifest capabilities sub-schema |
| purpose | string | REQUIRED | minLength: 1; maxLength: 512 |
| delegation_valid_for_seconds | integer | REQUIRED | min: 300; max: 31536000 |
| nonce | string | REQUIRED | minLength: 22; cryptographically random |
| request_expires_at | string | REQUIRED | ISO 8601 UTC |
| callback_uri | string | CONDITIONAL | REQUIRED for Web Redirect; MUST use HTTPS |
The deployer MUST generate the agent's AID before constructing the GrantRequest so that the resulting Principal Token correctly names the agent's AID in its sub field.¶
The Principal Wallet MUST implement the following requirements before signing any grant.¶
Nonce and expiry checks: 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.¶
Mandatory display elements: The consent UI MUST display ALL of the following before presenting the sign/decline choice:¶
Canonical human-readable capability strings:¶
| Scope | Display String |
|---|---|
| email.read | Read your email messages and metadata |
| email.send | Send email on your behalf |
| email.delete | Permanently delete your email messages - this cannot be undone |
| calendar.read | Read your calendar events |
| filesystem.execute | Execute scripts and commands on your system - HIGH RISK |
| transactions | Make financial transactions up to specified limits |
| spawn_agents.create | Create child AI agents on your behalf |
The GrantResponse is constructed and signed by the Principal Wallet after the principal completes the signing ceremony.¶
| Field | Required | Constraints |
|---|---|---|
| grant_request_id | REQUIRED | MUST match original request |
| nonce | REQUIRED | MUST match original nonce |
| status | REQUIRED | enum: "approved", "rejected", "partial" |
| principal_id | REQUIRED | W3C DID of signing principal |
| principal_token | CONDITIONAL | REQUIRED when status is "approved" or "partial" |
Deployer validation of GrantResponse: Upon receipt the deployer MUST:¶
Implementations MUST support the Web Redirect Flow.¶
Web Redirect Flow:¶
https://wallet.example.com/aip-grant ?request=<base64url-signed-GrantRequest> &aip_version=0.3¶
After the principal signs, the Principal Wallet delivers the GrantResponse:¶
POST <callback_uri>
Content-Type: application/json
{GrantResponse JSON}
¶
The callback_uri MUST be pre-registered by the deployer. Principal Wallets MUST NOT deliver GrantResponses to unregistered URIs.¶
When a parent agent delegates to a child agent, the parent acts as the Principal Wallet. No human consent UI is required.¶
The parent agent MUST:¶
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.¶
| Code | Description |
|---|---|
| grant_request_expired | request_expires_at has passed |
| grant_request_replayed | grant_request_id seen before |
| grant_request_invalid | GrantRequest malformed or signature failed |
| grant_rejected_by_principal | Principal declined the grant |
| grant_nonce_mismatch | GrantResponse nonce does not match |
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.¶
Actors: Agent Deployer, AIP Registry, Principal (via Registry-hosted or redirected wallet consent UI).¶
Deployer Registry Principal | | | |-- POST /v1/grants ->| | | (GrantRequest + | | | callback_uri) | | |<-- 201 grant_id + --| | | wallet_redirect | | | | | | [redirect principal to wallet URI] | | |<-- authenticates ---| | |<-- approve/decline -| | | | | [Registry signs Principal Token] | | | | |<-- POST callback ---| | | (GrantResponse) | | | | | |-- GET /v1/grants/id>| | |<-- GrantResponse ---| |¶
Step-by-step definition:¶
GET /v1/grants/{grant_id} authorisation: Only the deployer whose deployer_did appears in the original GrantRequest MUST be permitted to retrieve the GrantResponse.¶
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.¶
The Cascading Approval Problem: 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.¶
The Token-Expiry-While-Pending Problem: A Credential Token (TTL = 300s for Tier 2) may expire while approval is pending. Approval Envelopes decouple the approval phase from execution: the envelope waits in pending_approval state without requiring a valid token, and step-claim tokens are issued at execution time.¶
An Approval Envelope is submitted by the orchestrating agent to POST /v1/approvals and approved by the principal through their Principal Wallet.¶
| Field | Required | Constraints |
|---|---|---|
| approval_id | REQUIRED | pattern: apr:[uuid] |
| created_by | REQUIRED | AID of orchestrating agent |
| principal_id | REQUIRED | W3C DID; MUST NOT be did:aip |
| description | REQUIRED | minLength: 1; maxLength: 512 |
| approval_window_expires_at | REQUIRED | ISO 8601 UTC; RECOMMENDED max: 72 hours |
| steps | REQUIRED | minItems: 1; maxItems: 20 |
| compensation_steps | OPTIONAL | Compensation steps for SAGA rollback |
| total_value | OPTIONAL | Total financial value; MUST equal sum of step values |
| currency | OPTIONAL | ISO 4217; pattern: three uppercase ASCII letters |
| creator_signature | REQUIRED | base64url EdDSA signature |
| principal_signature | CONDITIONAL | REQUIRED once status is approved |
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.¶
Each element in the steps array represents one atomic action at one Relying Party.¶
| Field | Required | Constraints |
|---|---|---|
| step_index | REQUIRED | 1-based; unique within envelope |
| actor | REQUIRED | AID executing this step |
| relying_party_uri | REQUIRED | URI of the Relying Party |
| action_type | REQUIRED | Application-defined; maxLength: 128 |
| action_hash | REQUIRED | sha256:64-hex; see Section 4.6.7 |
| description | REQUIRED | minLength: 1; maxLength: 256 |
| required | REQUIRED | boolean; false = optional step |
| triggered_by | REQUIRED | null or step_index; 0 forbidden |
| value | OPTIONAL | Financial value; minimum: 0 |
| status | READ-ONLY | pending | claimed | completed | failed | compensated | skipped |
The triggered_by field implements the SAGA DAG. A step with triggered_by: null is a root step. A step with triggered_by: N may only be claimed after step N reaches completed status. Circular dependencies MUST be rejected.¶
Multiple steps MAY share the same triggered_by value, enabling parallel execution paths.¶
Compensation steps define SAGA rollback actions pre-authorised by the principal. If a forward step fails, compensation actions execute without requiring new human approval.¶
| Field | Required | Constraints |
|---|---|---|
| compensation_index | REQUIRED | 0-based index |
| actor | REQUIRED | AID executing compensation |
| relying_party_uri | REQUIRED | URI of the Relying Party |
| action_type | REQUIRED | Application-defined rollback |
| action_hash | REQUIRED | sha256:64-hex |
| description | REQUIRED | Plain-language rollback description |
The Registry maintains lifecycle state with valid transitions:¶
pending_approval --> approved --> executing --> completed
| | |
v v v
rejected expired compensating --> compensated
|
v
failed
¶
Transition rules:¶
Terminal states (no further transitions): completed, compensated, failed, rejected, expired.¶
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.¶
The action_hash is computed as:¶
action_hash = "sha256:" + LCHEX( SHA-256( JCS( action_parameters ) ) )¶
Where action_parameters contains:¶
Implementations MUST use an RFC8785-conformant library. Financial amounts MUST be represented as JSON numbers (not strings).¶
To execute a step, an agent follows this protocol:¶
Step 1 - Claim: The agent calls POST /v1/approvals/{id}/steps/{n}/claim with a valid Credential Token and action_parameters.¶
The Registry MUST atomically verify all of the following:¶
If all checks pass, the Registry returns a Step Execution Token:¶
{
"iss": "<Registry AID>",
"sub": "<actor AID>",
"aud": "<relying_party_uri>",
"iat": <now>,
"exp": <now + 300>,
"jti": "<UUID>",
"aip_version": "0.3",
"aip_scope": "<scope array>",
"aip_chain": "<principal chain array>",
"aip_approval_id": "<approval_id>",
"aip_approval_step": <step_index>
}
¶
Step 2 - Execute: The agent presents the Step Execution Token to the Relying Party.¶
Step 3 - Complete or Fail: After execution, the agent MUST call /complete or /fail within step_claim_timeout_seconds (RECOMMENDED: 600 seconds).¶
When any required: true step fails after one or more earlier steps completed, the Registry MUST:¶
Because the principal pre-approved all compensation steps, no additional human interaction is required. This is the core SAGA property.¶
If a compensation step itself fails, the envelope transitions to failed (terminal). This requires manual intervention outside the AIP protocol.¶
The Registry MUST reject an Approval Envelope at submission time if any of the following are true:¶
The Registry MUST also reject envelopes from agents whose Capability Manifest does not include spawn_agents.create or minimum transactions capability.¶
The principal.id field MUST be byte-for-byte identical in
every Principal Token in the aip_chain array. Relying
Parties MUST verify this. An intermediate agent MUST NOT change it,
substitute its own AID, or modify the original principal DID in any
way.¶
Every element in aip_chain is a compact-serialised JWT whose
payload conforms to the Principal Token schema (Section 4.2.4).
Elements are ordered root-to-leaf. The maximum aip_chain
length is 11. Implementations MUST reject tokens with
aip_chain length exceeding 11.¶
Cryptographic non-repudiation: Every Credential Token carries a
delegation chain in which each link is signed by the delegating
party's private key. Because principal.id is byte-identical
across all chain elements and is bound to the signing key, every
agent action is cryptographically attributable to the human or
organisational principal that authorised it. This satisfies the
non-repudiation requirement of [SP-800-63-4] Section
11.¶
Any Relying Party or agent MAY submit a signed Endorsement Object to the Registry after a completed interaction. The schema is in Section 4.2.7.¶
The Registry MUST verify every submitted Endorsement Object signature. Only success and partial outcomes increment endorsement_count. failure increments incident_count.¶
Completed Approval Envelope steps SHOULD generate Endorsement Objects. When an Approval Envelope reaches completed status, the orchestrator SHOULD submit an Endorsement Object for each agent that executed a step successfully.¶
The Registry MUST expose reputation data for every registered AID at GET /v1/agents/{aid}/reputation. Required fields: registration_date, task_count, successful_task_count, endorsement_count, incident_count, revocation_history, last_active.¶
The Registry MAY expose a reference advisory score labelled advisory_only: true. Relying Parties MUST NOT treat it as normative. AIP standardises reputation inputs, not the scoring formula.¶
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 full_revoke or principal_revoke status MUST NOT be weighted.¶
Revocation provides the kill switch for agent identity. When an agent is revoked, all its Credential Tokens become invalid and the agent can no longer act on behalf of any principal.¶
Revocation is performed by submitting a signed Revocation Object to POST /v1/revocations.¶
| Type | Description |
|---|---|
| full_revoke | Permanently revokes the AID. |
| scope_revoke | Removes specific scopes from Capability Manifest. Does NOT invalidate outstanding tokens. |
| delegation_revoke | Invalidates delegation chains rooted at target. Child agents lose authority. |
| principal_revoke | Issued by root principal. Equivalent to full_revoke plus child propagation. |
The Registry MUST expose a CRL at GET /v1/crl. The CRL MUST be updated within 15 minutes of a new Revocation Object being accepted. The CRL endpoint MUST be served from a CDN or distributed infrastructure.¶
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.¶
Tier 1 - Bounded-staleness: TTL is 3600 seconds. Validate against CRL at issuance time. CRL MUST be refreshed every 15 minutes.¶
Tier 2 - Real-time revocation: For scopes: transactions.*, communicate.*, filesystem.execute, spawn_agents.create, spawn_agents.manage. TTL is 300 seconds. Real-time Registry check on EVERY request. MUST NOT cache revocation status. DPoP MUST be verified. If Registry unreachable: MUST deny and return registry_unavailable.¶
Tier 3 - Enterprise: MUST use mTLS. MUST support OCSP per RFC 6960. Tier 3 supplements, not replaces, Tier 2.¶
Child Agent Propagation: When the Registry processes a Revocation Object with propagate_to_children: true, the Registry MUST recursively revoke descendants within 15 seconds.¶
Replica Registries MUST synchronise within 45 seconds. Combined end-to-end propagation MUST NOT exceed 60 seconds.¶
Approval Envelope interaction: 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.¶
RPNP is an OPTIONAL Registry capability for real-time revocation event delivery.¶
When RPNP is implemented:¶
For subscribing Relying Parties, the effective revocation window is the RPNP delivery latency (at most 5 seconds) rather than the CRL refresh interval. RPNP does not replace CRL; it supplements it.¶
A Relying Party subscribes by calling POST /v1/subscriptions:¶
| Field | Required | Constraints |
|---|---|---|
| subscriber_did | REQUIRED | MUST be did:web or did:aip |
| event_types | REQUIRED | full_revoke, scope_revoke, delegation_revoke, etc. |
| scope_filter | REQUIRED | aid, principal, or all |
| targets | CONDITIONAL | REQUIRED when scope_filter is aid or principal |
| webhook_uri | REQUIRED | HTTPS URI |
| hmac_secret_hash | REQUIRED | SHA-256 hash of shared secret |
| subscription_expires_at | REQUIRED | ISO 8601 UTC; max 90 days |
The subscription request MUST be authenticated via DPoP proof. Un-signed requests MUST be rejected with subscription_auth_required.¶
Push events are delivered as HTTP POST to the subscriber's webhook_uri. The body is a compact-serialised JWT signed by the Registry's administrative key.¶
| Header Field | Value |
|---|---|
| typ | AIP-RPNP+JWT |
| alg | EdDSA |
| kid | Registry AID key ID |
| Payload Field | Description |
|---|---|
| iss | Registry AID |
| sub | Affected AID or engagement ID |
| iat | Unix timestamp |
| jti | UUID v4; unique event ID |
| event_type | One of subscribed types |
| event_data | Event-specific data |
The request MUST include an X-AIP-Signature header containing HMAC-SHA256(shared_secret, body). The subscriber MUST verify both the JWT signature and the HMAC.¶
An AID has exactly two lifecycle states: active and
revoked. AIP does not define an inactive status;
the Dead Man's Switch mechanism (Section 17.8) uses
full_revoke.¶
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 exp.¶
Ephemeral agents MUST have a non-null task_id. They MUST be
explicitly revoked on task completion. The Registry SHOULD
auto-revoke ephemeral agents when their stored expires_at
passes.¶
A conformant AIP Registry MUST implement the HTTP endpoints defined in this section.¶
| Method | Path | Description |
|---|---|---|
| POST | /v1/agents | Register a new AID (Registration Envelope) |
| GET | /v1/agents/{aid} | Retrieve Agent Identity or DID Document |
| PUT | /v1/agents/{aid} | Key rotation only |
| POST | /v1/revocations | Submit a Revocation Object |
| GET | /v1/crl | Retrieve Certificate Revocation List |
| GET | /.well-known/aip-registry | Retrieve Registry configuration |
The AID MUST be URL-safe. The did:aip: prefix and all colons MUST be percent-encoded in URL paths:¶
GET /v1/agents/did:aip%3Anamespace%3A32hexcharacters¶
All responses MUST use Content-Type: application/json. Timestamps MUST be in ISO 8601 format.¶
| Method | Path | Description |
|---|---|---|
| POST | /v1/approvals | Submit Approval Envelope |
| GET | /v1/approvals/{id} | Retrieve envelope |
| POST | /v1/approvals/{id}/steps/{n}/claim | Claim a step |
| POST | /v1/approvals/{id}/steps/{n}/complete | Mark step complete |
| POST | /v1/approvals/{id}/steps/{n}/fail | Mark step failed |
The Registry MAY implement an OAuth 2.1 Authorization Server for token exchange (Section 7.5). The token endpoint:¶
POST /v1/oauth/token Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:token-exchange &subject_token=<AIP Credential Token> &subject_token_type=urn:ietf:params:oauth:token-type:jwt &resource=<target URI> &scope=urn:aip:scope:email.read¶
Scope URIs follow the URN namespace urn:aip:scope:*:¶
urn:aip:scope:email.read urn:aip:scope:email.write urn:aip:scope:email.send urn:aip:scope:calendar.read urn:aip:scope:transactions urn:aip:scope:filesystem.execute urn:aip:scope:spawn_agents.create¶
| Method | Path | Description |
|---|---|---|
| POST | /v1/subscriptions | Create subscription |
| GET | /v1/subscriptions/{id} | Get subscription |
| DELETE | /v1/subscriptions/{id} | Delete subscription |
EXAMPLE (informative):¶
{
"error": "<error_code>",
"error_description": "<human-readable description>",
"error_uri": "https://provai.dev/errors/<error_code>"
}
¶
Implementations MUST use exact string values for `error`.¶
Implementations MUST NOT return HTTP 200 for error conditions.¶
invalid_tokenHTTP 401. Token malformed, invalid signature, or invalid claims.¶
token_expiredHTTP 401. Token exp is in the past.¶
token_replayedHTTP 401. Token jti seen before within validity window.¶
dpop_proof_requiredHTTP 401. DPoP proof absent or invalid.¶
delegation_chain_refresh_requiredHTTP 401. Principal Token in chain has expired; agent must re-establish delegation.¶
agent_revokedHTTP 403. The AID has been revoked.¶
insufficient_scopeHTTP 403. Operation not within granted scopes.¶
invalid_delegation_depthHTTP 403. delegation_depth mismatch or exceeds max_delegation_depth.¶
chain_token_expiredHTTP 403. Principal Token in aip_chain expired.¶
delegation_chain_invalidHTTP 403. Structural error in delegation chain.¶
manifest_invalidHTTP 403. Capability Manifest signature failed or unavailable.¶
manifest_expiredHTTP 403. Capability Manifest expires_at passed.¶
approval_envelope_invalidHTTP 400. Approval Envelope malformed, circular dependencies, or hash mismatch.¶
approval_envelope_expiredHTTP 403. approval_window_expires_at has passed.¶
approval_not_foundHTTP 404. Approval Envelope ID not found.¶
approval_step_prerequisites_unmetHTTP 403. triggered_by step not yet completed.¶
approval_step_already_claimedHTTP 409. Step already claimed by another actor.¶
approval_step_action_mismatchHTTP 403. Presented action_parameters hash does not match stored action_hash.¶
approval_step_invalidHTTP 403. Step Execution Token verification against Registry failed.¶
grant_request_expiredHTTP 400. AIP-GRANT request_expires_at passed.¶
grant_request_replayedHTTP 400. AIP-GRANT grant_request_id seen before.¶
grant_request_invalidHTTP 400. GrantRequest malformed or signature failed.¶
grant_rejected_by_principalHTTP 403. Principal declined the grant.¶
grant_nonce_mismatchHTTP 400. GrantResponse nonce does not match.¶
unknown_aidHTTP 404. AID not registered in any accessible Registry.¶
registry_unavailableHTTP 503. Registry could not be reached.¶
rate_limit_exceededHTTP 429. Rate limit for this operation exceeded; see Section 14.¶
revocation_staleHTTP 403. Tier 2 operation with cached revocation status.¶
dpop_requiredHTTP 401. Tier 2 operation without DPoP proof.¶
mtls_requiredHTTP 403. Tier 3 operation without mTLS.¶
invalid_scopeHTTP 400. Token contains retired bare spawn_agents scope.¶
principal_did_method_forbiddenHTTP 403. Principal uses did:key for Tier 2 scope.¶
identity_proofing_insufficientHTTP 403. G3 identity proofing below requested acr_values.¶
grant_not_foundHTTP 404. G1 grant_id not found or expired.¶
grant_deployer_mismatchHTTP 403. G1 grant_id does not match deployer.¶
pkce_requiredHTTP 400. G3 authorization request missing PKCE.¶
registry_untrustedHTTP 403. Registry does not match principal DID-Document-declared Registry.¶
overlay_exceeds_manifestHTTP 400. Overlay violates CO-1 attenuation rule.¶
overlay_issuer_invalidHTTP 400. Overlay issuer uses did:key.¶
overlay_version_conflictHTTP 409. Overlay version not strictly increasing.¶
overlay_signature_invalidHTTP 400. Overlay signature verification failure.¶
engagement_terminatedHTTP 403. Engagement has been terminated or completed.¶
engagement_suspendedHTTP 403. Engagement is currently suspended.¶
engagement_participant_removedHTTP 403. Agent removed from engagement.¶
engagement_gate_pendingHTTP 403. Required approval gate not yet approved.¶
engagement_not_foundHTTP 404. Engagement ID not found.¶
engagement_countersign_requiredHTTP 400. Missing required countersignature.¶
change_log_immutableHTTP 400. Attempt to modify change log entry.¶
change_log_sequence_invalidHTTP 400. Out-of-sequence change log append.¶
subscription_auth_requiredHTTP 401. RPNP subscription without DPoP.¶
subscription_scope_forbiddenHTTP 403. scope_filter: "all" rejected by Registry policy.¶
invalid_webhook_uriHTTP 400. Webhook URI not HTTPS.¶
subscription_limit_exceededHTTP 429. RPNP subscription limit reached.¶
invalid_targetHTTP 400. Token exchange resource not registered.¶
For `registry_unavailable` (503): SHOULD include `Retry-After` per [RFC9110].¶
For `rate_limit_exceeded` (429): MUST include `Retry-After` per [RFC6585] Section 4 and rate limit headers per Section 14.1.¶
For `delegation_chain_refresh_required` (401): the response SHOULD include an `error_description` indicating which delegation depth's Principal Token has expired, to help the agent identify which delegation level to renew.¶
This section describes the security considerations for AIP implementations, including the threat model, cryptographic requirements, and recommended mitigations.¶
AIP identifies the following threat categories:¶
| Operation | Algorithm | Specification | Status |
|---|---|---|---|
| Signing / Verification | Ed25519 (EdDSA) | [RFC8037] | MUST |
| Hashing | SHA-256 | [FIPS-180-4] | MUST |
| Key representation | JWK | [RFC7517] | MUST |
| Key exchange (future) | X25519 | [RFC7748] | MUST |
Optional suites: ES256 (ECDSA P-256) per [RFC7518] for WebAuthn compatibility; RS256 (RSA-PKCS1) per [RFC7518] for legacy enterprise only - MUST NOT be the sole supported algorithm; RSA keys MUST be at least 2048 bits.¶
Prohibited: none, HS256/384/512, RS512, MD5, SHA-1. The alg header MUST be explicitly specified.¶
DPoP is REQUIRED for transactions.*, communicate.*, and filesystem.execute scopes. DPoP proofs MUST use EdDSA.¶
DPoP Proof Header:¶
{
"typ": "dpop+jwt",
"alg": "EdDSA",
"jwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "<base64url public key>",
"kid": "<DID URL>"
}
}
¶
DPoP Proof Payload:¶
{
"jti": "<UUID v4>",
"htm": "<HTTP method, uppercase>",
"htu": "<scheme + host + path>",
"iat": "<Unix timestamp>",
"ath": "<BASE64URL(SHA-256(token)))>"
}
¶
Relying Party validation: Verify alg is EdDSA, verify jwk.kid matches token kid, verify htm/htu/iat, check jti replay, verify ath, verify signature.¶
Private keys MUST NOT be stored in plaintext on disk, transmitted in protocol messages, or included in logs. Private keys SHOULD be stored in HSM, secure enclave, or OS-level keychain.¶
Key rotation: Generate new keypair, increment version, include previous_key_signature signed by retiring key, submit to Registry, retain retiring key until outstanding tokens expire.¶
Key compromise response: Immediately revoke with reason: key_compromised, register new AID with new keypair, re-establish delegations.¶
All tokens MUST be transmitted over TLS 1.2 or higher (TLS 1.3 RECOMMENDED). MUST NOT transmit over unencrypted HTTP.¶
JTI replay cache: Keyed by (iss, jti); window at least max TTL for served scopes; shared cache for distributed deployments.¶
Audience validation: MUST validate aud claim. Mismatch returns invalid_token.¶
Default max_delegation_depth MUST NOT exceed 3. Hard cap is 10. Circular delegation MUST be detected and rejected.¶
Ephemeral agents MUST have non-null task_id. Registry SHOULD auto-revoke when expires_at passes.¶
All write operations MUST be authenticated. Revocation issued_by MUST be verified as in the target's delegation chain.¶
Registry read endpoints MUST target 99.9% uptime. CRL MUST be served from CDN.¶
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.¶
Every Revocation Object MUST be signed. Unsigned or invalidly signed objects MUST be rejected.¶
Dead Man's Switch (Optional): Registry MAY issue full_revoke for agents that fail to submit a signed heartbeat within configured window (RECOMMENDED: 24 hours).¶
Timing attack mitigation: Short sensitive-scope TTLs (max 300s) limit exploitation window.¶
Action hash integrity: The action_hash binds principal approval to specific action parameters. Registry MUST reject any claim where recomputed hash does not match stored action_hash.¶
Double-spend prevention: Atomic step-claim ensures each step claimed by exactly one actor.¶
Envelope replay prevention: approval_id is UUID v4 unique per envelope. Registries MUST reject duplicate approval_id values.¶
AIP is designed for zero-trust environments. No personally identifiable information (PII) is transmitted in Credential Tokens beyond the AID and delegation chain.¶
Registries MUST NOT log or retain Credential Token payloads beyond validation. Relying Parties MUST NOT store tokens after validation.¶
The principal_id in Principal Tokens enables attribution for compliance but does not inherently reveal principal identity to Relying Parties.¶
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.¶
When rate limiting is applied, the Registry MUST return HTTP 429 with the following headers:¶
| Header | Required | Description |
|---|---|---|
| `Retry-After` | MUST | Seconds until the client may retry, or a HTTP-date per [RFC9110] |
| `X-RateLimit-Limit` | SHOULD | The request limit for this window |
| `X-RateLimit-Remaining` | SHOULD | Remaining requests in this window |
| `X-RateLimit-Reset` | SHOULD | Unix timestamp when the window resets |
| `X-RateLimit-Policy` | MAY | Human-readable description of the applicable policy |
The response body MUST conform to the error response format (Section 13.1) with `error: "rate_limit_exceeded"` and a human-readable `error_description` identifying the rate-limited operation and the applicable window.¶
The Registry MUST implement separate rate limit buckets for each of the following operation categories. The limits below are RECOMMENDED minimums; Registry operators MAY enforce stricter limits based on observed traffic patterns and threat models.¶
(`POST /v1/agents`):¶
(`PUT /v1/agents/{aid}`):¶
(`POST /v1/revocations`):¶
(`GET /v1/agents/{aid}/public-key/{key-id}`, `GET /v1/agents/{aid}/revocation`):¶
(`GET /v1/crl`):¶
(`POST /v1/endorsements`):¶
(`POST /v1/approvals`, `POST /v1/approvals/{id}/steps/{n}/claim`):¶
Beyond rate limiting, the Registry MUST implement the following structural checks to prevent registration abuse:¶
The Registry MUST check AID uniqueness under a distributed lock or equivalent atomic mechanism. Two simultaneous registration requests for the same AID MUST result in exactly one succeeding and one receiving an appropriate error (Section 5.2 Check 4).¶
The Registry MUST verify that the `principal_token` in the Registration Envelope was issued by a DID that is resolvable and has not been subjected to a `full_revoke` or `principal_revoke` RevocationObject in the Registry. A revoked principal MUST NOT be permitted to register new agents.¶
For the purposes of this check, a **revoked principal** is defined as a principal DID (`principal_token.principal.id`) against which a `principal_revoke` Revocation Object has been submitted to this Registry. The Registry MUST maintain an index of principal DIDs associated with `principal_revoke` revocations and MUST reject new Registration Envelopes where `principal_token.principal.id` matches a DID in this index, unless the `principal_revoke` object explicitly scopes the revocation to a specific agent AID other than the one being registered.¶
Registries that permit registration without deployer authentication (open Registries) SHOULD implement a lightweight proof-of-work or CAPTCHA mechanism for registrations where `deployer_did` is absent from the principal_token context.¶
Key lookup amplification occurs when an adversary presents many tokens with distinct `kid` values, forcing the Registry to perform a lookup for each. Mitigations:¶
Relying Parties MUST cache resolved public keys for a given `kid` for up to 300 seconds (Section 6.1). Repeated validation of tokens with the same `kid` SHOULD NOT trigger repeated Registry lookups within the cache window.¶
Registries MAY reject requests for key versions older than a configurable retention window (RECOMMENDED: 90 days past the key rotation date, since all tokens issued with that key must have expired within 3600s of rotation). This prevents adversaries from constructing tokens with ancient, never-rotated keys to force deep history lookups.¶
Relying Parties MUST validate that the `kid` in the token header matches the pattern:¶
did:aip:<lowercase-namespace>:<32-lowercase-hex>#key-<positive-integer>¶
before performing any Registry lookup (Validation Step 3). Malformed `kid` values MUST be rejected with `invalid_token` without making a Registry call.¶
Approval Envelope operations require specific abuse prevention because they involve asynchronous principal interactions and potential cascade effects.¶
Per Section 14.2, Category R7.¶
A Registry SHOULD enforce a maximum of 1,000 `pending_approval` envelopes per principal DID at any time. Envelopes that expire transition to `expired` and free this quota.¶
Step claims that are not completed or failed within 600 seconds MUST be automatically failed by the Registry (Section 4.6.8). This prevents a claimed step from blocking the workflow indefinitely due to a crashed or unresponsive agent.¶
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.¶
Clients that receive HTTP 429 responses MUST implement exponential backoff with jitter. The minimum retry interval is the value in the `Retry-After` header. Clients MUST NOT retry before `Retry-After` expires.¶
Implementations SHOULD use the following backoff formula:¶
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)¶
The `Retry-After` value from the 429 response acts as a mandatory minimum: clients MUST NOT retry before `Retry-After` seconds have elapsed, even if `computed_delay + jitter` is smaller. When no `Retry-After` header is present, clients MUST treat it as 0 and rely solely on the exponential formula.¶
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.¶
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.¶
This section describes the IANA considerations for the AIP specification.¶
The did:aip DID method is requested for registration in the W3C DID Method Registry per W3C DID Specification.¶
| Field | Value |
|---|---|
| Method Name | did:aip |
| Status | Draft |
| Canonical ID | did:aip:namespace:32-hex-characters |
AIP defines a URN namespace for scope identifiers: urn:aip:scope:*. This namespace is used in OAuth 2.0 token exchange to identify capability scopes.¶
urn:aip:scope:email.read urn:aip:scope:email.write urn:aip:scope:transactions urn:aip:scope:spawn_agents.create¶
| Value | Description |
|---|---|
| G1 | Registry-Mediated Grant Flow |
| G2 | Direct Deployer Grant Flow |
| G3 | Full Ceremony Grant Flow |
AIP defines a registry of error codes.¶
| Code | HTTP | Description |
|---|---|---|
| invalid_token | 401 | Token malformed or signature invalid |
| token_expired | 401 | Token exp has passed |
| agent_revoked | 403 | AID has been revoked |
| insufficient_scope | 403 | Operation not in granted scopes |
| registry_unavailable | 503 | Registry could not be reached |
| Type | Description |
|---|---|
| application/aip+jwt | AIP Credential Token (JWT format) |
The typ header value for AIP Credential Tokens is AIP+JWT per RFC 7515.¶
A conformant AIP Registry MUST implement the following HTTP endpoints:¶
| Method | Path | Description |
|---|---|---|
| POST | /v1/agents | Register a new AID (Registration Envelope) |
| GET | /v1/agents/{aid} | Retrieve Agent Identity or DID Document |
| PUT | /v1/agents/{aid} | Key rotation only |
| GET | /v1/agents/{aid}/public-key | Current public key (JWK) |
| GET | /v1/agents/{aid}/public-key/{key-id} | Historical key version |
| GET | /v1/agents/{aid}/capabilities | Current Capability Manifest |
| PUT | /v1/agents/{aid}/capabilities | Replace Capability Manifest |
| GET | /v1/agents/{aid}/revocation | Revocation status |
| POST | /v1/revocations | Submit RevocationObject |
| GET | /v1/crl | Certificate Revocation List |
| GET | /v1/agents/{aid}/reputation | Reputation data |
| POST | /v1/endorsements | Submit Endorsement Object |
| POST | /v1/grants | Submit G1 grant (Section 4.5) |
| GET | /v1/grants/{grant_id} | Retrieve grant status |
| PUT | /v1/agents/{aid}/overlays | Submit Capability Overlay (Section 4.7) |
| GET | /v1/agents/{aid}/overlays | Retrieve current overlay |
| POST | /v1/engagements | Create Engagement Object (Section 4.8) |
| GET | /v1/engagements/{id} | Retrieve Engagement Object |
| PUT | /v1/engagements/{id} | Update Engagement (append change log) |
| POST | /v1/subscriptions | Create RPNP subscription (Section 9.4) |
| DELETE | /v1/subscriptions/{id} | Cancel RPNP subscription |
| GET | /v1/scopes | Scope Map registry (Section 15.6) |
| POST | /v1/oauth/authorize | G3 authorization endpoint (Section 15.5) |
| POST | /v1/oauth/token | G3 token endpoint / token exchange (Section 15.5) |
| GET | /.well-known/oauth-authorization-server | AS Metadata [RFC8414] |
In all path parameters, the `did:aip:` prefix and colons MUST be percent-encoded per [RFC3986]:¶
did:aip:personal:9f3a1c82b4e6d7f0a2b5c8e1d4f7a0b3 → /v1/agents/did%3Aaip%3Apersonal%3A9f3a1c82b4e6d7f0a2b5c8e1d4f7a0b3¶
All Registry responses MUST use `Content-Type: application/json`.¶
All timestamps MUST be in ISO 8601 UTC format.¶
A conformant AIP Registry MUST implement the following additional endpoints for Approval Envelopes:¶
| Method | Path | Description |
|---|---|---|
| POST | /v1/approvals | Submit Approval Envelope for approval |
| GET | /v1/approvals/{id} | Retrieve Approval Envelope with step statuses |
| POST | /v1/approvals/{id}/approve | Principal approves (wallet call; sets `principal_signature`) |
| POST | /v1/approvals/{id}/reject | Principal rejects |
| POST | /v1/approvals/{id}/steps/{n}/claim | Claim step for execution; returns Step Execution Token |
| POST | /v1/approvals/{id}/steps/{n}/complete | Mark step completed |
| POST | /v1/approvals/{id}/steps/{n}/fail | Mark step failed; triggers compensation |
| GET | /v1/approvals/{id}/steps/{n} | Get step status (used by Relying Parties for Step Execution Token verification) |
| POST | /v1/approvals/{id}/compensation-steps/{n}/claim | Claim compensation step |
| POST | /v1/approvals/{id}/compensation-steps/{n}/complete | Mark compensation step completed |
| POST | /v1/approvals/{id}/compensation-steps/{n}/fail | Mark compensation step failed |
**`POST /v1/approvals` validation:** The Registry MUST perform all checks defined in Section 4.6.10 before accepting an Approval Envelope. The Registry MUST return HTTP 201 with the stored envelope (including the Registry-assigned `status: "pending_approval"`) on success.¶
**`POST /v1/approvals/{id}/approve` flow:** This endpoint is called by the Principal Wallet after the principal completes the signing ceremony. The request body MUST contain the `principal_signature` field: a base64url EdDSA signature computed by the principal's wallet over the JCS-canonical serialisation of the full Approval Envelope with `principal_signature` set to the empty string `""` before serialisation — identical to the pattern used for `creator_signature` (Section 4.6.3). The signing key MUST be the private key of `principal_id`. The Registry MUST verify this signature against the `principal_id`'s resolved public key before transitioning the envelope to `approved`.¶
**Atomicity requirement for step claim:** The Registry MUST implement step-claim operations atomically (e.g., using optimistic locking or a distributed lock) to prevent two actors from claiming the same step simultaneously. Only one claim MUST succeed; the other MUST receive `approval_step_already_claimed`.¶
**Step Execution Token format:** The Step Execution Token returned by `POST .../steps/{n}/claim` is a JWT signed by the Registry's administrative key with the claims described in Section 4.6.8. Its TTL MUST comply with normal TTL rules (Section 7.2) for the scopes involved.¶
A conformant AIP Registry supporting G3 grants MUST implement an OAuth 2.1 Authorization Server [RFC6749] with the following requirements:¶
The token endpoint also serves RFC 8693 [RFC8693] token exchange (Section 7.5).¶
The `/.well-known/aip-registry` response MUST also include:¶
| Field | Type | Description |
|---|---|---|
| `grant_tiers_supported` | array | Supported grant tiers: `["G1", "G2", "G3"]` |
| `acr_values_supported` | array | Supported `acr` values |
| `amr_values_supported` | array | Supported `amr` values |
| `oauth_authorization_server` | string | URI of the OAuth AS metadata document (present only if G3 supported) |
| `identity_proofing_required_for_tier2` | boolean | Whether this Registry requires G3 for Tier 2 operations |
All AIP capability scopes MUST be representable as URIs in the `urn:aip:scope:` namespace:¶
scope_string → urn:aip:scope:<scope_string>
¶
Examples: `email.read` → `urn:aip:scope:email.read`, `spawn_agents.create` → `urn:aip:scope:spawn_agents.create`.¶
When used in OAuth `scope` parameters, the URI form MUST be used. Within AIP Credential Tokens (`aip_scope` array), the short string form remains canonical.¶
A conformant AIP Registry MUST implement `GET /v1/scopes` returning a JSON object mapping scope strings to metadata:¶
| Field | Type | Description |
|---|---|---|
| `uri` | string | Full `urn:aip:scope:` URI |
| `description` | string | Human-readable description |
| `tier` | integer | Tier classification (1, 2, or 3) |
| `destructive` | boolean | Whether the scope requires additional confirmation per Section 4.5.3 |
| `constraint_schema` | object/null | JSON Schema fragment for scope-specific constraints |
A conformant AIP Registry supporting Engagement Objects (Section 4.8) MUST implement:¶
| Method | Path | Description |
|---|---|---|
| POST | /v1/engagements | Create Engagement Object |
| GET | /v1/engagements/{id} | Retrieve Engagement Object |
| PUT | /v1/engagements/{id} | Update Engagement (append change log entry) |
Create validation: The Registry MUST verify the initiator's DID signature, validate that all referenced participant AIDs exist and are not revoked, and assign `status: "active"` with `seq_number: 1`.¶
Update validation: The Registry MUST verify the submitter is an active participant, the change log entry's `seq_number` is exactly `current_max + 1`, and the entry signature is valid. The Registry MUST reject modifications to existing entries with `change_log_immutable`.¶
A conformant AIP Registry supporting RPNP (Section 9.4) MUST implement:¶
| Method | Path | Description |
|---|---|---|
| POST | /v1/subscriptions | Create RPNP subscription |
| GET | /v1/subscriptions/{id} | Retrieve subscription status |
| DELETE | /v1/subscriptions/{id} | Cancel subscription |
Subscription creation MUST be authenticated via DPoP. The `webhook_uri` MUST use HTTPS. The Registry MUST reject non-HTTPS URIs with `invalid_webhook_uri`.¶
AIP follows Semantic Versioning. Before v1.0, MINOR versions MAY include breaking changes.¶
Breaking changes from v0.2:¶
aip_version is now "0.3" for conforming implementations.¶
X-AIP-Version: 0.3 replaces X-AIP-Version: 0.2.¶
spawn_agents scope is retired; use spawn_agents.create and spawn_agents.manage (Section 3).¶
grant_tier (Section 4.5).¶
AIPRegistry service entry (Section 6.2).¶
/.well-known/aip-registry response MUST include signature, registry_name, and endpoints (Section 6.3.4).¶
Implementations MUST NOT silently accept tokens from unsupported versions without logging a version warning.¶
| Tier | Revocation | DPoP | mTLS |
grant_tier
|
Principal DID Method |
|---|---|---|---|---|---|
| 1 | CRL (15 min) | NOT REQUIRED | NOT REQUIRED | G1 or G2 | Any (note 1) |
| 2 | Real-time | REQUIRED | NOT REQUIRED | G2 or G3 |
did:web (note 2) |
| 3 | Real-time + OCSP | REQUIRED | REQUIRED | G3 |
did:web (note 2) |
For Tier 1, the principal MAY use any W3C DID method. Principals using did:key are permitted for Tier 1 only; see Note 2.¶
did:aip is NEVER a valid Principal DID method for Tier 2 or Tier 3. The principal.id field MUST NOT use the did:aip method (Section 4.2.4). Principals using did:key for Tier 2 agents MUST be rejected with principal_did_method_forbidden; the did:key method does not support a DID Document with the AIPRegistry service entry required by Section 6.2.¶
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).¶
This appendix is informative.¶
This version addresses four operational and security additions relative to v0.1:¶
This appendix is informative.¶
v0.3 introduces the following changes relative to v0.2. Items marked (breaking) are not backward compatible.¶
Breaking changes:¶
spawn_agents scope retired. Replaced by spawn_agents.create and spawn_agents.manage, both classified as Tier 2 with 300s max TTL and DPoP requirement.¶
grant_tier. did:key principals forbidden for Tier 2.¶
AIPRegistry service entry.¶
/.well-known/aip-registry response MUST include signature, registry_name, and endpoints fields.¶
X-AIP-Version updated from "0.2" to "0.3".¶
Additive changes:¶
aip_approval_step (integer) and aip_engagement_id (string) added to Credential Token schema.¶
acr and amr claims added for G3 identity proofing. iss field added.¶
BACKOFF_BASE separated from Retry-After; jitter bounded by BACKOFF_BASE.¶
urn:aip:scope: URI namespace and GET /v1/scopes endpoint.¶