# SDKs Source: https://agentidentityprotocol.io/SDKs Official SDK's for building transparent and secure AI agents. Coming soon. # Architecture Source: https://agentidentityprotocol.io/architecture This document specifies a proposed standard for identity management of artificial intelligence (AI) agents in the internet. AIP is a language-agnostic protocol for establishing cryptographic identities for AI agents, enabling authentication, authorization, and audit trails across heterogeneous systems. The proposed standard creates a framework that allows an AI agent to obtain approval to a resource running a model context protocol (MCP) server by requesting an agent identifier token tied to an end user. This and following pages goes into the structure of the agent identifiers and the authorization process performed between the client and server through AIP. # null Source: https://agentidentityprotocol.io/community/contributing # Contribute to the documentation Thank you for your interest in contributing to our documentation! This guide will help you get started. ## How to contribute ### Option 1: Edit directly on GitHub 1. Navigate to the page you want to edit 2. Click the "Edit this file" button (the pencil icon) 3. Make your changes and submit a pull request ### Option 2: Local development 1. Fork and clone this repository 2. Install the Mintlify CLI: `npm i -g mint` 3. Create a branch for your changes 4. Make changes 5. Navigate to the docs directory and run `mint dev` 6. Preview your changes at `http://localhost:3000` 7. Commit your changes and submit a pull request For more details on local development, see our [development guide](development.mdx). ## Writing guidelines * **Use active voice**: "Run the command" not "The command should be run" * **Address the reader directly**: Use "you" instead of "the user" * **Keep sentences concise**: Aim for one idea per sentence * **Lead with the goal**: Start instructions with what the user wants to accomplish * **Use consistent terminology**: Don't alternate between synonyms for the same concept * **Include examples**: Show, don't just tell # null Source: https://agentidentityprotocol.io/community/contributing-policy # Contributing to the Agent Identity Protocol (AIP) Thank you for your interest in contributing to AIP. This project aims to establish a zero-trust identity standard for autonomous AI agents. ## Ways to Contribute ### For Security Researchers * Threat modeling and attack surface analysis * Penetration testing of reference implementations * Review of cryptographic choices and identity flows ### For Platform Engineers * Kubernetes operators and sidecar implementations * Cloud provider integrations (AWS, GCP, Azure) * Service mesh integrations (Istio, Linkerd) ### For AI/ML Engineers * Agent framework integrations (LangChain, AutoGPT, CrewAI) * MCP transport implementations * SDK development (Python, TypeScript, Go) ### For Compliance Teams * Regulatory mapping (SOC2, GDPR, HIPAA, FedRAMP) * Audit log schema standardization * Policy template libraries ## Getting Started ### Prerequisites * Go 1.21+ (for proxy development) * Python 3.11+ (for SDK and examples) * Node.js 20+ (for TypeScript SDK) ### Development Setup ```bash theme={null} # Clone the repository git clone https://github.com/ArangoGutierrez/agent-identity-protocol.git cd agent-identity-protocol # For Go proxy development cd proxy && go mod download && go build ./... # For Python SDK cd sdk/python && pip install -e ".[dev]" # For TypeScript SDK cd sdk/typescript && npm install && npm run build ``` ## Contribution Process ### 1. Issues First Before starting work, please: * Check existing issues for duplicates * Open an issue describing what you want to work on * Wait for maintainer feedback on approach ### 2. Branch Naming ``` feat/short-description # New features fix/issue-number # Bug fixes docs/what-changed # Documentation spec/proposal-name # Specification changes ``` ### 3. Commit Messages Follow [Conventional Commits](https://www.conventionalcommits.org/): ``` feat: add OIDC token validation to proxy fix: handle empty manifest gracefully docs: clarify egress filtering behavior spec: add delegation token schema ``` ### 4. Pull Requests * Fill out the PR template completely * Ensure CI passes (lint, test, build) * Request review from relevant CODEOWNERS * Squash commits before merge ## Specification Changes Changes to the AIP specification (`spec/`) require: 1. **RFC Process**: Open an issue with `[RFC]` prefix 2. **Discussion Period**: Minimum 2 weeks for community feedback 3. **Consensus**: Approval from at least 2 maintainers 4. **Backward Compatibility**: Document migration path if breaking ## Code Style ### Go ```bash theme={null} gofmt -s -w . go vet ./... golangci-lint run ``` ### Python ```bash theme={null} ruff check . ruff format . mypy . ``` ### TypeScript ```bash theme={null} npm run lint npm run typecheck ``` ## Testing All code changes require tests: ```bash theme={null} # Go go test -race -cover ./... # Python pytest --cov=aip # TypeScript npm test ``` ## Documentation * Code should be self-documenting with clear names * Public APIs require doc comments * Complex logic needs inline comments explaining "why" * User-facing changes need README/docs updates ## Code of Conduct We follow the [Contributor Covenant v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). **TL;DR**: Be respectful, inclusive, and professional. Focus on the work, not the person. ## License By contributing to AIP, you agree that your contributions will be licensed under the [Apache License 2.0](LICENSE). ## Questions? * **GitHub Discussions**: Architecture and design questions * **GitHub Issues**: Bug reports and feature requests * **Security Issues**: See [SECURITY.md](SECURITY.md) *** Thank you for helping make AI agents safer and more accountable. # Introduction Source: https://agentidentityprotocol.io/community/introduction Check out our [Github](https://github.com/openagentidentityprotocol) for open discussions on standardizing AIP # FAQs Source: https://agentidentityprotocol.io/faq ## General ### What's the difference between v1alpha1 and v1alpha2? v1alpha2 adds robust security features for production deployments: * **Identity Tokens**: Cryptographic session binding and replay prevention. * **Server-Side Validation**: Centralized policy enforcement via HTTP. * **Policy Signatures**: Integrity verification for policy files. * **Tool Schema Hashing**: Protection against tool poisoning. ### Do I need identity tokens? Not for local development. Identity tokens are recommended when: * You run agents in a multi-tenant environment. * You need to audit *who* (which session) performed an action, not just *what* happened. * You are using the centralized AIP Server. ### How do I set up server-side validation? 1. Enable `spec.server` in your policy. 2. Configure TLS (required for non-localhost). 3. Set `failover_mode` (recommend `fail_closed` for security). 4. See the [Server-Side Validation Guide](../implementations/go-proxy/docs/server-guide.md) for details. ### What is AIP? AIP (Agent Identity Protocol) is an open standard for secure agentic identity management and authorization starting with a specification for policy-based authorization of AI agent tool calls. It defines how to declare, enforce, and audit what actions an AI agent can perform. ### What's the difference between the AIP specification and the Go proxy? * **AIP Specification** (`spec/`): The protocol standard that anyone can implement * **Go Proxy** (`implementations/go-proxy/`): One reference implementation of that standard Think of it like HTTP (the spec) vs Apache/Nginx (implementations). ### Can I use AIP without the Go proxy? Yes! AIP is a specification. You can: * Implement AIP natively in your MCP client (Cursor, Claude Desktop, etc.) * Build your own proxy in any language * Use the Go proxy as a reference ### Does AIP require changes to my MCP server? No. AIP sits between the MCP client and server as a transparent proxy. Your MCP server doesn't need any modifications. ``` [Agent] → [AIP Proxy] → [MCP Server] ``` The proxy intercepts `tools/call` requests, applies policy, and forwards allowed requests unchanged. ## Security ### How is AIP different from workforce AI governance tools like SurePath.ai? AIP and workforce AI governance tools solve different problems at different layers: **Workforce AI Governance (e.g., SurePath.ai)**: * Monitors *employee* AI usage across your organization * Network/application level visibility * Answers: "Who in my org is using ChatGPT? What are they asking?" * Typically SaaS platforms for compliance and governance **AIP (Agent Identity Protocol)**: * Controls *what actions AI agents can take* on your infrastructure * Tool-call level authorization (blocks dangerous operations) * Answers: "Can this agent delete files? Access production databases?" * Open protocol for developers building agents **These are complementary**: Use workforce governance to monitor employee AI usage. Use AIP to secure the agents those employees build. Think of it as different layers—one monitors people, one protects infrastructure. ### How is AIP different from OAuth? | Aspect | OAuth | AIP | | ----------- | --------------------------- | ------------------------------------- | | Granularity | Scope-level ("repo access") | Action-level ("repos.get with org:X") | | Timing | Grant-time | Runtime (every call) | | Audience | End users | Developers/Security teams | | Format | Token claims | YAML policy files | OAuth answers "who is this?" AIP answers "should this specific action be allowed?" ### Can AIP prevent all prompt injection attacks? AIP significantly reduces the blast radius of prompt injection by: * Limiting which tools an agent can call * Validating arguments with regex patterns * Requiring human approval for sensitive operations * Logging all decisions for forensic analysis However, AIP cannot prevent prompt injection itself—it mitigates the *consequences*. ### What about network egress? Can a malicious agent exfiltrate data? Network egress control is planned for AIP v1beta1 (see [spec Appendix D](../spec/aip-v1alpha1.md#appendix-d-future-extensions)). Currently, tool-level authorization is enforced but the MCP server subprocess can still make network calls. For maximum security today, run MCP servers in containers with `--network=none`. ### Are audit logs tamper-proof? Audit logs are append-only from the agent's perspective (the agent doesn't have write access to the log file). For production use, forward logs to an external SIEM or use signed logging. ## Policy ### Where do I put my policy file? Anywhere you like. Common locations: * `~/.config/aip/policy.yaml` (user config) * `./agent.yaml` (project root) * `/etc/aip/policy.yaml` (system-wide) Pass the path with `--policy /path/to/policy.yaml`. ### What happens if a tool isn't in `allowed_tools`? It's blocked with error code `-32001 Forbidden`. AIP is **default-deny**. ### Can I test a policy without blocking anything? Yes! Use monitor mode: ```yaml theme={null} spec: mode: monitor # Log violations but don't block ``` Check the audit log to see what *would* have been blocked. ### How do I allow a tool but require approval? Use `action: ask`: ```yaml theme={null} tool_rules: - tool: deploy_production action: ask # Shows native OS dialog ``` ### Can I validate tool arguments? Yes, with regex patterns: ```yaml theme={null} tool_rules: - tool: postgres_query allow_args: query: "^SELECT\\s+.*" # Only SELECT queries ``` ## Implementation ### My Docker container doesn't stop when I kill the proxy! When wrapping a Docker container with AIP, signals (SIGTERM/SIGINT) are sent to the `docker` CLI process, not the container itself. This can leave zombie containers running. **Solution:** Always use `--rm` and `--init` flags: ```bash theme={null} # Bad - container may not receive signals aip --policy policy.yaml --target "docker run myimage" # Good - proper signal handling and cleanup aip --policy policy.yaml --target "docker run --rm --init -i myimage" ``` | Flag | Purpose | | -------- | ------------------------------------------------------ | | `--rm` | Automatically remove container when it exits | | `--init` | Run init process (tini) that forwards signals properly | | `-i` | Keep stdin open for JSON-RPC communication | For production deployments, consider running the AIP proxy *inside* the container or using a container orchestrator with proper lifecycle management. ### What MCP clients work with AIP? Any MCP client that supports custom server commands: * **Cursor**: Add to `~/.cursor/mcp.json` * **Claude Desktop**: Add to `claude_desktop_config.json` * **Continue (VS Code)**: Add to Continue config * **Custom clients**: Use AIP as the server command ### Does AIP work on Windows? The Go proxy builds for Windows. Human-in-the-loop (`action: ask`) uses native Windows dialogs via PowerShell. ### How do I debug policy issues? 1. Enable verbose mode: `--verbose` 2. Check stderr for policy decisions 3. Review the audit log: `cat aip-audit.jsonl | jq .` 4. Use monitor mode to test without blocking ### What's the performance overhead? Minimal. The proxy adds: * \~1-5ms per request for policy evaluation * Negligible memory overhead (policies are loaded once) JSON-RPC parsing and regex matching are fast operations. ## Contributing ### How do I report a security vulnerability? See [SECURITY.md](../SECURITY.md) for responsible disclosure instructions. ### Can I contribute a new implementation? Yes! We welcome implementations in other languages. Requirements: * Pass the conformance test suite (`spec/conformance/`) * Document your implementation * Submit a PR to be listed in the registry ### How do I propose changes to the specification? 1. Open an issue describing the change 2. Discuss with maintainers 3. Submit a PR to `spec/AIP-v1alpha1.md` 4. Include conformance tests for new behavior # 00-IETF Agent Identity Protocol Draft Source: https://agentidentityprotocol.io/ietf/ietf-draft-00 ``` Network Working Group Open AIP Working Group James Cao Carlos Eduardo Arango Gutierrez Internet-Draft Intended status: Standards Track March 2026 Expires: March 4, 2026 Agent Identity Protocol (AIP): Authentication, Attestation, Authorization, and Governance for Artificial Intelligence Agents draft-aip-agent-identity-protocol-00 Abstract This document defines the Agent Identity Protocol (AIP), an open standard for verifiable identity and policy enforcement for artificial intelligence (AI) agents. AIP addresses the problem of AI agents operating with unbounded permissions -- running as users, inheriting full API key access, and executing tool calls with no verifiable identity boundary between human and non-human actors. The protocol is structured as two cooperating layers. Layer 1 (Identity) gives every agent a unique identifier and a key pair registered with an AIP Registry; the agent signs every outbound action with that key. Layer 2 (Enforcement) interposes a proxy between the AI client and every tool server that verifies the signature, evaluates a declarative policy, and produces an allow, deny, or hold decision before any tool is reached. Status of This Memo 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 August 28, 2026. Copyright Notice 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. Table of Contents 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 2. Terminology and Conventions . . . . . . . . . . . . . . . . . 5 3. Problem Statement . . . . . . . . . . . . . . . . . . . . . . 6 4. Protocol Overview . . . . . . . . . . . . . . . . . . . . . . 8 5. Layer 1: Agent Identity . . . . . . . . . . . . . . . . . . . 11 5.1. Agent Registration . . . . . . . . . . . . . . . . . . . 11 5.2. Agent Record . . . . . . . . . . . . . . . . . . . . . . 12 5.3. AIP Registry API . . . . . . . . . . . . . . . . . . . . 13 5.4. Key Rotation . . . . . . . . . . . . . . . . . . . . . . 14 5.5. Agent Revocation . . . . . . . . . . . . . . . . . . . . 15 5.6. The AIP Token . . . . . . . . . . . . . . . . . . . . . . 15 5.7. Token Verification . . . . . . . . . . . . . . . . . . . 17 6. Layer 2: Enforcement Proxy . . . . . . . . . . . . . . . . . 18 6.1. Proxy Architecture . . . . . . . . . . . . . . . . . . . 18 6.2. AgentPolicy . . . . . . . . . . . . . . . . . . . . . . . 19 6.3. Intercept Flow . . . . . . . . . . . . . . . . . . . . . 21 6.4. Decision Outcomes . . . . . . . . . . . . . . . . . . . . 22 6.5. Human-in-the-Loop (HITL) . . . . . . . . . . . . . . . . 23 6.6. Data Loss Prevention (DLP) . . . . . . . . . . . . . . . 24 6.7. Audit Logging . . . . . . . . . . . . . . . . . . . . . . 25 7. Wire Formats . . . . . . . . . . . . . . . . . . . . . . . . 26 7.1. AIP-Token Header . . . . . . . . . . . . . . . . . . . . 26 7.2. Error Response Format . . . . . . . . . . . . . . . . . . 27 7.3. Audit Log Record . . . . . . . . . . . . . . . . . . . . 28 8. Deployment Topologies . . . . . . . . . . . . . . . . . . . . 29 8.1. Localhost Proxy . . . . . . . . . . . . . . . . . . . . . 29 8.2. Kubernetes Sidecar . . . . . . . . . . . . . . . . . . . 30 8.3. Enterprise Federation . . . . . . . . . . . . . . . . . . 30 9. Security Considerations . . . . . . . . . . . . . . . . . . . 31 10. Privacy Considerations . . . . . . . . . . . . . . . . . . . 34 11. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 35 12. References . . . . . . . . . . . . . . . . . . . . . . . . . 35 12.1. Normative References . . . . . . . . . . . . . . . . . . 35 12.2. Informative References . . . . . . . . . . . . . . . . . 36 Appendix A. Example AgentPolicy . . . . . . . . . . . . . . . . 37 Appendix B. Example AIP Token . . . . . . . . . . . . . . . . . 38 Appendix C. Error Code Reference . . . . . . . . . . . . . . . . 39 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 40 1. Introduction AI agents are being deployed at scale with the same credentials as the humans who operate them. When an agent calls a tool -- writing a file, querying a database, sending a request to an external API -- there is nothing in the request that distinguishes it from a direct human action. The downstream service has no way to know it is talking to an agent, which agent, who authorized it, or what it is permitted to do. This creates compounding problems as agents become more capable and more numerous. An agent that is compromised, misbehaves, or is manipulated into acting outside its intended scope has no technical boundary stopping it from using every credential it has been given. Audit logs attribute actions to human accounts rather than to agents, making incident investigation difficult. Multi-agent systems can accumulate permissions across delegation steps without any explicit record of what was authorized. AIP closes this gap with two layers: Layer 1 -- Agent Identity. At provisioning time, the agent is registered with an AIP Registry. The registry assigns the agent a unique identifier (the Agent ID) and records the agent's public key alongside the identity of the accountable principal. From that point forward, the agent signs every outbound tool call with its private key. Any party that can reach the registry can verify who the agent is. Layer 2 -- Enforcement Proxy. An AIP Proxy sits between the AI client and every tool server. It intercepts every tool call, verifies the agent's signature against the registry, evaluates the call against a simple declarative policy (the AgentPolicy), and either forwards the call, blocks it, or holds it for human approval. The tool server is never reached until all checks pass. Every decision is written to an append-only audit log. The two layers are independent. Layer 1 can be used without Layer 2 to provide signed, attributable agent actions in existing systems. Layer 2 requires Layer 1 for identity verification but adds no new requirements on tool servers. AIP targets the Model Context Protocol (MCP) [MCP] as its primary tool-call interface but is designed to be applicable to any structured tool invocation mechanism. AIP does NOT: o Define a new transport protocol. o Replace existing service-level authentication (OAuth 2.0, mTLS). It adds an agent-identity layer on top of existing mechanisms. o Provide content moderation or model output filtering. 2. Terminology and Conventions 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. Agent: An autonomous software process that uses a large language model or other AI system to reason over tasks and invoke external tools on behalf of a principal. Agent ID: A unique, stable identifier assigned to an agent by an AIP Registry at registration time. The Agent ID is a UUID v4 [RFC4122] prefixed with the registry hostname, e.g., "reg.example.com/01933f4a-9b2c-7d8e-af01-3b5c6d7e8f9a". Agent Record: The data structure stored in the AIP Registry that holds an agent's Agent ID, public key, principal identifier, and metadata. Defined in Section 5.2. AIP Proxy (or "Enforcement Proxy"): A transparent forward proxy that intercepts tool calls between an AI client and tool servers. It verifies the AIP Token and evaluates the AgentPolicy before forwarding or blocking the call. AIP Registry: A server that stores Agent Records and exposes an HTTP API for registration, key lookup, and revocation. AIP Token: A signed JSON object attached to every tool call by the agent. It carries the agent's Agent ID, the tool being called, a nonce, a timestamp, and an key signature. Defined in Section 5.6. AgentPolicy: A YAML configuration file that declares which tools an agent is permitted to call, argument constraints, DLP rules, and HITL requirements. Defined in Section 6.2. DLP: Data Loss Prevention; scanning of tool call arguments and responses for sensitive data patterns. HITL: Human-in-the-Loop; a control mode in which the proxy holds a tool call and waits for explicit approval from an operator before forwarding it. IoA: Internet of Agents; the network of autonomous AI agents that act across organizational and infrastructure boundaries. MCP: Model Context Protocol [MCP]; a structured protocol for tool-call communication between AI clients and tool servers. Principal: The human operator or organization accountable for an agent. Tool Call: A structured invocation of an external capability initiated by an agent, typically carrying a tool name and arguments. Tool Server: A service that exposes tools callable by agents. 3. Problem Statement 3.1. The Identity Gap When an AI agent calls a tool, it presents credentials that belong to a human account. The tool server cannot tell whether the actor is a human or an agent, which agent it is, or what limits apply to it. This creates four concrete problems: (a) Security -- A compromised or manipulated agent can invoke any tool the human account can reach. There is no agent-specific authorization boundary. (b) Auditability -- Logs record actions against a human account. After an incident, investigators cannot determine which actions were taken by a human versus an agent. (c) Compliance -- Regulations increasingly require traceability of automated decision-making. Without agent-level identity, organizations cannot satisfy these requirements. (d) Accountability -- Billing, rate limits, and quotas are scoped to human accounts. Agent usage cannot be isolated or attributed. 3.2. The Enforcement Gap Even where agent behavior policies exist, they are expressed as text in model system prompts. System prompts are not tamper-evident and can be bypassed by adversarial inputs to the model. There is no infrastructure-layer enforcement point that acts independently of the model. 3.3. Scope of This Specification AIP closes both gaps. Layer 1 gives every agent a distinct, verifiable identity independent of the human principal's credentials. Layer 2 enforces agent-specific policy at the tool-call boundary, outside the model's trust domain, in a way that cannot be overridden by model outputs. 4. Protocol Overview 4.1. Architecture AIP introduces two components into the agent-to-tool-server path: an AIP Registry (Layer 1) and an AIP Proxy (Layer 2). +-----------------------------------------+ | AI Client (Agent) | +-----------------+-----------------------+ | tools/call + AIP Token v +-----------------------------------------+ | AIP Proxy (Layer 2) | | +-----------+ +--------------------+ | | | Verify | | Policy Engine | | | | Token | | (AgentPolicy) | | | +-----+-----+ +---------+----------+ | | | | | | +-----v------------------v----------+ | | | Audit Logger | | | +-----------------------------------+ | +-----+-------------------+---------------+ | | ALLOW | DENY | HOLD v v +-----------+ +------------------+ | Tool | | Error / HITL | | Server | | Queue | +-----------+ +------------------+ ^ | key lookup | +---------+ | AIP | | Registry| | (Lay. 1)| +---------+ Figure 1: AIP Architecture 4.2. Call Lifecycle The lifecycle of a single tool call under AIP is as follows: 1. Registration (once, at deploy time). The principal registers the agent with an AIP Registry. The registry assigns an Agent ID and stores the agent's public key. The agent stores its private key securely. 2. Token construction (per call). Before each tool call the agent constructs an AIP Token (Section 5.6): a small JSON object containing the Agent ID, tool name, argument hash, nonce, timestamp, and a key signature over the token. 3. Proxy intercept. The AIP Proxy receives the tool call request before it reaches the tool server. 4. Token verification (Layer 1). The proxy retrieves the agent's public key from the registry (or local cache) and verifies the signature. It also checks the nonce for replay and the timestamp for freshness. 5. Policy evaluation (Layer 2). The proxy checks the call against the AgentPolicy: is the tool on the allowlist? Do the arguments pass validation? Does the call trigger a HITL hold? Does the response contain sensitive data that must be redacted? 6. Decision. The proxy produces one of three outcomes: ALLOW -- forward the call to the tool server; DENY -- return an error to the agent, tool server not reached; HOLD -- queue the call for human approval. 7. Audit. Every decision is written to the audit log regardless of outcome. 4.3. Relationship to Existing Standards o OAuth 2.0 [RFC6749] / OIDC [OIDC]: AIP does not replace service- level authentication. The agent still presents its OAuth token or API key to the tool server. AIP provides a separate agent identity layer that the proxy can verify independently. o SPIFFE/SVID [SPIFFE]: In Kubernetes deployments, the AIP Proxy MAY use a SPIFFE SVID to authenticate to tool servers over mTLS, layering workload identity on top of AIP agent identity. o JSON-RPC 2.0 [JSON-RPC]: Tool call and error messages use the JSON-RPC 2.0 wire format, compatible with MCP and similar protocols. 5. Layer 1: Agent Identity 5.1. Agent Registration An agent is provisioned by its principal submitting a registration request to an AIP Registry. The request MUST include: (a) The agent's public key, base64url-encoded [RFC4648]; (b) The principal identifier -- a string that uniquely identifies the accountable human or organization (e.g., an email address, an organization slug, or an OAuth subject claim); (TO BE WORKED ON FURTHER) (c) A human-readable agent name (RECOMMENDED); (d) An optional free-text description. The registration request MUST be authenticated. Authentication MAY be via OAuth 2.0 bearer token, mTLS client certificate, or a pre- shared registration secret, at the registry operator's discretion. On successful registration, the registry: (a) Assigns a UUID v4 [RFC4122] as the agent's local identifier; (b) Constructs the Agent ID as "/"; (c) Stores the Agent Record (Section 5.2); (d) Returns the Agent ID to the principal. The principal MUST store the Agent ID and configure the agent with both the Agent ID and its private key before deployment. 5.2. Agent Record The Agent Record is the data structure the AIP Registry stores for each registered agent. It MUST contain: agentId (string): The Agent ID assigned at registration, in the form "/". Example: "reg.agentidentityprotocol.io/01933f4a-9b2c-7d8e-af01" publicKey (string): The agent's current public key, base64url-encoded. principalId (string): Identifier of the accountable principal. name (string): Human-readable agent name. Informational only; not authenticated. description (string, optional): Free-text description of the agent's purpose. createdAt (string): ISO 8601 UTC timestamp of registration. keyHistory (array): Append-only list of all public keys ever bound to this Agent ID. Each entry contains "publicKey", "activeFrom", and "revokedAt" (null if still active). status (string): One of "active" or "revoked". Example Agent Record (JSON): { "agentId": "reg.agentidentityprotocol.io/01933f4a-9b2c-7d8e-af01", "publicKey": "MCowBQYDK2VwAyEAz8vG...", "principalId": "acme-corp", "name": "ResearchAssistant-v1", "description": "Internal document retrieval agent", "createdAt": "2026-01-15T09:00:00Z", "keyHistory": [ { "publicKey": "MCowBQYDK2VwAyEAz8vG...", "activeFrom": "2026-01-15T09:00:00Z", "revokedAt": null } ], "status": "active" } 5.3. AIP Registry API The AIP Registry MUST expose the following HTTP endpoints over TLS 1.3 (Section 9.4): POST /v1/agents Register a new agent GET /v1/agents/{agentId} Retrieve an Agent Record PUT /v1/agents/{agentId}/key Rotate the agent's public key DELETE /v1/agents/{agentId} Revoke an agent GET /v1/revocations/stream SSE stream for revocation events The GET /v1/agents/{agentId} endpoint is the only endpoint that MUST be reachable by AIP Proxies at call-verification time. All other endpoints are used during provisioning and key management. Responses from GET /v1/agents/{agentId} MUST include the full Agent Record. Proxies SHOULD cache this response for at least 30 seconds. The cache MUST be invalidated on receipt of a revocation event from the SSE stream. 5.4. Key Rotation A principal rotates an agent's key by submitting a PUT request to /v1/agents/{agentId}/key, authenticated with the current private key. The request body MUST contain the new public key. The registry MUST: (a) Set "revokedAt" on the current keyHistory entry to the current timestamp; (b) Append a new keyHistory entry with the new public key; (c) Update the "publicKey" field on the Agent Record; (d) Emit a rotation event on the revocations SSE stream so that proxies can invalidate their cached Agent Records immediately. The Agent ID does NOT change on key rotation. 5.5. Agent Revocation A principal revokes an agent by sending a DELETE request to /v1/agents/{agentId}. The registry MUST set the agent's status to "revoked" and emit a revocation event on the SSE stream. Proxies MUST reject AIP Tokens from revoked agents with error AIP-E012. Revoked Agent Records MUST be retained in the registry for audit log verification but MUST NOT be returned as "active". 5.6. The AIP Token (Agent Attestation Token) The AIP Token is a compact signed JSON object that the agent constructs and attaches to every outbound tool call. It is the mechanism by which the agent asserts its identity to the proxy. 5.6.1. Token Fields aipVersion (string, REQUIRED): Protocol version. MUST be "1" for this specification. agentId (string, REQUIRED): The agent's Agent ID as registered with the AIP Registry. tool (string, REQUIRED): The name of the tool being called, exactly as declared in the tool server's manifest. argumentsHash (string, REQUIRED): Lowercase hex-encoded SHA-256 hash of the canonical JSON serialization of the tool call arguments. This binds the token to the specific arguments being passed. nonce (string, REQUIRED): A 128-bit cryptographically random value, hex-encoded. MUST be unique per token. MUST be generated by a CSPRNG. timestamp (string, REQUIRED): ISO 8601 UTC timestamp of token construction. signature (string, REQUIRED): Base64url-encoded signature over the canonical serialization of the token (Section 5.6.2). 5.6.2. Canonical Serialization To produce the bytes that are signed: (a) Construct a JSON object containing all token fields except "signature"; (b) Serialize with no insignificant whitespace; (c) Sort object keys lexicographically; (d) Encode as UTF-8. The signature field is then set to the base64url encoding of the signature over these bytes. 5.6.3. Example Token (before base64url encoding for transport) { "aipVersion": "1", "agentId": "reg.agentidentityprotocol.io/01933f4a-9b2c-7d8e-af01", "tool": "read_file", "argumentsHash": "e3b0c44298fc1c149afb4c8996fb924...", "nonce": "a3f8b2c1d4e5f607a8b9c0d1e2f3a4b5", "timestamp": "2026-02-24T14:30:00Z", "signature": "TUlJQ0lqQU5CZ2txaGtpRzl3MEJB..." } 5.7. Token Verification The AIP Proxy MUST perform the following checks in order. A failure at any step MUST produce a DENY decision with the corresponding error code (Section 7.2) and an audit log entry. Step 1 -- Presence check. Confirm the AIP Token is present in the request. On failure: AIP-E010. Step 2 -- Agent Record lookup. Resolve the Agent ID from the token against the AIP Registry (or local cache). Confirm the Agent Record status is "active". On failure: AIP-E011 (unresolvable) or AIP-E012 (revoked). Step 3 -- Signature verification. Compute the canonical serialization (Section 5.6.2) and verify the key signature against the public key in the Agent Record. This operation MUST be performed in constant time. On failure: AIP-E013. Step 4 -- Nonce replay check. Check the nonce against the proxy's local nonce cache (minimum TTL: 600 seconds). If the nonce has been seen before, reject. On failure: AIP-E004. Step 5 -- Timestamp freshness. Reject the token if its timestamp is more than 300 seconds in the past or 30 seconds in the future relative to the proxy clock. On failure: AIP-E005. If all five steps pass, the token is verified and the call proceeds to policy evaluation (Section 6.3). 6. Layer 2: Enforcement Proxy 6.1. Proxy Architecture The AIP Proxy is a forward proxy interposed between the AI client and one or more tool servers. It is transparent: it presents itself to the AI client as the tool server endpoint, and to the tool server as an authorized caller. The proxy operates in one of two modes, set per agent in the AgentPolicy: enforce: Policy violations result in DENY responses. The tool server is never reached for blocked calls. This is the default and RECOMMENDED mode. monitor: Policy violations are logged but calls are forwarded regardless. Used for baselining and policy development before switching to enforce mode. The proxy MUST maintain locally: o The AgentPolicy for each agent it serves; o A bounded nonce cache (TTL >= 600 seconds, LRU eviction); o A revocation cache (refresh interval <= 60 seconds); o An append-only audit log. 6.2. AgentPolicy The AgentPolicy is a YAML file that declares the enforcement rules for a specific agent. One AgentPolicy file corresponds to one Agent ID. A AgentPolicy file can apply to multiple Agent IDs. 6.2.1. Schema agentId: mode: tools: allowed: - rules: - tool: action: args: : pattern: maxLength: dlp: - name: regex: action: scope: hitl: approvers: - timeout_seconds: on_timeout: 6.2.2. Tool Allowlist The tools.allowed list defines every tool the agent is permitted to call. Any call to a tool not on this list MUST be denied with AIP-E001 when the proxy is in enforce mode. 6.2.3. Tool Rules Each entry in tools.rules applies additional handling to a named tool: allow: The tool is on the allowlist and is forwarded after argument validation. This is the default when no rule is specified. ask: The call is held for HITL approval before forwarding, regardless of any other policy check. block: The call is unconditionally denied. This overrides the allowlist and cannot be circumvented. Use this for tools that must never be reachable by the agent under any circumstances. Argument rules (args) apply PCRE regex pattern matching and a maximum length check to named arguments before the call is forwarded. A violation produces AIP-E002. 6.2.4. DLP Rules Each DLP rule specifies a regex pattern, an action, and a scope: redact / request: Matching content in arguments is replaced with "[REDACTED:]" and the call proceeds. redact / response: Matching content in the tool response is replaced with "[REDACTED:]" before the response is returned to the agent. block / both: If a match is found anywhere in the request or response, the call or response is rejected with AIP-E008. 6.2.5. HITL Configuration The hitl block identifies who may approve held calls, how long the proxy waits, and what to do on timeout. The default on_timeout is "deny". Setting on_timeout to "allow" SHOULD only be used for non-sensitive, low-impact tools. 6.3. Intercept Flow The following is the normative sequence for every tool call received by the proxy. 1. Receive the tool call from the AI client. 2. Run token verification (Section 5.7). Any failure -> DENY with the corresponding AIP-Exxx code. 3. Check tools.allowed. Tool not present and mode == enforce -> DENY AIP-E001. 4. Check tools.rules for this tool. action == block -> DENY AIP-E003. action == ask -> go to step 7 (HITL). 5. Validate arguments against any matching tools.rules.args. Violation -> DENY AIP-E002. 6. Run DLP scan on request arguments. block match -> DENY AIP-E008. redact match -> replace content, continue. 7. If action == ask: submit to HITL queue (Section 6.5). Approved -> continue to step 8. Denied -> DENY AIP-E015. Timed out -> resolve per on_timeout. 8. ALLOW: forward the call to the tool server. 9. Receive the tool server response. 10. Run DLP scan on the response. block match -> return AIP-E008 to agent, suppress response. redact match -> replace content, continue. 11. Return the (possibly redacted) response to the agent. 12. Write audit log record (Section 6.7). 6.4. Decision Outcomes ALLOW: The call passed all checks and was forwarded. The tool server response was returned to the agent. DENY: The call was rejected before reaching the tool server. The proxy returns a JSON-RPC 2.0 error (Section 7.2). The tool server receives nothing. HOLD: The call is queued. The proxy returns a pending response to the agent. The call is resolved when a human approver responds or the HITL timeout is reached. 6.5. Human-in-the-Loop (HITL) When a call is held, the proxy MUST notify all addresses listed in hitl.approvers. The notification MUST include: o The Agent ID and agent name; o The tool name and (post-redaction) arguments; o The policy rule that triggered the hold; o A unique hold_id. Notification delivery (email, webhook, Slack, web UI) is out of scope for this specification. Approvers respond to the proxy via: POST /v1/hitl/{hold_id}/approve POST /v1/hitl/{hold_id}/deny These endpoints MUST be authenticated. If no response is received within hitl.timeout_seconds, the proxy resolves the hold according to hitl.on_timeout and writes the outcome to the audit log. 6.6. Data Loss Prevention (DLP) The DLP scanner applies the dlp rules from the AgentPolicy to both the inbound tool call arguments and the outbound tool server response. Rules are evaluated in the order they are listed. The first matching rule wins. Implementations SHOULD provide a standard rule library covering common sensitive data types: o Cloud provider credentials (AWS, GCP, Azure key patterns); o Private key material (PEM headers); o Common PII patterns (email, phone, SSN formats); o Generic high-entropy secrets (long alphanumeric tokens). 6.7. Audit Logging The proxy MUST write one log record per tool call outcome. Records MUST be appended to an append-only log and MUST NOT be modified after writing. Log records SHOULD be hash-chained: each record includes the SHA-256 hash of the previous record, enabling tamper detection. See Section 7.3 for the normative record format. Log records MUST be retained for a minimum of 90 days. 7. Wire Formats 7.1. AIP-Token Header For HTTP-based tool transports, the AIP Token is conveyed in the request header: AIP-Token: base64url encoding is defined in [RFC4648] Section 5 (no padding). For MCP stdio transports, the AIP Token is conveyed as a "_aip" field at the top level of the JSON-RPC request object: { "jsonrpc": "2.0", "method": "tools/call", "id": 1, "params": { "name": "read_file", "arguments": { "path": "/data/report.txt" } }, "_aip": { "aipVersion": "1", "agentId": "reg.agentidentityprotocol.io/01933f4a...", "tool": "read_file", "argumentsHash": "e3b0c44298...", "nonce": "a3f8b2c1d4e5f607...", "timestamp": "2026-02-24T14:30:00Z", "signature": "TUlJQ0lq..." } } 7.2. Error Response Format Error responses MUST conform to JSON-RPC 2.0 [JSON-RPC]. AIP error codes are in the range -32001 to -32099: Code ID Meaning ------ -------- ----------------------------------------------- -32001 AIP-E001 Tool not in allowlist -32002 AIP-E002 Argument validation failed -32003 AIP-E003 Tool unconditionally blocked -32004 AIP-E004 Nonce replay detected -32005 AIP-E005 Timestamp out of range -32008 AIP-E008 DLP violation -32010 AIP-E010 AIP Token missing -32011 AIP-E011 Agent ID not found in registry -32012 AIP-E012 Agent revoked -32013 AIP-E013 Signature verification failed -32015 AIP-E015 HITL approval denied -32016 AIP-E016 HITL timed out -32099 AIP-E099 Internal proxy error Example error response: { "jsonrpc": "2.0", "id": 1, "error": { "code": -32001, "message": "AIP-E001: tool not in allowlist", "data": { "aipCode": "AIP-E001", "agentId": "reg.agentidentityprotocol.io/01933f4a...", "tool": "exec_command" } } } 7.3. Audit Log Record Each record is a single JSON object on one line (JSONL [JSONL]): { "v": 1, "ts": "", "eventId": "", "prevHash": "", "decision": "", "errorCode": "", "agentId": "", "principalId": "", "tool": "", "argumentsHash": "", "policyName": "", "verificationStep": "<1-5 or null if passed>", "dlp": [ { "rule": "", "scope": "", "action": "" } ], "holdId": "", "proxyVersion": "" } 8. Deployment Topologies 8.1. Localhost Proxy The localhost proxy is the simplest deployment: a single binary running on the developer's machine alongside the AI client. Binding: 127.0.0.1:8787 (configurable) Transport: HTTP or MCP stdio interception Storage: Local SQLite (nonce cache, revocation cache, audit log) Policy: YAML file at ~/.aip/policy.yaml This mode is suitable for individual developers and is the recommended starting point for adopting AIP. A go-proxy implementation is already ready at the working group's GitHub. 8.2. Kubernetes Sidecar For production deployments, the AIP Proxy runs as a sidecar container in the same pod as the agent container. Intercept: iptables REDIRECT rules route outbound tool-server traffic through the proxy on port 15001 Policy: Kubernetes ConfigMap or Secret Storage: Redis (nonce cache); PersistentVolumeClaim (audit log) Metrics: Prometheus endpoint at /metrics Exported metrics include: aip_calls_total, aip_denials_total, aip_holds_total, aip_verification_latency_seconds. 8.3. Enterprise Federation In enterprise environments the AIP Proxy integrates with existing identity infrastructure: o OIDC mapping: The registry maintains a table mapping Agent IDs to OIDC subject claims. When a tool server requires an OIDC token, the proxy can present one on the agent's behalf after AIP verification passes, without the agent holding OIDC credentials directly. o SPIFFE/mTLS: The proxy is issued a SPIFFE SVID and uses it to establish mTLS connections to tool servers, providing transport- layer mutual authentication in addition to AIP application-layer identity. o Central policy management: AgentPolicy files are managed via a policy API with versioning, rollback, and change auditing, rather than local YAML files. 9. Security Considerations 9.1. Cryptographic Algorithm AIP is built with the idea of using Ed25519 [RFC8032] for all signatures. Ed25519 was chosen because it is fast (sign and verify in under 1ms), produces short keys and signatures (32 and 64 bytes respectively), is deterministic (no per-signature randomness required), and has broad library support across all major languages and platforms. However other cryptographic algorithms should be supported. All Ed25519 operations MUST use a constant-time implementation to prevent timing side-channels (Section 9.3). 9.2. Prompt Injection Resistance The principal threat model for AIP at Layer 2 is the prompt injection attack: malicious content in the agent's context causes it to attempt tool calls outside its intended scope. The allowlist in the AgentPolicy prevents the agent from reaching tools it was not provisioned for, regardless of what the model produces. The "block" action provides an unconditional deny for specified tools that cannot be overridden by any model output, injected prompt, or delegation claim. The proxy operates entirely outside the model's trust boundary; the model cannot influence the proxy's decisions. AIP does not protect against: o A compromised agent runtime that routes calls around the proxy; o Tool servers that accept calls from sources other than the proxy. 9.3. Transport Security All communication between AIP Proxies and the AIP Registry MUST use TLS 1.3 [RFC8446]. Server certificates MUST be validated against the system trust store. Certificate pinning is RECOMMENDED for registry connections in production deployments. Proxy-to-tool-server connections MUST use TLS 1.2 [RFC5246] or later. TLS 1.3 is RECOMMENDED. 9.4. Private Key Storage Agent private keys MUST be stored in a secure key store. In order of preference: 1. Hardware Security Module (HSM) or Trusted Platform Module (TPM); 2. OS keychain (macOS Keychain, Windows DPAPI, Linux Secret Service); 3. Encrypted file with passphrase derived using Argon2id [RFC9106]. Private keys MUST NOT be stored in environment variables, plaintext configuration files, or source code repositories. 9.5. Registry Trust A proxy MUST be configured with an explicit list of trusted registry hostnames and the TLS certificate fingerprint (or CA) for each. Agent Records from registries not on this list MUST be rejected. 9.6. Nonce Cache Sizing The nonce cache must be large enough to hold all unique nonces generated within the TTL window (600 seconds). Implementations MUST use a bounded cache with LRU eviction and MUST NOT silently drop old nonces without ensuring they are outside the TTL window. 9.7. Revocation Latency The proxy's revocation cache has a maximum refresh interval of 60 seconds (Section 6.1). In the worst case, a revoked agent can continue making calls for up to 60 seconds after revocation. Deployments with stricter requirements SHOULD subscribe to the registry's SSE revocation stream (Section 5.3) and invalidate the cache on receipt of a revocation event. 9.8. Denial-of-Service The proxy adds a small amount of latency to every tool call (one cache lookup and one key verification). To prevent the proxy from becoming an availability bottleneck: o Agent Record lookups MUST be served from the local cache except on cache miss or invalidation; o The proxy SHOULD enforce a per-agent call rate limit to prevent a runaway agent from flooding the registry with cache-miss lookups. 10. Privacy Considerations 10.1. Agent Record Visibility Agent Records are visible to any party that can query the registry. Principals SHOULD use non-identifying agent names for agents that handle sensitive workloads. 10.2. Audit Log Sensitivity Audit logs contain Agent IDs, tool names, and argument hashes. While argument values are not logged in plaintext, the combination of Agent ID and tool name may itself be sensitive in some contexts. Audit logs MUST be access-controlled appropriately. 10.3. DLP Redaction When DLP redaction is applied to a response, the proxy modifies the data the agent receives. Operators MUST ensure that redaction does not cause the agent to produce incorrect or harmful downstream actions due to missing context. 10.4. Registry Data Retention Registry operators MUST publish a data retention policy. Agent Records for revoked agents SHOULD be pseudonymized after the minimum retention period required for audit log verification. 11. IANA Considerations 11.1. HTTP Header Field This document defines the "AIP-Token" HTTP header field. Registration is requested in the "Permanent Message Header Field Names" registry per [RFC3864]: Header field name: AIP-Token Applicable protocol: http Status: standard Author/Change controller: IETF Specification document: This document (Section 7.1) 11.2. Media Type The media type "application/aip+json" is requested for AIP Tokens serialized as standalone JSON documents: Type name: application Subtype name: aip+json Required parameters: version ("1") Encoding considerations: UTF-8 Security considerations: See Section 9. 12. References 12.1. Normative References [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997. [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017. [RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital Signature Algorithm (EdDSA)", RFC 8032, DOI 10.17487/RFC8032, January 2017. [RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018. [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security (TLS) Protocol Version 1.2", RFC 5246, DOI 10.17487/RFC5246, August 2008. [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006. [RFC4122] Leach, P. et al., "A Universally Unique IDentifier (UUID) URN Namespace", RFC 4122, DOI 10.17487/RFC4122, July 2005. [RFC6749] Hardt, D., Ed., "The OAuth 2.0 Authorization Framework", RFC 6749, DOI 10.17487/RFC6749, October 2012. [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security (TLS) Protocol Version 1.2", RFC 5246, DOI 10.17487/RFC5246, August 2008. [RFC9106] Biryukov, A. et al., "Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications", RFC 9106, DOI 10.17487/RFC9106, September 2021. [JSON-RPC] JSON-RPC Working Group, "JSON-RPC 2.0 Specification", January 2013. [RFC3864] Klyne, G. et al., "Registration Procedures for Message Header Fields", BCP 90, RFC 3864, DOI 10.17487/RFC3864, September 2004. 12.2. Informative References [MCP] Anthropic, "Model Context Protocol", 2024. [OIDC] Sakimura, N. et al., "OpenID Connect Core 1.0", November 2014. [SPIFFE] CNCF SPIFFE Project, "SPIFFE: Secure Production Identity Framework for Everyone". [JSONL] "JSON Lines", Appendix A. Example AgentPolicy A complete AgentPolicy for a research assistant agent. agentId: reg.agentidentityprotocol.io/01933f4a-9b2c-7d8e-af01 mode: enforce tools: allowed: - read_file - list_directory - web_search - git_status - write_file - create_issue rules: - tool: write_file action: ask args: path: pattern: "^/workspace/.*" maxLength: 512 - tool: exec_command action: block - tool: delete_file action: block - tool: create_issue action: ask dlp: - name: aws-access-key regex: "AKIA[A-Z0-9]{16}" action: block scope: both - name: private-key-pem regex: "-----BEGIN (RSA |EC |)PRIVATE KEY-----" action: block scope: both - name: generic-token regex: "[a-zA-Z0-9_\\-]{40,}" action: redact scope: response hitl: approvers: - ops@acme.example timeout_seconds: 300 on_timeout: deny Appendix B. Example AIP Token An AIP Token for a read_file call, shown before base64url encoding for transport in the AIP-Token header. { "aipVersion": "1", "agentId": "reg.agentidentityprotocol.io/01933f4a-9b2c-7d8e-af01", "tool": "read_file", "argumentsHash": "e3b0c44298fc1c149afb4c8996fb92427ae41e4649b934ca495991b7852b855", "nonce": "a3f8b2c1d4e5f607a8b9c0d1e2f3a4b5", "timestamp": "2026-02-24T14:30:00Z", "signature": "TUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFOQ0E..." } Appendix C. Error Code Reference AIP-E001 Tool not in allowlist The requested tool is not listed in tools.allowed and the proxy is in enforce mode. AIP-E002 Argument validation failed A tool argument failed a pattern or maxLength check defined in tools.rules.args. AIP-E003 Tool unconditionally blocked The tool has action: block in tools.rules. AIP-E004 Nonce replay The nonce in the AIP Token was already seen within the 600-second cache window. AIP-E005 Timestamp out of range The token timestamp is more than 300 seconds old or more than 30 seconds in the future. AIP-E008 DLP violation A DLP rule with action: block matched content in the request or response. AIP-E010 AIP Token missing The tool call arrived with no AIP-Token header or _aip field. AIP-E011 Agent ID not found The agentId in the token could not be resolved at the registry. AIP-E012 Agent revoked The Agent Record for this agentId has status: revoked. AIP-E013 Signature verification failed The key signature did not verify against the public key in the Agent Record. AIP-E015 HITL approval denied A human approver explicitly denied the held call. AIP-E016 HITL timed out The HITL hold expired and on_timeout is set to deny. AIP-E099 Internal proxy error An unexpected error occurred in the proxy. See proxy logs for details. Authors' Addresses Open Agent Identity Protocol Working Group GitHub: Comments and suggestions should be directed to the working group mailing list or submitted as issues on the GitHub repository. Author bios can also be found in the github. This document is a work in progress. It does not represent the consensus of the IETF and has not been approved for publication as an RFC. The authors actively solicit review, implementation experience reports, and contributions toward formal IETF submission. ``` # Agent Identity Protocol (AIP) Source: https://agentidentityprotocol.io/index ## What is AIP? AIP(Agent Identity Protocol) is an open-source standard for authentication, attestation, and governance of artificial intelligence (AI) agents. There's an increasing problem of agents getting full permissions to API keys, secrets, permissions and running AS the user. This will become a larger problem when the line between the actions between what a human and non-human actor becomes blurred. This has implications not just at a security level but also a legal, societal, and economic level. AIP is being built and proposed to the IETF to provide a universal standard for identity in the Internet of Agents (IoA) so that anyone, anywhere, can build secure agents and gain visibility with confidence. The protocol is served by two layers, a Layer 1 identity model and a Layer 2 enforcement proxy model. This creates a zero trust layer that validates every agent before a tool is called. ## Architecture Follow how AIP is being implemented ## Develop Get started with the SDKs # Identity Workflow Source: https://agentidentityprotocol.io/layer-1-identity/identity-workflow Below is the proposed workflow for AIP in action. ## Agent Registration ``` 1. Agent generates cryptographic key pair 2. Agent → Registry POST /v1/agents/register Request Body: { "identity": { "name": "customer_support_agent", "type": "autonomous_agent", "created_by": "joe", "organization_id": "acme123" }, "provenance": { "framework": "langchain", // do we need? "model": "latest-model", "build_environment": "production", "code_hash": ABCD, }, "capabilities": [ "read:customer_tickets", "write:email_responses" ] "public_key": "", } 3. Registry validates: - Public key format and algorithm - Proof of private key possession (signature verification) - Organization exists and has capacity - Capabilities align with org policies - Code hash is unique (prevents duplicate registrations) 4. Registry → Agent Response returns certificate for agent. { "agent_id": "1234567", "version": "1.0.0", "status": "registered", "registry_url": "https://registry.viewagents.ai", "attestation_certificate": "-----BEGIN CERTIFICATE-----\nMIIC...", "created_at": "2026-02-09" } 5. Agent stores: - agent_id - Private key (encrypted at rest) - Attestation cert - Issuer public key for verification ``` ## Token Authentication ``` 1. Agent needs to call an API 2. Agent → Token Issuer POST /v1/auth/token Request Body: { "agent_id": "abc123", "requested_scope": ["read:customer_tickets"], "context": { "task_id": "task_resolve_ticket_12345", "target_system": "company_api", }, "ttl": 3600 } 3. Token Issuer validates: - Agent signature using stored public key - Agent is not revoked - Requested scope ⊆ agent capabilities - TTL ≤ agent max_token_lifetime - Context satisfies organizational policies - Agent hasn't exceeded rate limits 4. Token Issuer → Agent Response (200 OK): { "access_token": "abcde", "expires_in": 3600, "scope": ["read:customer_tickets"], "issued_at": "2026-02-11T11:00:00Z", } 5. Agent stores token in memory (never disk for security) 6. Token Issuer logs audit event: { "event_type": "token_issued", "agent_id": "abc123", "scope": ["read:customer_tickets"], "context": {...}, "timestamp": "2026-02-11" } ``` ## Token Verification ``` 1. Agent → Resource Server (same as above) 2. Resource Server → Token Issuer POST /v1/auth/verify Request Body: { "token": "abc....", "required_scope": ["read:customer_tickets"], "resource_context": { "resource_type": "customer_ticket", "resource_id": "12345", "data_classification": "pii", "geographic_location": "us-east" } } 3. Token Issuer validates: - All local checks (signature, expiration, scope) - Token not revoked (authoritative check) - Context matches agent constraints (data_residency, etc.) - Real-time policy evaluation (new policies since token issued) 4. Token Issuer → Resource Server Response (200 OK): { "valid": true, "agent_id": "abc123", "agent_name": "customer_support_agent", "agent_version": "1.0.0", "scope": ["read:customer_tickets"], "expires_at": "2026-02-06T12:00:00Z", "trust_level": "verified", "code_hash": "sha256:a3f5b8c9d2e1...", "constraints_satisfied": true, "warnings": [] } 5. Resource Server proceeds with authorization decision ``` # Policy Reference Source: https://agentidentityprotocol.io/layer-2-enforcement/policy-reference > **Note**: This is a user-friendly guide to writing AIP policies. For the formal specification, see [AIP v1alpha1](/specs/aip-v1alpha1). Complete reference for AIP policy YAML files (`agent.yaml`). ## Table of Contents * [Overview](#overview) * [Schema](#schema) * [Metadata](#metadata) * [Spec Fields](#spec-fields) * [Tool Rules](#tool-rules) * [DLP Configuration](#dlp-configuration) * [Examples](#examples) * [Validation](#validation) ## Overview AIP policies are declarative YAML files that define what tools an agent can use and under what conditions. The policy is loaded at proxy startup and evaluated for every `tools/call` request. **Design principle**: Default deny. If a tool is not explicitly allowed, it's blocked. ## Schema ```yaml theme={null} apiVersion: aip.io/v1alpha1 kind: AgentPolicy metadata: name: string # Policy identifier version: string # Semantic version (optional) owner: string # Contact email (optional) signature: string # Policy signature (optional, v1alpha2) spec: mode: enforce | monitor allowed_tools: [string] tool_rules: [ToolRule] dlp: DLPConfig identity: IdentityConfig # (optional, v1alpha2) server: ServerConfig # (optional, v1alpha2) ``` ## Metadata | Field | Type | Required | Description | | ----------- | ------ | -------- | ------------------------------------------------- | | `name` | string | Yes | Unique identifier for this policy | | `version` | string | No | Semantic version (e.g., "1.0.0") | | `owner` | string | No | Contact email for policy questions | | `signature` | string | No | Ed25519 signature for policy integrity (v1alpha2) | Example: ```yaml theme={null} metadata: name: code-review-agent version: "2.1.0" owner: platform-team@company.com signature: "ed25519:YWJjZGVm..." ``` ## Spec Fields ### mode Controls whether policy violations are enforced or just logged. | Value | Behavior | | --------- | ------------------------------------------------- | | `enforce` | Block violations, return JSON-RPC error (default) | | `monitor` | Log violations but allow through (dry-run) | ```yaml theme={null} spec: mode: enforce # or "monitor" ``` **Use case for monitor mode**: Test new policies in production before enforcement. ### allowed\_tools Allowlist of tool names that the agent can invoke. Tool names must exactly match what the MCP server reports. ```yaml theme={null} spec: allowed_tools: - github_get_repo - github_list_pulls - read_file - list_directory ``` **Important**: If a tool is not in this list AND not in `tool_rules` with `action: allow`, it will be blocked. ## Tool Rules Fine-grained control over individual tools. Each rule can specify an action and argument validation. ### Structure ```yaml theme={null} spec: tool_rules: - tool: string # Tool name (required) action: string # allow | block | ask (default: allow) allow_args: object # Argument validation patterns rate_limit: string # Rate limiting (e.g., "10/minute") schema_hash: string # Tool schema integrity hash (v1alpha2) ``` ### Actions | Action | Description | | ------- | --------------------------------------------------------- | | `allow` | Permit the tool call (subject to `allow_args` validation) | | `block` | Deny unconditionally | | `ask` | Prompt user via native OS dialog for approval | ### Block Action Explicitly deny a tool: ```yaml theme={null} tool_rules: - tool: github_delete_repo action: block - tool: exec_command action: block ``` ### Ask Action (Human-in-the-Loop) Require user approval for sensitive operations: ```yaml theme={null} tool_rules: - tool: run_training action: ask - tool: deploy_production action: ask ``` When triggered: 1. Native OS dialog appears: "Allow tool 'run\_training'?" 2. User clicks "Allow" or "Deny" 3. If no response in 60 seconds: auto-deny ### Argument Validation Use `allow_args` to validate tool arguments with regex patterns: ```yaml theme={null} tool_rules: - tool: exec_command action: ask allow_args: command: "^(ls|cat|echo|pwd)\\s.*" # Only safe commands - tool: postgres_query action: allow allow_args: query: "^SELECT\\s+.*" # Only SELECT, no INSERT/UPDATE/DELETE ``` **Rules**: * Regex must match the **entire** argument value (implicit `^...$`) * If any `allow_args` pattern fails, the request is blocked * Arguments not in `allow_args` are not validated ### Rate Limiting Limit how often a tool can be called: ```yaml theme={null} tool_rules: - tool: list_gpus rate_limit: "10/minute" - tool: search_files rate_limit: "100/minute" ``` **Format**: `/` where period is `second`, `minute`, or `hour`. When rate limit is exceeded: * Request is blocked with JSON-RPC error code `-32003` * Audit log records `RATE_LIMITED` event ## Identity Configuration (v1alpha2) Configure agent identity and session management. ```yaml theme={null} spec: identity: enabled: true # Enable identity management token_ttl: "10m" # Token lifetime rotation_interval: "8m" # Rotate before expiry require_token: true # Enforce token presence session_binding: "strict" # Binding mode audience: "https://api.example.com" # Token audience ``` ### Fields | Field | Type | Default | Description | | ------------------- | -------- | --------------- | ---------------------------------- | | `enabled` | bool | `false` | Enable identity features | | `token_ttl` | duration | `"5m"` | Token time-to-live | | `rotation_interval` | duration | `"4m"` | When to rotate token | | `require_token` | bool | `false` | Block requests without valid token | | `session_binding` | string | `"process"` | `process`, `policy`, or `strict` | | `audience` | string | `metadata.name` | Token audience URI | ### Session Binding Modes | Mode | Description | Use Case | | --------- | -------------------------------- | --------------------------------- | | `process` | Binds to OS process ID | Single-machine, local agents | | `policy` | Binds to policy hash | Distributed agents sharing policy | | `strict` | Binds to process + policy + host | High security, non-ephemeral | ## Server Configuration (v1alpha2) Configure the built-in HTTP server for remote validation. ```yaml theme={null} spec: server: enabled: true listen: "127.0.0.1:9443" failover_mode: "fail_closed" tls: cert: "/path/to/cert.pem" key: "/path/to/key.pem" ``` ### Fields | Field | Type | Default | Description | | --------------- | -------- | ------------------ | ------------------------------------------ | | `enabled` | bool | `false` | Enable HTTP server | | `listen` | string | `"127.0.0.1:9443"` | Bind address | | `failover_mode` | string | `"fail_closed"` | `fail_closed`, `fail_open`, `local_policy` | | `timeout` | duration | `"5s"` | Validation timeout | ## DLP Configuration Data Loss Prevention scans tool responses for sensitive patterns and redacts matches. ### Structure ```yaml theme={null} spec: dlp: enabled: true # Optional, true when dlp block present patterns: - name: string # Rule name for audit log regex: string # Regex pattern to match ``` ### Built-in Pattern Library ```yaml theme={null} dlp: patterns: # API Keys - name: "AWS Key" regex: "(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}" - name: "GitHub Token" regex: "ghp_[a-zA-Z0-9]{36}" - name: "Generic Secret" regex: "(?i)(api_key|secret|password)\\s*[:=]\\s*['\"]?([a-zA-Z0-9-_]+)['\"]?" # PII - name: "Email" regex: "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}" - name: "SSN" regex: "\\b\\d{3}-\\d{2}-\\d{4}\\b" - name: "Credit Card" regex: "\\b(?:\\d{4}[- ]?){3}\\d{4}\\b" # Secrets - name: "Private Key" regex: "-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----" ``` ### Redaction Output Matched content is replaced with: `[REDACTED:]` ``` Before: "Connect with: AKIAIOSFODNN7EXAMPLE" After: "Connect with: [REDACTED:AWS Key]" ``` ## Examples ### Read-Only Policy ```yaml theme={null} apiVersion: aip.io/v1alpha1 kind: AgentPolicy metadata: name: read-only spec: mode: enforce allowed_tools: - read_file - list_directory - search_files tool_rules: - tool: write_file action: block - tool: delete_file action: block ``` ### GPU/ML Policy ```yaml theme={null} apiVersion: aip.io/v1alpha1 kind: AgentPolicy metadata: name: gpu-policy spec: mode: enforce allowed_tools: - list_gpus - get_gpu_metrics tool_rules: - tool: list_gpus rate_limit: "10/minute" - tool: run_training action: ask # Interactive approval - tool: allocate_gpu action: ask ``` ### Prompt Injection Defense ```yaml theme={null} apiVersion: aip.io/v1alpha1 kind: AgentPolicy metadata: name: gemini-jack-defense spec: mode: enforce allowed_tools: - read_file - search_code tool_rules: # Block all external communication - tool: send_email action: block - tool: post_slack action: block - tool: http_request action: block # Block file system writes - tool: write_file action: block - tool: exec_command action: block dlp: patterns: - name: "Exfil URL" regex: "https?://[a-zA-Z0-9.-]+\\.(ngrok|requestbin|pipedream)" ``` ### Monitor Mode (Testing) ```yaml theme={null} apiVersion: aip.io/v1alpha1 kind: AgentPolicy metadata: name: test-policy spec: mode: monitor # Log only, don't block allowed_tools: - list_files ``` ## Validation ### Policy File Validation AIP validates policies at startup. Common errors: | Error | Cause | Fix | | -------------------------------- | ---------------------------------- | ------------------------- | | `invalid apiVersion` | Wrong API version | Use `aip.io/v1alpha1` | | `empty allowed_tools` | No tools specified | Add tools or tool\_rules | | `invalid regex in allow_args` | Bad regex pattern | Validate regex syntax | | `invalid rate_limit format` | Wrong rate limit format | Use `/` | | `rotation_interval >= token_ttl` | Rotation must happen before expiry | Reduce rotation\_interval | ### Common Error Codes | Code | Name | Description | | ------ | ----------------- | ----------------------------------------------- | | -32001 | Forbidden | Tool not allowed | | -32002 | Rate Limited | Too many requests | | -32008 | Token Required | Missing identity token (v1alpha2) | | -32009 | Token Invalid | Expired or invalid token (v1alpha2) | | -32010 | Signature Invalid | Policy signature verification failed (v1alpha2) | | -32013 | Schema Mismatch | Tool definition changed (v1alpha2) | ### Testing Policies 1. **Dry run with monitor mode**: ```yaml theme={null} spec: mode: monitor ``` 2. **Check audit logs**: ```bash theme={null} cat aip-audit.jsonl | jq 'select(.violation == true)' ``` 3. **Verbose logging**: ```bash theme={null} ./aip --policy policy.yaml --target "..." --verbose ``` ## Best Practices 1. **Start restrictive**: Begin with minimal `allowed_tools`, expand as needed 2. **Use monitor mode first**: Test policies before enforcement 3. **Review audit logs**: Regularly check for unexpected tool usage 4. **Version your policies**: Use semantic versioning in metadata 5. **Document decisions**: Add comments explaining why tools are blocked 6. **Separate policies per agent type**: Different agents need different permissions # AIP v1alpha1 Source: https://agentidentityprotocol.io/specs/aip-v1alpha1 # Agent Identity Protocol (AIP) Specification **Version:** v1alpha1\ **Status:** Draft\ **Last Updated:** 2026-01-20\ **Authors:** Eduardo Arango ([arangogutierrez@gmail.com](mailto:arangogutierrez@gmail.com)) *** ## Abstract The Agent Identity Protocol (AIP) defines a standard for policy-based authorization of AI agent tool calls. AIP enables runtime environments to enforce fine-grained access control over Model Context Protocol (MCP) tool invocations, providing a security boundary between AI agents and external resources. This specification defines: 1. The policy document schema (`AgentPolicy`) 2. Evaluation semantics for authorization decisions 3. Error codes for denied requests 4. Audit log format for compliance AIP is designed to be implementation-agnostic. Any MCP-compatible runtime (Cursor, Claude Desktop, VS Code, custom implementations) can implement this specification. *** ## Table of Contents 1. [Introduction](#1-introduction) 2. [Terminology](#2-terminology) 3. [Policy Document Schema](#3-policy-document-schema) 4. [Evaluation Semantics](#4-evaluation-semantics) 5. [Error Codes](#5-error-codes) 6. [Audit Log Format](#6-audit-log-format) 7. [Conformance](#7-conformance) 8. [Security Considerations](#8-security-considerations) 9. [IANA Considerations](#9-iana-considerations) **Appendices** * [Appendix A: Complete Schema Reference](#appendix-a-complete-schema-reference) * [Appendix B: Changelog](#appendix-b-changelog) * [Appendix C: References](#appendix-c-references) * [Appendix D: Future Extensions](#appendix-d-future-extensions) * [Appendix E: Implementation Notes](#appendix-e-implementation-notes) *** ## 1. Introduction ### 1.1 Motivation AI agents operating through the Model Context Protocol (MCP) have access to powerful tools: file systems, databases, APIs, and cloud infrastructure. Without a policy layer, agents operate with unrestricted access to any tool the MCP server exposes. AIP addresses this gap by introducing: * **Capability declaration**: Explicit allowlists of permitted tools * **Argument validation**: Regex-based constraints on tool parameters * **Human-in-the-loop**: Interactive approval for sensitive operations * **Audit trail**: Immutable logging of all authorization decisions ### 1.2 Goals 1. **Interoperability**: Any MCP runtime can implement AIP 2. **Simplicity**: YAML-based policies readable by security teams 3. **Defense in depth**: Multiple layers (method, tool, argument) 4. **Fail-closed**: Unknown tools are denied by default ### 1.3 Non-Goals The following are explicitly out of scope for **this version** of the specification: * Network egress control (see [Appendix D: Future Extensions](#appendix-d-future-extensions)) * Subprocess sandboxing (implementation-defined) * Identity federation (future specification) * Rate limiting algorithms (implementation-defined) ### 1.4 Relationship to MCP AIP is designed as a security layer for MCP. It intercepts `tools/call` requests and applies policy checks before forwarding to the MCP server. ``` ┌─────────┐ ┌─────────────┐ ┌─────────────┐ │ Agent │────▶│ AIP Policy │────▶│ MCP Server │ │ │◀────│ Engine │◀────│ │ └─────────┘ └─────────────┘ └─────────────┘ ``` *** ## 2. Terminology The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). | Term | Definition | | ------------- | ------------------------------------------------------- | | **Agent** | An AI system that invokes MCP tools on behalf of a user | | **Policy** | A document specifying authorization rules (AgentPolicy) | | **Tool** | An MCP tool exposed by an MCP server | | **Decision** | The result of policy evaluation: ALLOW, BLOCK, or ASK | | **Violation** | A policy rule was triggered (may or may not block) | *** ## 3. Policy Document Schema ### 3.1 Document Structure An AIP policy document is a YAML file with the following top-level structure: ```yaml theme={null} apiVersion: aip.io/v1alpha1 kind: AgentPolicy metadata: name: version: # OPTIONAL owner: # OPTIONAL spec: mode: # OPTIONAL, default: "enforce" allowed_tools: [] # OPTIONAL allowed_methods: [] # OPTIONAL denied_methods: [] # OPTIONAL tool_rules: [] # OPTIONAL protected_paths: [] # OPTIONAL strict_args_default: # OPTIONAL, default: false dlp: # OPTIONAL ``` ### 3.2 Required Fields | Field | Type | Description | | --------------- | ------ | --------------------------------- | | `apiVersion` | string | MUST be `aip.io/v1alpha1` | | `kind` | string | MUST be `AgentPolicy` | | `metadata.name` | string | Unique identifier for this policy | ### 3.3 Metadata ```yaml theme={null} metadata: name: # REQUIRED - Policy identifier version: # OPTIONAL - Semantic version (e.g., "1.0.0") owner: # OPTIONAL - Contact email ``` ### 3.4 Spec Fields #### 3.4.1 mode Controls enforcement behavior. | Value | Behavior | | --------- | --------------------------------- | | `enforce` | Violations are blocked (default) | | `monitor` | Violations are logged but allowed | Implementations MUST support both modes. #### 3.4.2 allowed\_tools A list of tool names that the agent MAY invoke. ```yaml theme={null} allowed_tools: - github_get_repo - read_file - list_directory ``` Tool names are subject to normalization (see Section 4.1). #### 3.4.3 allowed\_methods A list of JSON-RPC methods that are permitted. If not specified, implementations MUST use the default safe list: ```yaml theme={null} # Default allowed methods (when not specified) allowed_methods: - initialize - initialized - ping - tools/call - tools/list - completion/complete - notifications/initialized - notifications/progress - notifications/message - notifications/resources/updated - notifications/resources/list_changed - notifications/tools/list_changed - notifications/prompts/list_changed - cancelled ``` The wildcard `*` MAY be used to allow all methods. #### 3.4.4 denied\_methods A list of JSON-RPC methods that are explicitly denied. Denied methods take precedence over allowed methods. ```yaml theme={null} denied_methods: - resources/read - resources/write ``` #### 3.4.5 protected\_paths A list of file paths that tools MUST NOT access. Any tool argument containing a protected path MUST be blocked. ```yaml theme={null} protected_paths: - ~/.ssh - ~/.aws/credentials - .env ``` Implementations MUST: * Expand `~` to the user's home directory * Automatically protect the policy file itself #### 3.4.6 strict\_args\_default When `true`, tool rules reject any arguments not explicitly declared in `allow_args`. Default: `false` ### 3.5 Tool Rules Tool rules provide fine-grained control over specific tools. ```yaml theme={null} tool_rules: - tool: # REQUIRED - Tool name action: # OPTIONAL - allow|block|ask (default: allow) rate_limit: # OPTIONAL - e.g., "10/minute" strict_args: # OPTIONAL - Override strict_args_default allow_args: # OPTIONAL : ``` #### 3.5.1 Actions | Action | Behavior | | ------- | --------------------------------------- | | `allow` | Permit (subject to argument validation) | | `block` | Deny unconditionally | | `ask` | Require interactive user approval | #### 3.5.2 Rate Limiting Format: `/` | Period | Aliases | | -------- | ---------- | | `second` | `sec`, `s` | | `minute` | `min`, `m` | | `hour` | `hr`, `h` | Example: `"10/minute"`, `"100/hour"`, `"5/second"` Rate limiting algorithm is implementation-defined (token bucket, sliding window, etc.). #### 3.5.3 Argument Validation The `allow_args` field maps argument names to regex patterns. ```yaml theme={null} allow_args: url: "^https://github\\.com/.*" query: "^SELECT\\s+.*" ``` Implementations MUST: * Use a regex engine with linear-time guarantees (RE2 or equivalent) * Match against the string representation of the argument value * Treat missing constrained arguments as a violation ### 3.6 DLP Configuration Data Loss Prevention (DLP) scans tool responses for sensitive data. ```yaml theme={null} dlp: enabled: # OPTIONAL, default: true when dlp block present detect_encoding: # OPTIONAL, default: false filter_stderr: # OPTIONAL, default: false patterns: - name: # REQUIRED - Rule identifier regex: # REQUIRED - Detection pattern ``` When a pattern matches, the matched content MUST be replaced with: ``` [REDACTED:] ``` *** ## 4. Evaluation Semantics ### 4.1 Name Normalization Tool names and method names MUST be normalized before comparison using the following algorithm: ``` NORMALIZE(input): 1. Apply NFKC Unicode normalization 2. Convert to lowercase 3. Trim leading/trailing whitespace 4. Remove non-printable and control characters 5. Return result ``` This prevents bypass attacks using: * Fullwidth characters: `delete` → `delete` * Ligatures: `file` → `file` * Zero-width characters: `dele​te` → `delete` ### 4.2 Method-Level Authorization Method authorization is the FIRST line of defense, evaluated BEFORE tool-level checks. ``` IS_METHOD_ALLOWED(method): normalized = NORMALIZE(method) IF normalized IN denied_methods: RETURN DENY IF "*" IN allowed_methods: RETURN ALLOW IF normalized IN allowed_methods: RETURN ALLOW RETURN DENY ``` ### 4.3 Tool-Level Authorization Tool authorization applies to `tools/call` requests. ``` IS_TOOL_ALLOWED(tool_name, arguments): normalized = NORMALIZE(tool_name) # Step 1: Check rate limiting IF rate_limiter_exceeded(normalized): RETURN RATE_LIMITED # Step 2: Check protected paths IF arguments_contain_protected_path(arguments): RETURN PROTECTED_PATH # Step 3: Check tool rules rule = find_rule(normalized) IF rule EXISTS: IF rule.action == "block": RETURN BLOCK IF rule.action == "ask": IF validate_arguments(rule, arguments): RETURN ASK ELSE: RETURN BLOCK # action == "allow" falls through # Step 4: Check allowed_tools list IF normalized NOT IN allowed_tools: RETURN BLOCK # Step 5: Validate arguments (if rule exists) IF rule EXISTS AND rule.allow_args NOT EMPTY: IF NOT validate_arguments(rule, arguments): RETURN BLOCK # Step 6: Strict args check IF strict_args_enabled(rule): IF arguments has undeclared keys: RETURN BLOCK RETURN ALLOW ``` ### 4.4 Decision Outcomes | Decision | Mode=enforce | Mode=monitor | | --------------- | --------------- | ------------------------------ | | ALLOW | Forward request | Forward request | | BLOCK | Return error | Forward request, log violation | | ASK | Prompt user | Prompt user | | RATE\_LIMITED | Return error | Return error (always enforced) | | PROTECTED\_PATH | Return error | Return error (always enforced) | ### 4.5 Argument Validation ``` VALIDATE_ARGUMENTS(rule, arguments): FOR EACH (arg_name, pattern) IN rule.allow_args: IF arg_name NOT IN arguments: RETURN FALSE # Required argument missing value = STRING(arguments[arg_name]) IF NOT REGEX_MATCH(pattern, value): RETURN FALSE RETURN TRUE ``` The `STRING()` function converts values to string representation: * String → as-is * Number → decimal representation * Boolean → "true" or "false" * Null → empty string * Array/Object → JSON serialization *** ## 5. Error Codes AIP defines the following JSON-RPC error codes: | Code | Name | Description | | ------ | ------------------ | -------------------------------- | | -32001 | Forbidden | Tool not in allowed\_tools list | | -32002 | Rate Limited | Rate limit exceeded | | -32004 | User Denied | User rejected approval prompt | | -32005 | User Timeout | Approval prompt timed out | | -32006 | Method Not Allowed | JSON-RPC method not permitted | | -32007 | Protected Path | Access to protected path blocked | ### 5.1 Error Response Format ```json theme={null} { "jsonrpc": "2.0", "id": , "error": { "code": , "message": "", "data": { "tool": "", "reason": "" } } } ``` ### 5.2 Error Code Details #### -32001 Forbidden Returned when a tool is not in the `allowed_tools` list and has no `tool_rules` entry with `action: allow`. ```json theme={null} { "code": -32001, "message": "Forbidden", "data": { "tool": "dangerous_tool", "reason": "Tool not in allowed_tools list" } } ``` #### -32002 Rate Limited Returned when a tool's rate limit is exceeded. ```json theme={null} { "code": -32002, "message": "Rate limit exceeded", "data": { "tool": "list_gpus", "reason": "Rate limit exceeded for list_gpus. Try again later." } } ``` *** ## 6. Audit Log Format Implementations SHOULD log all authorization decisions in JSON Lines format. ### 6.1 Required Fields | Field | Type | Description | | ------------- | -------- | ---------------------------------------------------------- | | `timestamp` | ISO 8601 | Time of the decision | | `direction` | string | `upstream` (client→server) or `downstream` (server→client) | | `decision` | string | `ALLOW`, `BLOCK`, `ALLOW_MONITOR`, `RATE_LIMITED` | | `policy_mode` | string | `enforce` or `monitor` | | `violation` | boolean | Whether a policy violation was detected | ### 6.2 Optional Fields | Field | Type | Description | | ------------- | ------ | ----------------------------------- | | `method` | string | JSON-RPC method name | | `tool` | string | Tool name (for tools/call) | | `args` | object | Tool arguments (SHOULD be redacted) | | `failed_arg` | string | Argument that failed validation | | `failed_rule` | string | Regex pattern that failed | ### 6.3 Example ```json theme={null} { "timestamp": "2026-01-20T10:30:45.123Z", "direction": "upstream", "method": "tools/call", "tool": "delete_file", "args": {"path": "/etc/passwd"}, "decision": "BLOCK", "policy_mode": "enforce", "violation": true, "failed_arg": "path", "failed_rule": "^/home/.*" } ``` ### 6.4 DLP Events DLP redaction events SHOULD be logged separately: ```json theme={null} { "timestamp": "2026-01-20T10:30:45.123Z", "direction": "downstream", "event": "DLP_TRIGGERED", "dlp_rule": "AWS Key", "dlp_action": "REDACTED", "dlp_match_count": 2 } ``` *** ## 7. Conformance ### 7.1 Conformance Levels | Level | Requirements | | ------------ | -------------------------------------------------------------- | | **Basic** | Method authorization, tool allowlist, error codes | | **Full** | Basic + argument validation, rate limiting, DLP, audit logging | | **Extended** | Full + Human-in-the-Loop (action=ask) | ### 7.2 Conformance Testing Implementations MUST pass the conformance test suite to claim AIP compliance. The test suite consists of: 1. **Schema validation tests**: Verify policy parsing 2. **Decision tests**: Input → expected decision 3. **Normalization tests**: Verify Unicode handling 4. **Error format tests**: Verify JSON-RPC errors See `spec/conformance/` for test vectors. ### 7.3 Implementation Requirements Implementations MUST: * Parse `apiVersion: aip.io/v1alpha1` documents * Reject documents with unknown `apiVersion` * Apply NFKC normalization to names * Return specified error codes * Support `enforce` and `monitor` modes Implementations SHOULD: * Log decisions in the specified format * Support DLP scanning * Support rate limiting Implementations MAY: * Use any regex engine with RE2 semantics * Implement additional security features (egress control, sandboxing) *** ## 8. Security Considerations ### 8.1 Policy File Protection The policy file itself MUST be protected from modification by the agent. Implementations MUST automatically add the policy file path to `protected_paths`. ### 8.2 Regex Denial of Service (ReDoS) Implementations MUST use a regex engine that guarantees linear-time matching (RE2 or equivalent). Pathological patterns like `(a+)+$` MUST NOT cause exponential execution time. ### 8.3 Unicode Normalization Implementations MUST apply NFKC normalization to prevent homoglyph attacks. However, implementers should be aware that NFKC does not normalize all visually similar characters (e.g., Cyrillic 'а' vs Latin 'a'). ### 8.4 Monitor Mode Risks Monitor mode allows all requests through. Implementations SHOULD warn users when monitor mode is enabled in production environments. ### 8.5 Audit Log Integrity Audit logs SHOULD be written to a location not writable by the agent. Implementations MAY support log signing or forwarding to external systems. *** ## 9. IANA Considerations This specification requests registration of the following: ### 9.1 Media Type * Type name: application * Subtype name: vnd.aip.policy+yaml * Required parameters: None * File extension: .yaml, .yml ### 9.2 URI Scheme This specification uses the `aip.io` namespace for versioning: * `aip.io/v1alpha1` - This specification *** ## Appendix A: Complete Schema Reference ```yaml theme={null} # Complete AgentPolicy schema apiVersion: aip.io/v1alpha1 # REQUIRED kind: AgentPolicy # REQUIRED metadata: # REQUIRED name: string # REQUIRED - Policy identifier version: string # OPTIONAL - Semantic version owner: string # OPTIONAL - Contact email spec: # REQUIRED mode: enforce | monitor # OPTIONAL, default: enforce allowed_tools: # OPTIONAL - string allowed_methods: # OPTIONAL - string denied_methods: # OPTIONAL - string protected_paths: # OPTIONAL - string strict_args_default: boolean # OPTIONAL, default: false tool_rules: # OPTIONAL - tool: string # REQUIRED action: allow|block|ask # OPTIONAL, default: allow rate_limit: string # OPTIONAL, format: "N/period" strict_args: boolean # OPTIONAL allow_args: # OPTIONAL : dlp: # OPTIONAL enabled: boolean # OPTIONAL, default: true detect_encoding: boolean # OPTIONAL, default: false filter_stderr: boolean # OPTIONAL, default: false patterns: # REQUIRED if dlp present - name: string # REQUIRED regex: string # REQUIRED ``` *** ## Appendix B: Changelog ### v1alpha1 (2026-01-20) * Initial draft specification * Defined core policy schema * Defined evaluation semantics * Defined error codes * Defined audit log format *** ## Appendix C: References * [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) * [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification) * [RFC 2119 - Key words for use in RFCs](https://www.rfc-editor.org/rfc/rfc2119) * [Unicode NFKC Normalization](https://unicode.org/reports/tr15/) * [RE2 Syntax](https://github.com/google/re2/wiki/Syntax) *** ## Appendix D: Future Extensions This appendix describes features under consideration for future versions of AIP. ### D.1 Network Egress Control **Status:** Proposed for v1beta1 #### Motivation Tool-level authorization prevents agents from calling dangerous tools, but a compromised or malicious MCP server can still exfiltrate data through: * Outbound HTTP requests embedded in tool implementations * DNS exfiltration * Covert channels in allowed network traffic Egress control would allow policies to restrict which network destinations an MCP server (or its subprocesses) can reach. #### Proposed Schema Extension ```yaml theme={null} apiVersion: aip.io/v1beta1 # Future version kind: AgentPolicy metadata: name: egress-example spec: # ... existing fields ... egress: mode: block | allow | monitor # Default: allow (no restriction) allowed_hosts: - "api.github.com" - "*.openai.com" - "10.0.0.0/8" # CIDR notation denied_hosts: - "*.ngrok.io" - "*.requestbin.com" allowed_ports: - 443 - 80 denied_ports: - 22 - 3389 ``` #### Implementation Considerations Egress control is inherently platform-specific: | Platform | Mechanism | Limitations | | ------------------ | ------------------------------------- | -------------------------- | | **Linux** | eBPF, seccomp-bpf, network namespaces | Requires CAP\_BPF or root | | **macOS** | Network Extension, sandbox-exec | Requires entitlements | | **Windows** | Windows Filtering Platform (WFP) | Requires admin | | **Container** | `--network=none`, network policies | Requires container runtime | | **Cross-platform** | DNS-based filtering, HTTP proxy | Bypassable, incomplete | Implementations MAY support egress control through any mechanism appropriate for their platform. The specification will define the **policy schema** and **expected behavior**, not the enforcement mechanism. #### Open Questions 1. Should egress rules be per-tool or global? 2. How to handle DNS resolution (allow DNS but block resolved IP)? 3. Should there be a "learning mode" to auto-generate allowlists? 4. How to handle localhost connections (MCP servers often bind locally)? ### D.2 Identity Federation **Status:** Under Discussion Allow policies to reference external identity providers: ```yaml theme={null} spec: identity: provider: "oidc" issuer: "https://accounts.google.com" required_claims: email_verified: true hd: "company.com" ``` ### D.3 Policy Inheritance **Status:** Under Discussion Allow policies to extend base policies: ```yaml theme={null} apiVersion: aip.io/v1beta1 kind: AgentPolicy metadata: name: team-policy spec: extends: "org-base-policy" # Inherit from another policy allowed_tools: - additional_tool # Add to parent's list ``` ### D.4 Telemetry and Metrics **Status:** Under Discussion Standardized metrics export for observability: ```yaml theme={null} spec: telemetry: metrics: endpoint: "http://prometheus:9090/metrics" format: "prometheus" traces: endpoint: "http://jaeger:14268/api/traces" format: "otlp" ``` *** ## Appendix E: Implementation Notes This appendix provides guidance for implementers. ### E.1 Reference Implementation The reference implementation is available at: [https://github.com/ArangoGutierrez/agent-identity-protocol](https://github.com/ArangoGutierrez/agent-identity-protocol) It provides: * Go-based proxy (`aip-proxy`) * Policy engine (`pkg/policy`) * DLP scanner (`pkg/dlp`) * Audit logger (`pkg/audit`) ### E.2 Testing Against Conformance Suite ```bash theme={null} # Clone the spec repository git clone https://github.com/ArangoGutierrez/agent-identity-protocol # Run conformance tests against your implementation cd agent-identity-protocol/spec/conformance ./run-tests.sh --impl "your-aip-binary" ``` ### E.3 Registering Your Implementation Implementations that pass the conformance suite may be listed in the official registry. Submit a PR to the AIP repository with: * Implementation name and URL * Conformance level achieved (Basic/Full/Extended) * Platform support matrix # AIP v1alpha2 Source: https://agentidentityprotocol.io/specs/aip-v1alpha2 # Agent Identity Protocol (AIP) Specification **Version:** v1alpha2\ **Status:** Draft\ **Last Updated:** 2026-01-24\ **Authors:** Eduardo Arango ([arangogutierrez@gmail.com](mailto:arangogutierrez@gmail.com)) *** ## Abstract The Agent Identity Protocol (AIP) defines a standard for policy-based authorization of AI agent tool calls. AIP enables runtime environments to enforce fine-grained access control over Model Context Protocol (MCP) tool invocations, providing a security boundary between AI agents and external resources. This specification defines: 1. The policy document schema (`AgentPolicy`) 2. Evaluation semantics for authorization decisions 3. **Agent identity and session management** *(new in v1alpha2)* 4. **Server-side validation endpoints** *(new in v1alpha2)* 5. Error codes for denied requests 6. Audit log format for compliance AIP is designed to be implementation-agnostic. Any MCP-compatible runtime (Cursor, Claude Desktop, VS Code, custom implementations) can implement this specification. *** ## Table of Contents 1. [Introduction](#1-introduction) 2. [Terminology](#2-terminology) 3. [Policy Document Schema](#3-policy-document-schema) 4. [Evaluation Semantics](#4-evaluation-semantics) 5. [Agent Identity](#5-agent-identity) *(new in v1alpha2)* 6. [Server-Side Validation](#6-server-side-validation) *(new in v1alpha2)* 7. [Error Codes](#7-error-codes) 8. [Audit Log Format](#8-audit-log-format) 9. [Conformance](#9-conformance) 10. [Security Considerations](#10-security-considerations) 11. [IANA Considerations](#11-iana-considerations) **Appendices** * [Appendix A: Complete Schema Reference](#appendix-a-complete-schema-reference) * [Appendix B: Changelog](#appendix-b-changelog) * [Appendix C: References](#appendix-c-references) * [Appendix D: Future Extensions](#appendix-d-future-extensions) * [Appendix E: Implementation Notes](#appendix-e-implementation-notes) *** ## 1. Introduction ### 1.1 Motivation AI agents operating through the Model Context Protocol (MCP) have access to powerful tools: file systems, databases, APIs, and cloud infrastructure. Without a policy layer, agents operate with unrestricted access to any tool the MCP server exposes. AIP addresses this gap by introducing: * **Capability declaration**: Explicit allowlists of permitted tools * **Argument validation**: Regex-based constraints on tool parameters * **Human-in-the-loop**: Interactive approval for sensitive operations * **Audit trail**: Immutable logging of all authorization decisions * **Agent identity**: Cryptographic binding of policies to agent sessions *(new in v1alpha2)* * **Server-side validation**: Optional HTTP endpoints for distributed policy enforcement *(new in v1alpha2)* ### 1.2 Goals 1. **Interoperability**: Any MCP runtime can implement AIP 2. **Simplicity**: YAML-based policies readable by security teams 3. **Defense in depth**: Multiple layers (method, tool, argument, identity) 4. **Fail-closed**: Unknown tools are denied by default 5. **Zero-trust ready**: Support for token-based identity verification *(new in v1alpha2)* ### 1.3 Non-Goals The following are explicitly out of scope for **this version** of the specification: * Network egress control (see [Appendix D: Future Extensions](#appendix-d-future-extensions)) * Subprocess sandboxing (implementation-defined) * External identity federation (OIDC/SPIFFE - see [Appendix D](#d3-external-identity-federation)) * Rate limiting algorithms (implementation-defined) * Policy expression languages beyond regex (CEL/Rego - see [Appendix D](#d5-advanced-policy-expressions)) ### 1.4 Relationship to MCP AIP is designed as a security layer for MCP. It intercepts `tools/call` requests and applies policy checks before forwarding to the MCP server. ``` ┌─────────┐ ┌─────────────┐ ┌─────────────┐ │ Agent │────▶│ AIP Policy │────▶│ MCP Server │ │ │◀────│ Engine │◀────│ │ └─────────┘ └─────────────┘ └─────────────┘ │ ▼ ┌─────────────┐ │ AIP Server │ (optional, v1alpha2) │ Endpoint │ └─────────────┘ ``` ### 1.5 Relationship to MCP Authorization MCP defines an optional OAuth 2.1-based authorization layer (MCP 2025-06-18 and later). AIP is **complementary** to MCP authorization: | Concern | MCP Authorization | AIP | | -------------------- | ------------------------------ | ------------------------------ | | **Scope** | Transport-level authentication | Tool-level authorization | | **What it protects** | Access to MCP server | Access to specific tools | | **Token type** | OAuth 2.1 access tokens | AIP Identity Tokens (optional) | | **Policy language** | OAuth scopes | YAML policy documents | Implementations MAY use both MCP authorization (for server access) and AIP (for tool access) simultaneously. *** ## 2. Terminology The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). | Term | Definition | | ------------------ | ------------------------------------------------------------------- | | **Agent** | An AI system that invokes MCP tools on behalf of a user | | **Policy** | A document specifying authorization rules (AgentPolicy) | | **Tool** | An MCP tool exposed by an MCP server | | **Decision** | The result of policy evaluation: ALLOW, BLOCK, or ASK | | **Violation** | A policy rule was triggered (may or may not block) | | **Session** | A bounded period of agent activity with consistent identity *(new)* | | **Identity Token** | A cryptographic token binding policy to session *(new)* | | **Policy Hash** | SHA-256 hash of the canonical policy document *(new)* | *** ## 3. Policy Document Schema ### 3.1 Document Structure An AIP policy document is a YAML file with the following top-level structure: ```yaml theme={null} apiVersion: aip.io/v1alpha2 kind: AgentPolicy metadata: name: version: # OPTIONAL owner: # OPTIONAL signature: # OPTIONAL (v1alpha2) spec: mode: # OPTIONAL, default: "enforce" allowed_tools: [] # OPTIONAL allowed_methods: [] # OPTIONAL denied_methods: [] # OPTIONAL tool_rules: [] # OPTIONAL protected_paths: [] # OPTIONAL strict_args_default: # OPTIONAL, default: false dlp: # OPTIONAL identity: # OPTIONAL (v1alpha2) server: # OPTIONAL (v1alpha2) ``` ### 3.2 Required Fields | Field | Type | Description | | --------------- | ------ | --------------------------------- | | `apiVersion` | string | MUST be `aip.io/v1alpha2` | | `kind` | string | MUST be `AgentPolicy` | | `metadata.name` | string | Unique identifier for this policy | ### 3.3 Metadata ```yaml theme={null} metadata: name: # REQUIRED - Policy identifier version: # OPTIONAL - Semantic version (e.g., "1.0.0") owner: # OPTIONAL - Contact email signature: # OPTIONAL - Policy signature (v1alpha2) ``` #### 3.3.1 Policy Signature (v1alpha2) The `signature` field provides cryptographic integrity verification for the policy document. Format: `:` Supported algorithms: * `ed25519` - Ed25519 signature (RECOMMENDED) Example: ```yaml theme={null} metadata: name: production-agent signature: "ed25519:YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo..." ``` When present, implementations MUST verify the signature before applying the policy. Signature verification failure MUST result in policy rejection. The signature is computed over the **canonical form** of the policy document (see Section 5.2.1). ### 3.4 Spec Fields *\[Sections 3.4.1 through 3.6 remain unchanged from v1alpha1]* #### 3.4.1 mode Controls enforcement behavior. | Value | Behavior | | --------- | --------------------------------- | | `enforce` | Violations are blocked (default) | | `monitor` | Violations are logged but allowed | Implementations MUST support both modes. #### 3.4.2 allowed\_tools A list of tool names that the agent MAY invoke. ```yaml theme={null} allowed_tools: - github_get_repo - read_file - list_directory ``` Tool names are subject to normalization (see Section 4.1). #### 3.4.3 allowed\_methods A list of JSON-RPC methods that are permitted. If not specified, implementations MUST use the default safe list: ```yaml theme={null} # Default allowed methods (when not specified) allowed_methods: - initialize - initialized - ping - tools/call - tools/list - completion/complete - notifications/initialized - notifications/progress - notifications/message - notifications/resources/updated - notifications/resources/list_changed - notifications/tools/list_changed - notifications/prompts/list_changed - cancelled ``` The wildcard `*` MAY be used to allow all methods. #### 3.4.4 denied\_methods A list of JSON-RPC methods that are explicitly denied. Denied methods take precedence over allowed methods. ```yaml theme={null} denied_methods: - resources/read - resources/write ``` #### 3.4.5 protected\_paths A list of file paths that tools MUST NOT access. Any tool argument containing a protected path MUST be blocked. ```yaml theme={null} protected_paths: - ~/.ssh - ~/.aws/credentials - .env ``` Implementations MUST: * Expand `~` to the user's home directory * Automatically protect the policy file itself #### 3.4.6 strict\_args\_default When `true`, tool rules reject any arguments not explicitly declared in `allow_args`. Default: `false` ### 3.5 Tool Rules Tool rules provide fine-grained control over specific tools. ```yaml theme={null} tool_rules: - tool: # REQUIRED - Tool name action: # OPTIONAL - allow|block|ask (default: allow) rate_limit: # OPTIONAL - e.g., "10/minute" strict_args: # OPTIONAL - Override strict_args_default schema_hash: # OPTIONAL - Tool schema integrity (v1alpha2) allow_args: # OPTIONAL : ``` #### 3.5.1 Actions | Action | Behavior | | ------- | --------------------------------------- | | `allow` | Permit (subject to argument validation) | | `block` | Deny unconditionally | | `ask` | Require interactive user approval | #### 3.5.2 Rate Limiting Format: `/` | Period | Aliases | | -------- | ---------- | | `second` | `sec`, `s` | | `minute` | `min`, `m` | | `hour` | `hr`, `h` | Example: `"10/minute"`, `"100/hour"`, `"5/second"` Rate limiting algorithm is implementation-defined (token bucket, sliding window, etc.). #### 3.5.3 Argument Validation The `allow_args` field maps argument names to regex patterns. ```yaml theme={null} allow_args: url: "^https://github\\.com/.*" query: "^SELECT\\s+.*" ``` Implementations MUST: * Use a regex engine with linear-time guarantees (RE2 or equivalent) * Match against the string representation of the argument value * Treat missing constrained arguments as a violation #### 3.5.4 Tool Schema Hashing (v1alpha2) The `schema_hash` field provides cryptographic verification of tool definitions to prevent tool poisoning attacks. **Format**: `:` **Supported algorithms**: * `sha256` (RECOMMENDED) * `sha384` * `sha512` **Example**: ```yaml theme={null} tool_rules: - tool: read_file action: allow schema_hash: "sha256:a3c7f2e8d9b4f1e2c8a7d6f3e9b2c4f1a8e7d3c2b5f4e9a7c3d8f2b6e1a9c4f7" allow_args: path: "^/home/.*" ``` **Hash computation**: The schema hash is computed over the canonical form of the tool's MCP schema: ``` TOOL_SCHEMA_HASH(tool): schema = { "name": tool.name, "description": tool.description, "inputSchema": tool.inputSchema # JSON Schema for arguments } canonical = JSON_CANONICALIZE(schema) # RFC 8785 hash = SHA256(canonical) RETURN "sha256:" + hex_encode(hash) ``` **Behavior**: | Condition | Behavior | | -------------------- | --------------------------------------------- | | `schema_hash` absent | No schema verification (backward compatible) | | Hash matches | Tool allowed (proceed to argument validation) | | Hash mismatch | Tool BLOCKED with error -32013 | | Tool not found | Tool BLOCKED with error -32001 | **Use cases**: 1. **Tool poisoning prevention**: Detect when an MCP server changes a tool's behavior after policy approval 2. **Compliance auditing**: Prove that approved tools haven't been modified 3. **Supply chain security**: Pin specific tool versions in policy **Generating schema hashes**: ```bash theme={null} # Using the AIP CLI (reference implementation) aip-proxy schema-hash --server mcp://localhost:8080 --tool read_file # Output: sha256:a3c7f2e8... # Or from tools/list response aip-proxy schema-hash --tools-file tools.json --tool read_file ``` **Operational considerations**: * Schema hashes MUST be regenerated when MCP server is updated * Implementations SHOULD log hash mismatches with both expected and actual hashes * Policy authors SHOULD document which tool version the hash corresponds to **Error code** (new): | Code | Name | Description | | ------ | --------------- | ---------------------------------------------- | | -32013 | Schema Mismatch | Tool schema hash does not match policy *(new)* | ### 3.6 DLP Configuration Data Loss Prevention (DLP) scans for sensitive data in requests and responses. ```yaml theme={null} dlp: enabled: # OPTIONAL, default: true when dlp block present scan_requests: # OPTIONAL, default: false (v1alpha2) scan_responses: # OPTIONAL, default: true detect_encoding: # OPTIONAL, default: false filter_stderr: # OPTIONAL, default: false max_scan_size: # OPTIONAL, default: "1MB" (v1alpha2) on_request_match: # OPTIONAL, default: "block" (v1alpha2) patterns: - name: # REQUIRED - Rule identifier regex: # REQUIRED - Detection pattern scope: # OPTIONAL, default: "all" (request|response|all) ``` #### 3.6.1 scan\_requests (v1alpha2) When `true`, DLP patterns are applied to tool arguments before the request is forwarded. Default: `false` (backward compatible) **Use case**: Prevents data exfiltration via arguments (e.g., embedding secrets in API queries). #### 3.6.2 scan\_responses When `true`, DLP patterns are applied to tool responses. Default: `true` #### 3.6.3 max\_scan\_size (v1alpha2) Maximum size of content to scan per request/response. Format: Size string (e.g., `"1MB"`, `"512KB"`, `"10MB"`) Default: `"1MB"` Content exceeding this limit: * SHOULD be truncated for scanning (scan first `max_scan_size` bytes) * MUST log a warning **Purpose**: Prevents ReDoS and memory exhaustion on large payloads. #### 3.6.4 on\_request\_match (v1alpha2) Action when DLP pattern matches in a request (when `scan_requests: true`). | Value | Behavior | | -------- | ---------------------------------------------- | | `block` | Reject the request with error -32001 (default) | | `redact` | Replace matched content and forward | | `warn` | Log warning and forward unchanged | Default: `block` **Security note**: `redact` for requests may produce invalid tool arguments. Use with caution. **Redaction failure handling (v1alpha2)**: When `on_request_match: "redact"` is configured, redacted content may cause downstream failures: 1. **Invalid JSON**: Redaction in nested structures may break JSON parsing 2. **Schema validation failure**: Redacted values may violate tool argument schemas 3. **Tool execution failure**: The MCP server may reject redacted arguments **Configuration for redaction failure behavior**: ```yaml theme={null} dlp: scan_requests: true on_request_match: "redact" on_redaction_failure: # OPTIONAL, default: "block" (v1alpha2) log_original_on_failure: # OPTIONAL, default: false (v1alpha2) ``` | Field | Type | Description | | ------------------------- | ------ | ----------------------------------------------------------------------- | | `on_redaction_failure` | string | Action when redacted request fails: `block`, `allow_original`, `reject` | | `log_original_on_failure` | bool | Log pre-redaction content for forensics (sensitive!) | **on\_redaction\_failure values**: | Value | Behavior | Security | Use Case | | ---------------- | ----------------------------- | -------- | ----------------- | | `block` | Block with -32001 (default) | High | Production | | `allow_original` | Forward original unredacted | Low | Debug only | | `reject` | Block with -32014 (new error) | High | Strict compliance | **Example configuration**: ```yaml theme={null} dlp: scan_requests: true on_request_match: "redact" on_redaction_failure: "block" log_original_on_failure: true # For forensic analysis patterns: - name: "API Key" regex: "sk-[a-zA-Z0-9]{32}" scope: "request" ``` **Error code for redaction failures** (new): | Code | Name | Description | | ------ | -------------------- | -------------------------------------------------- | | -32014 | DLP Redaction Failed | Request redaction produced invalid content *(new)* | **Example error response**: ```json theme={null} { "code": -32014, "message": "DLP redaction failed", "data": { "tool": "http_request", "reason": "Redacted request failed argument validation", "dlp_rule": "API Key", "validation_error": "url: expected string, got [REDACTED:API Key]" } } ``` **Audit logging for redaction events**: ```json theme={null} { "timestamp": "2026-01-24T10:30:45.123Z", "event": "DLP_REQUEST_REDACTION", "tool": "http_request", "dlp_rule": "API Key", "redaction_count": 1, "forwarded": false, "failure_reason": "argument_validation_failed" } ``` ⚠️ **Security consideration**: Setting `log_original_on_failure: true` will log sensitive data that DLP attempted to redact. This SHOULD only be enabled: * In development environments * With appropriate log access controls * For time-limited forensic investigations #### 3.6.5 Pattern Scope (v1alpha2) Patterns can be scoped to requests, responses, or both: ```yaml theme={null} patterns: - name: "AWS Key" regex: "AKIA[0-9A-Z]{16}" scope: "all" # Scan both requests and responses - name: "SQL Injection" regex: "(?i)(DROP|DELETE|TRUNCATE)\\s+TABLE" scope: "request" # Only scan requests (detect exfiltration attempts) - name: "SSN" regex: "\\d{3}-\\d{2}-\\d{4}" scope: "response" # Only scan responses (PII protection) ``` When a pattern matches, the matched content MUST be replaced with: ``` [REDACTED:] ``` ### 3.7 Identity Configuration (v1alpha2) The `identity` section configures agent identity and token management. ```yaml theme={null} spec: identity: enabled: # OPTIONAL, default: false token_ttl: # OPTIONAL, default: "5m" rotation_interval: # OPTIONAL, default: "4m" require_token: # OPTIONAL, default: false session_binding: # OPTIONAL, default: "process" nonce_window: # OPTIONAL, default: equals token_ttl (v1alpha2) policy_transition_grace: # OPTIONAL, default: "0s" (v1alpha2) audience: # OPTIONAL, default: policy metadata.name (v1alpha2) nonce_storage: # OPTIONAL (v1alpha2) keys: # OPTIONAL (v1alpha2) ``` #### 3.7.1 enabled When `true`, the AIP engine generates and manages identity tokens for the session. Default: `false` #### 3.7.2 token\_ttl The time-to-live for identity tokens. Format: Go duration string (e.g., `"5m"`, `"1h"`, `"300s"`) Default: `"5m"` (5 minutes) Implementations SHOULD use short TTLs (5-15 minutes) to limit token theft window. #### 3.7.3 rotation\_interval How often to rotate tokens before expiry. Format: Go duration string Default: `"4m"` (4 minutes, ensuring rotation before 5m TTL) **Constraint**: `rotation_interval` MUST be less than `token_ttl`. **Validation behavior (v1alpha2)**: When loading a policy, implementations MUST validate the rotation\_interval constraint: ``` VALIDATE_ROTATION_INTERVAL(config): IF config.rotation_interval >= config.token_ttl: RETURN ERROR("rotation_interval must be less than token_ttl") # Recommended: rotation should leave grace period for in-flight requests IF config.rotation_interval > (config.token_ttl * 0.9): LOG_WARNING("rotation_interval very close to token_ttl; consider reducing") RETURN OK ``` **Error handling**: | Condition | Behavior | Error | | ------------------------------------- | -------------------- | ------------------- | | `rotation_interval >= token_ttl` | Reject policy | Policy load failure | | `rotation_interval > token_ttl * 0.9` | Warn, allow | Log warning | | `rotation_interval` not specified | Use default (`"4m"`) | - | | `rotation_interval: "0s"` | Disable rotation | - | **Invalid configuration example**: ```yaml theme={null} # INVALID: rotation_interval >= token_ttl identity: enabled: true token_ttl: "5m" rotation_interval: "6m" # ERROR: must be < 5m ``` **Policy load error response**: ```json theme={null} { "error": "policy_validation_failed", "message": "rotation_interval (6m) must be less than token_ttl (5m)", "field": "spec.identity.rotation_interval" } ``` **Recommended configurations**: | Use Case | `token_ttl` | `rotation_interval` | Rationale | | ------------- | ----------- | ------------------- | ---------------------------- | | Default | `"5m"` | `"4m"` | 1 minute grace for in-flight | | High-security | `"5m"` | `"2m"` | More frequent rotation | | Low-latency | `"1m"` | `"45s"` | Minimal token lifetime | | Long-lived | `"1h"` | `"50m"` | 10 minute grace | **Disabling rotation**: Setting `rotation_interval: "0s"` disables automatic rotation. Tokens will only be refreshed when explicitly requested or when they expire. ```yaml theme={null} identity: enabled: true token_ttl: "5m" rotation_interval: "0s" # No automatic rotation ``` ⚠️ **Not recommended** for production as it increases token theft window. #### 3.7.4 require\_token When `true`, all tool calls MUST include a valid identity token. Calls without tokens are rejected with error code -32008. Default: `false` This enables gradual rollout: start with `require_token: false` to generate tokens without enforcement, then enable enforcement. #### 3.7.5 session\_binding Determines what context is bound to the session identity. | Value | Binding | | --------- | --------------------------------------------- | | `process` | Session bound to process ID (default) | | `policy` | Session bound to policy hash | | `strict` | Session bound to process + policy + timestamp | #### 3.7.6 nonce\_window The duration to retain nonces for replay detection. Format: Go duration string Default: Equals `token_ttl` (e.g., `"5m"` if token\_ttl is `"5m"`) **Purpose**: Bounds the storage required for replay prevention. Nonces older than `nonce_window` MAY be pruned from storage. **Constraints**: * `nonce_window` MUST be greater than or equal to `token_ttl` * Setting `nonce_window` less than `token_ttl` is a configuration error **Storage considerations**: | Deployment | Recommended `nonce_window` | | ------------------------------- | ---------------------------------- | | Single instance | `token_ttl` (default) | | Multi-instance (shared storage) | `token_ttl + clock_skew_tolerance` | | High-security | `2 * token_ttl` | Example: ```yaml theme={null} identity: enabled: true token_ttl: "5m" nonce_window: "10m" # Retain nonces for 2x TTL ``` #### 3.7.7 policy\_transition\_grace The grace period during which tokens issued with the previous policy hash remain valid after a policy update. Format: Go duration string Default: `"0s"` (no grace period - strict policy enforcement) **Purpose**: Allows gradual policy rollouts without invalidating all in-flight tokens immediately. **Behavior**: 1. When policy is updated, the previous policy hash is retained in `recent_policy_hashes` 2. Tokens with either current or recent policy hash are accepted during the grace period 3. After grace period expires, only current policy hash is valid **Constraints**: * `policy_transition_grace` SHOULD be less than `token_ttl` to ensure policy changes take effect within one token lifetime * Setting very long grace periods weakens security guarantees **Example**: ```yaml theme={null} identity: enabled: true token_ttl: "5m" policy_transition_grace: "2m" # Accept old policy hash for 2 minutes ``` **Use cases**: | Scenario | Recommended Setting | | ---------------------------- | -------------------------------------------- | | Development | `"0s"` - Immediate policy updates | | Production (single instance) | `"30s"` - Brief grace for in-flight requests | | Production (distributed) | `"2m"` - Allow for propagation delay | | Canary deployments | Equal to deployment window | #### 3.7.8 audience (v1alpha2) The intended audience for identity tokens. This value is included in the token's `aud` claim and MUST be validated by recipients. Format: URI string identifying the MCP server or service Default: Value of `metadata.name` **Purpose**: Prevents tokens issued for one MCP server from being accepted by another. This is critical for: * Multi-tenant deployments where agents access multiple MCP servers * Defense against token theft and replay across services * Compliance with OAuth 2.1 audience binding requirements (RFC 8707) **Example**: ```yaml theme={null} identity: enabled: true audience: "https://mcp.example.com/api" ``` **Validation requirements**: * Implementations MUST reject tokens where `aud` does not match the expected audience * When `server.enabled: true`, the audience SHOULD be the server's canonical URL * Wildcards are NOT permitted in audience values **Constraints**: * `audience` MUST be a valid URI or the policy `metadata.name` * Empty string is NOT valid; use default (metadata.name) instead #### 3.7.9 nonce\_storage (v1alpha2) Configuration for distributed nonce storage, required for multi-instance deployments. ```yaml theme={null} spec: identity: nonce_storage: type: # OPTIONAL, default: "memory" address: # REQUIRED if type != "memory" key_prefix: # OPTIONAL, default: "aip:nonce:" clock_skew_tolerance: # OPTIONAL, default: "30s" ``` | Field | Type | Description | | ---------------------- | -------- | ---------------------------------------------- | | `type` | string | Storage backend: `memory`, `redis`, `postgres` | | `address` | string | Connection string for external storage | | `key_prefix` | string | Prefix for nonce keys (namespacing) | | `clock_skew_tolerance` | duration | Added to TTL to handle clock drift | **Storage type requirements**: | Type | Atomicity | Persistence | Multi-instance | Use Case | | ---------- | ------------ | ----------- | -------------- | ---------------------------- | | `memory` | ✅ (sync.Map) | ❌ | ❌ | Development, single-instance | | `redis` | ✅ (SET NX) | ✅ | ✅ | Production (RECOMMENDED) | | `postgres` | ✅ (UNIQUE) | ✅ | ✅ | Production with existing DB | **Example configurations**: ```yaml theme={null} # Single instance (default) identity: enabled: true nonce_storage: type: "memory" # Redis cluster identity: enabled: true nonce_storage: type: "redis" address: "redis://redis-cluster:6379" key_prefix: "prod:aip:nonce:" clock_skew_tolerance: "30s" # PostgreSQL identity: enabled: true nonce_storage: type: "postgres" address: "postgres://user:pass@db:5432/aip?sslmode=require" key_prefix: "nonces_" ``` ⚠️ **Multi-instance deployments**: Using `type: "memory"` with multiple AIP instances is a **security vulnerability** that allows cross-instance replay attacks. Implementations SHOULD warn when `memory` storage is detected in environments with multiple instances. ### 3.8 Server Configuration (v1alpha2) The `server` section configures optional HTTP endpoints for server-side validation. ```yaml theme={null} spec: server: enabled: # OPTIONAL, default: false listen: # OPTIONAL, default: "127.0.0.1:9443" failover_mode: # OPTIONAL, default: "fail_closed" (v1alpha2) timeout: # OPTIONAL, default: "5s" (v1alpha2) tls: # OPTIONAL cert: # Path to TLS certificate key: # Path to TLS private key endpoints: # OPTIONAL validate: # Validation endpoint path (default: "/v1/validate") revoke: # Revocation endpoint path (default: "/v1/revoke") health: # Health check path (default: "/health") metrics: # Metrics endpoint path (default: "/metrics") ``` #### 3.8.1 enabled When `true`, the AIP engine starts an HTTP server for remote validation. Default: `false` #### 3.8.2 listen The address and port to bind the HTTP server. Format: `:` or `:` Default: `"127.0.0.1:9443"` (localhost only) ⚠️ **Security**: Binding to `0.0.0.0` exposes the validation endpoint to the network. Implementations MUST require TLS when listen address is not localhost. #### 3.8.3 failover\_mode Defines behavior when the validation server is unreachable (for clients) or when internal validation fails (for server). | Value | Behavior | Security | Availability | | -------------- | ------------------------------------ | -------- | ------------ | | `fail_closed` | Deny all requests | High | Low | | `fail_open` | Allow all requests | Low | High | | `local_policy` | Fall back to local policy evaluation | Medium | Medium | Default: `fail_closed` (deny-by-default for security) **fail\_closed** (RECOMMENDED for production): ```yaml theme={null} server: failover_mode: "fail_closed" ``` * All validation requests are denied when server is unreachable * Returns error code -32001 (Forbidden) with reason "validation\_unavailable" * Highest security, may cause availability issues **fail\_open** (NOT RECOMMENDED): ```yaml theme={null} server: failover_mode: "fail_open" ``` * All requests are allowed when server is unreachable * Logs warning: "failover\_mode=fail\_open triggered" * ⚠️ Only use in development or when availability > security **fail\_open constraints (v1alpha2)**: When `failover_mode: "fail_open"` is configured, implementations SHOULD require additional constraints to limit exposure: ```yaml theme={null} server: failover_mode: "fail_open" fail_open_constraints: # RECOMMENDED when fail_open allowed_tools: [] # Only these tools fail-open max_duration: # Auto-revert to fail_closed max_requests: # Max requests before fail_closed alert_webhook: # Notify on fail_open activation require_local_policy: # Must have valid local policy ``` | Field | Type | Description | | ---------------------- | --------- | --------------------------------------------------------------- | | `allowed_tools` | \[]string | Only these tools are allowed during fail\_open (others blocked) | | `max_duration` | duration | Auto-revert to fail\_closed after this period | | `max_requests` | int | Auto-revert after N requests in fail\_open mode | | `alert_webhook` | string | POST notification when fail\_open activates | | `require_local_policy` | bool | Only fail\_open if local policy is loaded and valid | **Example with constraints**: ```yaml theme={null} server: failover_mode: "fail_open" fail_open_constraints: allowed_tools: - read_file - list_directory max_duration: "5m" max_requests: 100 alert_webhook: "https://alerts.example.com/aip-failover" require_local_policy: true ``` **Behavior**: * When validation server becomes unreachable: 1. Increment fail\_open counter 2. Check if `max_requests` exceeded → revert to fail\_closed 3. Check if `max_duration` exceeded → revert to fail\_closed 4. If request tool NOT in `allowed_tools` → block with -32001 5. If `require_local_policy` and no valid local policy → block with -32001 6. POST to `alert_webhook` (async, fire-and-forget) 7. Allow request, log warning **Implementation requirements**: * Implementations SHOULD warn at policy load time if `fail_open` is used without constraints * Implementations MUST log every request processed in fail\_open mode * Implementations SHOULD expose a metric `aip_fail_open_requests_total` **local\_policy** (RECOMMENDED for hybrid deployments): ```yaml theme={null} server: failover_mode: "local_policy" ``` * Falls back to local policy file evaluation * Requires local policy to be loaded and valid * Provides security with graceful degradation #### 3.8.4 timeout Maximum time to wait for validation server response. Format: Go duration string Default: `"5s"` (5 seconds) After timeout, the `failover_mode` behavior is triggered. Example: ```yaml theme={null} server: enabled: true timeout: "3s" # Shorter timeout for latency-sensitive apps failover_mode: "local_policy" ``` #### 3.8.5 TLS Configuration When the listen address is not localhost (`127.0.0.1` or `::1`), TLS MUST be configured. ```yaml theme={null} tls: cert: "/path/to/cert.pem" key: "/path/to/key.pem" ``` Implementations SHOULD support: * PEM-encoded certificates and keys * Let's Encrypt/ACME integration (implementation-defined) #### 3.8.6 Endpoints Customizable endpoint paths: | Endpoint | Default | Description | | ---------- | -------------- | -------------------------------------------------- | | `validate` | `/v1/validate` | Policy validation endpoint | | `revoke` | `/v1/revoke` | Token/session revocation (v1alpha2) | | `jwks` | `/v1/jwks` | JSON Web Key Set for token verification (v1alpha2) | | `health` | `/health` | Health check (for load balancers) | | `metrics` | `/metrics` | Prometheus metrics (optional) | *** ## 4. Evaluation Semantics *\[Sections 4.1 through 4.5 remain unchanged from v1alpha1]* ### 4.1 Name Normalization Tool names and method names MUST be normalized before comparison using the following algorithm: ``` NORMALIZE(input): 1. Apply NFKC Unicode normalization 2. Convert to lowercase 3. Trim leading/trailing whitespace 4. Remove non-printable and control characters 5. Return result ``` This prevents bypass attacks using: * Fullwidth characters: `delete` → `delete` * Ligatures: `file` → `file` * Zero-width characters: `dele​te` → `delete` ### 4.2 Method-Level Authorization Method authorization is the FIRST line of defense, evaluated BEFORE tool-level checks. ``` IS_METHOD_ALLOWED(method): normalized = NORMALIZE(method) IF normalized IN denied_methods: RETURN DENY IF "*" IN allowed_methods: RETURN ALLOW IF normalized IN allowed_methods: RETURN ALLOW RETURN DENY ``` ### 4.3 Tool-Level Authorization Tool authorization applies to `tools/call` requests. ``` IS_TOOL_ALLOWED(tool_name, arguments, token): normalized = NORMALIZE(tool_name) # Step 0: Verify identity token (v1alpha2) IF identity.require_token: IF token IS EMPTY OR NOT valid_token(token): RETURN TOKEN_REQUIRED # Step 1: Check rate limiting IF rate_limiter_exceeded(normalized): RETURN RATE_LIMITED # Step 2: Check protected paths IF arguments_contain_protected_path(arguments): RETURN PROTECTED_PATH # Step 3: Check tool rules rule = find_rule(normalized) IF rule EXISTS: IF rule.action == "block": RETURN BLOCK IF rule.action == "ask": IF validate_arguments(rule, arguments): RETURN ASK ELSE: RETURN BLOCK # action == "allow" falls through # Step 4: Check allowed_tools list IF normalized NOT IN allowed_tools: RETURN BLOCK # Step 5: Validate arguments (if rule exists) IF rule EXISTS AND rule.allow_args NOT EMPTY: IF NOT validate_arguments(rule, arguments): RETURN BLOCK # Step 6: Strict args check IF strict_args_enabled(rule): IF arguments has undeclared keys: RETURN BLOCK RETURN ALLOW ``` ### 4.4 Decision Outcomes | Decision | Mode=enforce | Mode=monitor | | --------------- | --------------- | -------------------------------------- | | ALLOW | Forward request | Forward request | | BLOCK | Return error | Forward request, log violation | | ASK | Prompt user | Prompt user | | RATE\_LIMITED | Return error | Return error (always enforced) | | PROTECTED\_PATH | Return error | Return error (always enforced) | | TOKEN\_REQUIRED | Return error | Return error (always enforced) *(new)* | | TOKEN\_INVALID | Return error | Return error (always enforced) *(new)* | ### 4.5 Argument Validation ``` VALIDATE_ARGUMENTS(rule, arguments): FOR EACH (arg_name, pattern) IN rule.allow_args: IF arg_name NOT IN arguments: RETURN FALSE # Required argument missing value = STRING(arguments[arg_name]) IF NOT REGEX_MATCH(pattern, value): RETURN FALSE RETURN TRUE ``` The `STRING()` function converts values to string representation: * String → as-is * Number → decimal representation * Boolean → "true" or "false" * Null → empty string * Array/Object → JSON serialization *** ## 5. Agent Identity (v1alpha2) This section defines the agent identity model introduced in v1alpha2. ### 5.1 Overview Agent identity provides: 1. **Session binding**: Cryptographic proof that requests belong to the same session 2. **Policy integrity**: Verification that the policy hasn't changed mid-session 3. **Replay prevention**: Nonces prevent token reuse across sessions 4. **Audit correlation**: Session IDs link related audit events ### 5.2 Policy Hash The policy hash uniquely identifies a policy configuration. #### 5.2.1 Canonical Form Before hashing, the policy MUST be converted to canonical form: ``` CANONICALIZE(policy): 1. Remove metadata.signature field (if present) 2. Serialize to JSON using RFC 8785 (JSON Canonicalization Scheme) 3. Return UTF-8 encoded bytes ``` #### 5.2.2 Hash Computation ``` POLICY_HASH(policy): canonical = CANONICALIZE(policy) hash = SHA-256(canonical) RETURN hex_encode(hash) ``` The policy hash is a 64-character lowercase hexadecimal string. ### 5.3 Identity Token Structure An AIP Identity Token is a JWT-like structure (but NOT necessarily JWT-encoded) with the following fields: ```json theme={null} { "version": "aip/v1alpha2", "aud": "", "policy_hash": "<64-char-hex>", "session_id": "", "agent_id": "", "issued_at": "", "expires_at": "", "nonce": "", "binding": { "process_id": , "policy_path": "", "hostname": "" } } ``` | Field | Type | Description | | ------------- | ------ | --------------------------------------------------------------- | | `version` | string | Token format version (`aip/v1alpha2`) | | `aud` | string | Intended audience (from `identity.audience` or `metadata.name`) | | `policy_hash` | string | SHA-256 hash of canonical policy | | `session_id` | string | UUID identifying this session | | `agent_id` | string | Value of `metadata.name` from policy | | `issued_at` | string | Token issuance time (ISO 8601) | | `expires_at` | string | Token expiration time (ISO 8601) | | `nonce` | string | Random value for replay prevention | | `binding` | object | Session binding context (see 5.3.2) | #### 5.3.2 Binding Object (v1alpha2) The `binding` object ties tokens to their execution context: ```json theme={null} { "binding": { "process_id": 12345, "policy_path": "/etc/aip/policy.yaml", "hostname": "worker-node-1.example.com", "container_id": "abc123def456", "pod_uid": "550e8400-e29b-41d4-a716-446655440000" } } ``` | Field | Type | Required | Description | | -------------- | ------ | -------- | -------------------------------- | | `process_id` | int | Yes | OS process ID | | `policy_path` | string | Yes | Absolute path to policy file | | `hostname` | string | Yes | Normalized hostname (see below) | | `container_id` | string | No | Container ID (Docker/containerd) | | `pod_uid` | string | No | Kubernetes pod UID | **Hostname Normalization (v1alpha2)**: Hostnames MUST be normalized for consistent binding: ``` NORMALIZE_HOSTNAME(): # Priority order (use first available): # 1. Kubernetes pod UID (most stable in k8s) IF env.POD_UID exists: RETURN "k8s:" + env.POD_UID # 2. Container ID (stable within container lifecycle) IF running_in_container(): container_id = read_container_id() # /proc/1/cpuset or cgroup RETURN "container:" + container_id[0:12] # 3. FQDN (prefer over short hostname) IF gethostname() contains ".": RETURN lowercase(gethostname()) # 4. Short hostname + domain from resolv.conf hostname = lowercase(gethostname()) IF /etc/resolv.conf contains "search" or "domain": domain = first_search_domain() RETURN hostname + "." + domain # 5. Fallback to short hostname RETURN hostname ``` **Environment-specific binding**: | Environment | `hostname` Value | Additional Fields | | ----------- | --------------------- | ------------------------- | | Bare metal | FQDN | - | | VM | FQDN | - | | Docker | `container:` | `container_id` | | Kubernetes | `k8s:` | `pod_uid`, `container_id` | | Serverless | `lambda:` | Implementation-defined | **Kubernetes deployment**: For Kubernetes deployments, inject pod UID via downward API: ```yaml theme={null} env: - name: POD_UID valueFrom: fieldRef: fieldPath: metadata.uid - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name ``` **Session binding modes and hostname**: | `session_binding` | Hostname Checked | Container ID Checked | Pod UID Checked | | ----------------- | ---------------- | -------------------- | ---------------- | | `process` | No | No | No | | `policy` | No | No | No | | `strict` | Yes | Yes (if present) | Yes (if present) | **Strict binding in ephemeral environments**: ⚠️ Using `session_binding: "strict"` in Kubernetes or serverless environments may cause issues: * Pod restarts change pod UID → tokens invalid * Horizontal scaling creates multiple instances → tokens not portable **Recommendation for Kubernetes**: ```yaml theme={null} identity: session_binding: "policy" # Don't bind to ephemeral pod identity require_token: true audience: "https://my-mcp-server.svc.cluster.local" ``` #### 5.3.1 Token Encoding Implementations MUST encode tokens using one of the following formats: | Format | When to Use | Interoperability | | ------------------------- | -------------------------------------- | ---------------------- | | **JWT** (RFC 7519) | When `server.enabled: true` (REQUIRED) | High - standard format | | **Compact** (Base64 JSON) | Local-only deployments | Low - AIP-specific | **JWT Encoding (REQUIRED for server mode)**: When `server.enabled: true`, tokens MUST be encoded as RFC 7519 JWTs. This ensures interoperability with external systems and standard JWT libraries. JWT Header: ```json theme={null} { "alg": "ES256", "typ": "aip+jwt" } ``` Supported signing algorithms (in order of preference): 1. `ES256` (ECDSA with P-256 and SHA-256) - RECOMMENDED for production 2. `EdDSA` (Ed25519) - RECOMMENDED for performance 3. `HS256` (HMAC-SHA256) - MAY be used only when `server.enabled: false` ⚠️ **Security**: `HS256` requires a shared secret, which is unsuitable for distributed validation. Implementations MUST reject `HS256` tokens on server endpoints. **Compact Encoding (local-only)**: When `server.enabled: false`, implementations MAY use compact encoding: ``` base64url(json_payload) + "." + base64url(signature) ``` Compact tokens MUST NOT be sent to remote validation endpoints. ### 5.4 Token Lifecycle ``` ┌──────────────┐ │ Session │ │ Start │ └──────┬───────┘ │ ▼ ┌──────────────┐ ┌──────────────┐ │ Issue │────▶│ Active │ │ Token │ │ Token │ └──────────────┘ └──────┬───────┘ │ ┌────────────────────┼────────────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Rotation │ │ Expired │ │ Session │ │ (new token)│ │ (reject) │ │ End │ └──────────────┘ └──────────────┘ └──────────────┘ ``` #### 5.4.1 Token Issuance Tokens are issued when: 1. Session starts (first tool call with `identity.enabled: true`) 2. Rotation interval elapsed 3. Policy changes (new policy\_hash) #### 5.4.2 Token Rotation Rotation creates a new token while the old token is still valid (grace period). ``` ROTATE_TOKEN(current_token): IF current_token.expires_at - now() > rotation_grace_period: RETURN current_token # Not yet time to rotate new_token = ISSUE_TOKEN( session_id: current_token.session_id, # Preserve session policy_hash: POLICY_HASH(current_policy), agent_id: current_policy.metadata.name, nonce: RANDOM_HEX(32) ) RETURN new_token ``` #### 5.4.3 Token Validation ``` VALIDATE_TOKEN(token): # Step 0: Check revocation FIRST (before any other validation) revocation_result = CHECK_REVOCATION(token) IF revocation_result == REVOKED: RETURN (INVALID, revocation_result.reason) # Step 1: Check expiration IF now() > token.expires_at: RETURN (INVALID, "token_expired") # Step 2: Check audience (v1alpha2) expected_audience = identity.audience OR current_policy.metadata.name IF token.aud != expected_audience: RETURN (INVALID, "audience_mismatch") # Step 3: Check policy hash IF token.policy_hash != POLICY_HASH(current_policy): # Check if within grace period (if configured) IF policy_transition_grace > 0: IF token.policy_hash IN recent_policy_hashes: # Allow during transition CONTINUE RETURN (INVALID, "policy_changed") # Step 4: Check session binding IF identity.session_binding == "process": IF token.binding.process_id != current_process_id: RETURN (INVALID, "session_mismatch") IF identity.session_binding == "strict": IF token.binding != current_binding: RETURN (INVALID, "binding_mismatch") # Step 5: Check nonce with bounded window (atomic operation required) IF NOT ATOMIC_CHECK_AND_RECORD_NONCE(token.nonce, identity.nonce_window): RETURN (INVALID, "replay_detected") # Step 6: Prune old nonces (may be async) PRUNE_NONCES_OLDER_THAN(now() - identity.nonce_window) RETURN (VALID, nil) ``` ### 5.5 Session Management #### 5.5.1 Session Start A session starts when: * The AIP engine loads a policy with `identity.enabled: true` * A new process starts with AIP configured #### 5.5.2 Session End A session ends when: * The AIP engine process terminates * The policy is unloaded or changed significantly * Explicit session termination (implementation-defined) #### 5.5.3 Session ID Session IDs MUST be: * UUID v4 (random) - RECOMMENDED * Globally unique * Not predictable ### 5.6 Token and Session Revocation (v1alpha2) Revocation allows immediate invalidation of tokens or sessions before their natural expiration. #### 5.6.1 Revocation Targets | Target | Scope | Use Case | | ---------------------------- | --------------------- | -------------------------------- | | **Token** (by nonce) | Single token | Suspected token compromise | | **Session** (by session\_id) | All tokens in session | User logout, session termination | #### 5.6.2 Revocation Storage Implementations MUST maintain a revocation set containing: ```json theme={null} { "revoked_sessions": ["", ...], "revoked_tokens": ["", ...] } ``` Storage requirements: * Revoked sessions SHOULD be retained for `max_session_duration` (implementation-defined, default: 24h) * Revoked tokens SHOULD be retained for `nonce_window` duration (then naturally expire) #### 5.6.3 Revocation Check Token validation MUST include revocation check: ``` CHECK_REVOCATION(token): IF token.session_id IN revoked_sessions: RETURN (REVOKED, "session_revoked") IF token.nonce IN revoked_tokens: RETURN (REVOKED, "token_revoked") RETURN (VALID, nil) ``` #### 5.6.4 Local Revocation For local-only deployments (`server.enabled: false`), implementations SHOULD provide: * Signal handler (e.g., `SIGUSR1`) to trigger session termination * File-based revocation list that is polled periodically * API for programmatic revocation (implementation-defined) ### 5.7 Compatibility with Agentic JWT AIP Identity Tokens are designed to be **compatible** with the emerging Agentic JWT standard (draft-goswami-agentic-jwt-00). Implementations MAY support Agentic JWT by: 1. Computing `agent_checksum` from policy content 2. Including `agent_proof` claims in JWT tokens 3. Supporting the `agent_checksum` OAuth grant type See [Appendix D.6](#d6-agentic-jwt-compatibility) for mapping details. ### 5.8 Key Management (v1alpha2) This section defines key management requirements for JWT signing when `server.enabled: true`. #### 5.8.1 Key Configuration ```yaml theme={null} spec: identity: keys: # OPTIONAL (v1alpha2) signing_algorithm: # OPTIONAL, default: "ES256" key_source: # OPTIONAL, default: "generate" key_path: # REQUIRED if key_source is "file" rotation_period: # OPTIONAL, default: "7d" jwks_endpoint: # OPTIONAL, default: "/v1/jwks" ``` | Field | Type | Description | | ------------------- | -------- | -------------------------------------------- | | `signing_algorithm` | string | JWT signing algorithm (see 5.8.2) | | `key_source` | string | `generate`, `file`, or `external` | | `key_path` | string | Path to key file (PEM format) | | `rotation_period` | duration | How often to rotate keys | | `jwks_endpoint` | string | Endpoint path for JWKS (when server.enabled) | #### 5.8.2 Supported Algorithms | Algorithm | Key Type | Security | Performance | Recommendation | | --------- | ----------- | -------- | ----------- | ----------------------------------- | | `ES256` | ECDSA P-256 | High | Fast | **Default, RECOMMENDED** | | `ES384` | ECDSA P-384 | Higher | Medium | High-security environments | | `EdDSA` | Ed25519 | High | Fastest | Performance-critical | | `RS256` | RSA 2048+ | High | Slow | Legacy compatibility | | `HS256` | HMAC | Medium | Fastest | **Local-only, NOT for server mode** | ⚠️ **Security**: `HS256` uses symmetric keys and MUST NOT be used when `server.enabled: true`. Implementations MUST reject this configuration. #### 5.8.3 Key Sources **generate** (default): ```yaml theme={null} keys: key_source: "generate" rotation_period: "7d" ``` * Implementation generates and manages keys automatically * Private key stored in memory (RECOMMENDED) or encrypted file * JWKS endpoint exposes public keys for verification **file**: ```yaml theme={null} keys: key_source: "file" key_path: "/etc/aip/signing-key.pem" ``` * Key loaded from PEM file * Implementation MUST NOT expose private key * Key rotation requires file replacement and restart/reload **external** (future): ```yaml theme={null} keys: key_source: "external" external: type: "vault" address: "https://vault.example.com" key_name: "aip-signing-key" ``` * Keys managed by external KMS (HashiCorp Vault, AWS KMS, etc.) * Implementation-defined integration #### 5.8.4 Key Rotation Keys SHOULD be rotated periodically to limit exposure from key compromise. **Rotation process**: ``` TIME 0: KEY_A active, KEY_A in JWKS TIME T: KEY_B generated, KEY_A + KEY_B in JWKS TIME T+1: KEY_B active (new tokens), KEY_A + KEY_B in JWKS TIME T+TTL: KEY_A removed from JWKS (tokens expired) ``` **Requirements**: 1. New keys MUST be added to JWKS before becoming active 2. Old keys MUST remain in JWKS for at least `token_ttl` after rotation 3. Implementations MUST support at least 2 concurrent keys in JWKS **Configuration**: ```yaml theme={null} identity: keys: rotation_period: "7d" # Rotate weekly grace_period: "1h" # Keep old key in JWKS for 1 hour extra ``` #### 5.8.5 JWKS Endpoint When `server.enabled: true`, implementations MUST expose a JWKS endpoint for token verification. **Request**: ```http theme={null} GET /v1/jwks HTTP/1.1 Host: aip-server:9443 ``` **Response**: ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json Cache-Control: public, max-age=3600 { "keys": [ { "kty": "EC", "crv": "P-256", "kid": "key-2026-01-24", "use": "sig", "alg": "ES256", "x": "...", "y": "..." }, { "kty": "EC", "crv": "P-256", "kid": "key-2026-01-17", "use": "sig", "alg": "ES256", "x": "...", "y": "..." } ] } ``` **Caching**: * Clients SHOULD cache JWKS responses * `Cache-Control` header SHOULD indicate TTL (default: 1 hour) * Clients MUST refresh JWKS when encountering unknown `kid` #### 5.8.6 Key Compromise Response If a signing key is compromised: 1. **Immediate**: Remove compromised key from JWKS 2. **Generate**: Create new signing key 3. **Revoke**: Revoke all sessions that used compromised key 4. **Rotate**: Force token rotation for all active sessions 5. **Audit**: Log compromise event with forensic details **Emergency key revocation endpoint** (implementation-defined): ```http theme={null} POST /v1/keys/revoke HTTP/1.1 Host: aip-server:9443 Authorization: Bearer Content-Type: application/json { "kid": "key-2026-01-17", "reason": "Key compromise detected", "revoke_sessions": true } ``` ⚠️ **This is a destructive operation** that invalidates all tokens signed with the specified key. *** ## 6. Server-Side Validation (v1alpha2) This section defines the optional HTTP server for remote policy validation. ### 6.1 Overview The AIP server provides: 1. **Remote validation**: Validate tool calls from external systems 2. **Health checks**: Integration with load balancers and orchestrators 3. **Metrics**: Prometheus-compatible metrics export ### 6.2 Validation Endpoint #### 6.2.1 Request Format ```http theme={null} POST /v1/validate HTTP/1.1 Host: aip-server:9443 Content-Type: application/json Authorization: Bearer { "tool": "", "arguments": { ... } } ``` | Field | Type | Required | Description | | ----------- | ------ | -------- | --------------------- | | `tool` | string | Yes | Tool name to validate | | `arguments` | object | Yes | Tool arguments | **Token Transmission (RFC 6750 compliant)**: The identity token MUST be transmitted in the `Authorization` header using the Bearer scheme: ```http theme={null} Authorization: Bearer ``` Implementations MUST NOT accept tokens in: * Request body parameters * Query string parameters * Cookies This prevents: * Token leakage via access logs (query strings) * CSRF attacks (body parameters) * Cross-origin token theft (cookies) When `identity.require_token: true`, requests without a valid Authorization header MUST be rejected with HTTP 401. #### 6.2.2 Response Format ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json { "decision": "allow|block|ask", "reason": "", "violations": [ { "type": "", "field": "", "message": "" } ], "token_status": { "valid": true, "expires_in": 240 } } ``` | Field | Type | Description | | -------------- | ------ | ---------------------------------------------- | | `decision` | string | `allow`, `block`, or `ask` | | `reason` | string | Human-readable explanation | | `violations` | array | List of policy violations (if any) | | `token_status` | object | Token validity information (if token provided) | #### 6.2.3 Error Responses | HTTP Status | Error Code | Description | | ----------- | ----------------- | ------------------------------- | | 400 | `invalid_request` | Malformed request body | | 401 | `token_required` | Token required but not provided | | 401 | `token_invalid` | Token validation failed | | 403 | `forbidden` | Tool not allowed | | 429 | `rate_limited` | Rate limit exceeded | | 500 | `internal_error` | Server error | ### 6.3 Health Endpoint #### 6.3.1 Request ```http theme={null} GET /health HTTP/1.1 Host: aip-server:9443 ``` #### 6.3.2 Response ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json { "status": "healthy", "version": "v1alpha2", "policy_hash": "<64-char-hex>", "uptime_seconds": 3600 } ``` | Status | HTTP Code | Description | | ----------- | --------- | ---------------------------- | | `healthy` | 200 | Server is ready | | `degraded` | 200 | Server running with warnings | | `unhealthy` | 503 | Server not ready | ### 6.4 Metrics Endpoint When enabled, the metrics endpoint exposes Prometheus-compatible metrics. #### 6.4.1 Request ```http theme={null} GET /metrics HTTP/1.1 Host: aip-server:9443 ``` #### 6.4.2 Metrics | Metric | Type | Description | | ------------------------------ | --------- | ----------------------------------------- | | `aip_requests_total` | counter | Total validation requests | | `aip_decisions_total` | counter | Decisions by type (allow/block/ask) | | `aip_violations_total` | counter | Policy violations by type | | `aip_token_validations_total` | counter | Token validations (valid/invalid) | | `aip_revocations_total` | counter | Revocation events by type (session/token) | | `aip_active_sessions` | gauge | Currently active sessions | | `aip_request_duration_seconds` | histogram | Request latency | | `aip_policy_hash` | gauge | Current policy hash (as label) | ### 6.5 Revocation Endpoint (v1alpha2) The revocation endpoint allows immediate invalidation of tokens or sessions. #### 6.5.1 Request Format ```http theme={null} POST /v1/revoke HTTP/1.1 Host: aip-server:9443 Content-Type: application/json Authorization: Bearer { "type": "session|token", "session_id": "", // Required if type=session "token_nonce": "", // Required if type=token "reason": "" // OPTIONAL } ``` | Field | Type | Required | Description | | ------------- | ------ | ----------- | --------------------------------------- | | `type` | string | Yes | `session` or `token` | | `session_id` | string | Conditional | Session UUID (required if type=session) | | `token_nonce` | string | Conditional | Token nonce (required if type=token) | | `reason` | string | No | Audit trail reason | #### 6.5.2 Response Format ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json { "revoked": true, "type": "session", "target": "550e8400-e29b-41d4-a716-446655440000", "revoked_at": "2026-01-24T10:30:00.000Z" } ``` #### 6.5.3 Error Responses | HTTP Status | Error Code | Description | | ----------- | ----------------- | ----------------------------- | | 400 | `invalid_request` | Missing required fields | | 401 | `unauthorized` | Admin authentication required | | 404 | `not_found` | Session or token not found | | 500 | `internal_error` | Server error | #### 6.5.4 Authorization The revocation endpoint MUST require elevated privileges: * Separate admin token (not user identity token) * mTLS with admin certificate * Operator API key ⚠️ **Security**: Revocation is a privileged operation. Do not allow agents to revoke their own or other sessions. #### 6.5.5 Audit Logging Revocation events MUST be logged: ```json theme={null} { "timestamp": "2026-01-24T10:30:00.000Z", "event": "REVOCATION", "type": "session", "target": "550e8400-e29b-41d4-a716-446655440000", "reason": "Suspected compromise", "admin": "operator@example.com" } ``` ### 6.6 Authentication The validation endpoint SHOULD be protected. Implementations MUST support: * **Bearer tokens**: AIP Identity Tokens in Authorization header * **mTLS**: Mutual TLS for service-to-service authentication Implementations MAY support: * API keys * OAuth 2.0 tokens (for integration with external IdPs) *** ## 7. Error Codes AIP defines the following JSON-RPC error codes: | Code | Name | Description | | ------ | ------------------------ | ---------------------------------------------------- | | -32001 | Forbidden | Tool not in allowed\_tools list | | -32002 | Rate Limited | Rate limit exceeded | | -32004 | User Denied | User rejected approval prompt | | -32005 | User Timeout | Approval prompt timed out | | -32006 | Method Not Allowed | JSON-RPC method not permitted | | -32007 | Protected Path | Access to protected path blocked | | -32008 | Token Required | Identity token required but not provided *(new)* | | -32009 | Token Invalid | Identity token validation failed *(new)* | | -32010 | Policy Signature Invalid | Policy signature verification failed *(new)* | | -32011 | Token Revoked | Token or session explicitly revoked *(new)* | | -32012 | Audience Mismatch | Token audience does not match expected value *(new)* | | -32013 | Schema Mismatch | Tool schema hash does not match policy *(new)* | | -32014 | DLP Redaction Failed | Request redaction produced invalid content *(new)* | ### 7.1 Error Response Format ```json theme={null} { "jsonrpc": "2.0", "id": , "error": { "code": , "message": "", "data": { "tool": "", "reason": "" } } } ``` ### 7.2 New Error Codes (v1alpha2) #### -32008 Token Required Returned when `identity.require_token: true` and no token is provided. ```json theme={null} { "code": -32008, "message": "Token required", "data": { "tool": "file_write", "reason": "Identity token required for this policy" } } ``` #### -32009 Token Invalid Returned when token validation fails. ```json theme={null} { "code": -32009, "message": "Token invalid", "data": { "tool": "file_write", "reason": "Token expired", "token_error": "token_expired" } } ``` Possible `token_error` values: * `token_expired` - Token past expiration time * `policy_changed` - Policy hash mismatch * `session_mismatch` - Session binding mismatch * `binding_mismatch` - Strict binding validation failed * `replay_detected` - Nonce reuse detected * `audience_mismatch` - Token audience does not match expected value *(new)* * `malformed` - Token structure invalid **Note**: `token_revoked` errors use the dedicated -32011 error code for clearer operational distinction. #### -32010 Policy Signature Invalid Returned when policy signature verification fails. ```json theme={null} { "code": -32010, "message": "Policy signature invalid", "data": { "policy": "production-agent", "reason": "Signature verification failed" } } ``` #### -32011 Token Revoked (v1alpha2) Returned when a token or its session has been explicitly revoked via the revocation endpoint. ```json theme={null} { "code": -32011, "message": "Token revoked", "data": { "tool": "file_write", "reason": "Session revoked by administrator", "revoked_at": "2026-01-24T10:30:00.000Z", "revocation_type": "session" } } ``` Possible `revocation_type` values: * `session` - Entire session was revoked (all tokens invalid) * `token` - Specific token was revoked (by nonce) **Operational note**: Error -32011 is distinct from -32009 to enable security teams to differentiate between normal token lifecycle events (expiration) and security incident responses (revocation). #### -32012 Audience Mismatch (v1alpha2) Returned when the token's `aud` claim does not match the expected audience. ```json theme={null} { "code": -32012, "message": "Audience mismatch", "data": { "tool": "file_write", "reason": "Token not valid for this service", "expected_audience": "https://mcp.example.com", "token_audience": "https://other-mcp.example.com" } } ``` **Security note**: This error indicates a possible token misuse or attack. The `token_audience` value SHOULD be logged for forensics but MAY be omitted from client responses to prevent information disclosure. #### -32013 Schema Mismatch (v1alpha2) Returned when a tool's schema hash does not match the expected value in the policy. ```json theme={null} { "code": -32013, "message": "Schema mismatch", "data": { "tool": "read_file", "reason": "Tool schema has changed since policy was created", "expected_hash": "sha256:a3c7f2e8...", "actual_hash": "sha256:b4d8e3f9..." } } ``` **Security note**: This error indicates a potential tool poisoning attack or uncontrolled tool update. Implementations SHOULD: 1. Alert security teams immediately 2. Log full schema details for forensic analysis 3. Consider blocking the MCP server until verified *** ## 8. Audit Log Format *\[Section 8.1-8.3 remain unchanged from v1alpha1]* ### 8.1 Required Fields | Field | Type | Description | | ------------- | -------- | ---------------------------------------------------------- | | `timestamp` | ISO 8601 | Time of the decision | | `direction` | string | `upstream` (client→server) or `downstream` (server→client) | | `decision` | string | `ALLOW`, `BLOCK`, `ALLOW_MONITOR`, `RATE_LIMITED` | | `policy_mode` | string | `enforce` or `monitor` | | `violation` | boolean | Whether a policy violation was detected | ### 8.2 Optional Fields | Field | Type | Description | | ------------- | ------ | ------------------------------------ | | `method` | string | JSON-RPC method name | | `tool` | string | Tool name (for tools/call) | | `args` | object | Tool arguments (SHOULD be redacted) | | `failed_arg` | string | Argument that failed validation | | `failed_rule` | string | Regex pattern that failed | | `session_id` | string | Session identifier *(new)* | | `token_id` | string | Token nonce *(new)* | | `policy_hash` | string | Policy hash at decision time *(new)* | ### 8.3 Example ```json theme={null} { "timestamp": "2026-01-24T10:30:45.123Z", "direction": "upstream", "method": "tools/call", "tool": "delete_file", "args": {"path": "/etc/passwd"}, "decision": "BLOCK", "policy_mode": "enforce", "violation": true, "failed_arg": "path", "failed_rule": "^/home/.*", "session_id": "550e8400-e29b-41d4-a716-446655440000", "policy_hash": "a3c7f2e8d9b4f1e2c8a7d6f3e9b2c4f1a8e7d3c2b5f4e9a7c3d8f2b6e1a9c4f7" } ``` ### 8.4 Identity Events (v1alpha2) Identity-related events SHOULD be logged: #### Token Issued ```json theme={null} { "timestamp": "2026-01-24T10:30:00.000Z", "event": "TOKEN_ISSUED", "session_id": "550e8400-e29b-41d4-a716-446655440000", "token_id": "abc123def456", "expires_at": "2026-01-24T10:35:00.000Z", "policy_hash": "a3c7f2e8..." } ``` #### Token Rotated ```json theme={null} { "timestamp": "2026-01-24T10:34:00.000Z", "event": "TOKEN_ROTATED", "session_id": "550e8400-e29b-41d4-a716-446655440000", "old_token_id": "abc123def456", "new_token_id": "xyz789ghi012", "expires_at": "2026-01-24T10:39:00.000Z" } ``` #### Token Validation Failed ```json theme={null} { "timestamp": "2026-01-24T10:36:00.000Z", "event": "TOKEN_VALIDATION_FAILED", "session_id": "550e8400-e29b-41d4-a716-446655440000", "token_id": "abc123def456", "error": "token_expired" } ``` *** ## 9. Conformance ### 9.1 Conformance Levels | Level | Requirements | | ------------ | -------------------------------------------------------------- | | **Basic** | Method authorization, tool allowlist, error codes | | **Full** | Basic + argument validation, rate limiting, DLP, audit logging | | **Extended** | Full + Human-in-the-Loop (action=ask) | | **Identity** | Full + Identity tokens, session management *(new)* | | **Server** | Identity + Server-side validation endpoints *(new)* | ### 9.2 Conformance Testing Implementations MUST pass the conformance test suite to claim AIP compliance. The test suite consists of: 1. **Schema validation tests**: Verify policy parsing 2. **Decision tests**: Input → expected decision 3. **Normalization tests**: Verify Unicode handling 4. **Error format tests**: Verify JSON-RPC errors 5. **Identity tests**: Token lifecycle, rotation, validation *(new)* 6. **Server tests**: HTTP endpoint behavior *(new)* See `spec/conformance/` for test vectors. ### 9.3 Implementation Requirements Implementations MUST: * Parse `apiVersion: aip.io/v1alpha2` documents * Reject documents with unknown `apiVersion` * Apply NFKC normalization to names * Return specified error codes * Support `enforce` and `monitor` modes Implementations SHOULD: * Log decisions in the specified format * Support DLP scanning * Support rate limiting * Support identity tokens (for Identity conformance level) Implementations MAY: * Use any regex engine with RE2 semantics * Implement additional security features (egress control, sandboxing) * Implement server-side validation (for Server conformance level) *** ## 10. Security Considerations ### 10.0 Threat Model This section defines the security assumptions and threat model for AIP. #### 10.0.1 Trust Boundaries AIP defines the following trust boundaries: ``` ┌─────────────────────────────────────────────────────────────────┐ │ UNTRUSTED │ │ ┌──────────┐ │ │ │ Agent │ AI agent may be manipulated via prompt injection │ │ └────┬─────┘ │ │ │ │ ├───────┼─────────────────────────────────────────────────────────┤ │ │ TRUST BOUNDARY (AIP) │ │ ▼ │ │ ┌──────────────┐ │ │ │ AIP Policy │ Policy engine is TRUSTED │ │ │ Engine │ Policy file integrity assumed │ │ └──────┬───────┘ │ │ │ │ ├─────────┼───────────────────────────────────────────────────────┤ │ │ TRUST BOUNDARY (MCP) │ │ ▼ │ │ ┌──────────────┐ │ │ │ MCP Server │ Server behavior is UNTRUSTED │ │ │ │ Tool definitions may be malicious │ │ └──────────────┘ │ │ UNTRUSTED │ └─────────────────────────────────────────────────────────────────┘ ``` | Component | Trust Level | Rationale | | ---------------------- | ----------- | ----------------------------------------------- | | **User** | Trusted | Defines policy, approves sensitive operations | | **Policy file** | Trusted | Integrity verified via signature (when present) | | **AIP Engine** | Trusted | Assumed correctly implemented | | **Agent (LLM)** | Untrusted | Subject to prompt injection, jailbreaks | | **MCP Server** | Untrusted | May be malicious or compromised | | **Tool definitions** | Untrusted | May contain poisoned descriptions | | **External resources** | Untrusted | May contain indirect prompt injections | #### 10.0.2 Threats In Scope AIP is designed to mitigate the following threats: | Threat | Attack Vector | AIP Mitigation | | -------------------------------- | ---------------------------------------- | ---------------------------------------------- | | **Unauthorized tool access** | Agent calls tools outside intended scope | `allowed_tools` allowlist, fail-closed default | | **Argument manipulation** | Agent passes malicious arguments | `allow_args` regex validation, `strict_args` | | **Privilege escalation** | Agent accesses sensitive files | `protected_paths`, path expansion | | **Data exfiltration (response)** | Sensitive data in tool responses | DLP scanning with redaction | | **Resource exhaustion** | Agent floods tool calls | Rate limiting per tool | | **Policy bypass (Unicode)** | Homoglyph attacks on tool names | NFKC normalization | | **Session hijacking** | Stolen token reuse | Session binding, nonce tracking | | **Policy tampering** | Agent modifies policy | Protected paths, signature verification | | **Replay attacks** | Reuse of captured tokens | Nonce validation, short TTL | #### 10.0.3 Threats Out of Scope The following threats are explicitly **not addressed** by this specification: | Threat | Reason | Potential Future Extension | | ------------------------------- | --------------------------------------------- | -------------------------- | | **Network egress** | Platform-specific enforcement | Appendix D.1 | | **Tool poisoning** | ✅ **Addressed in v1alpha2** via `schema_hash` | Section 3.5.4 | | **Rug pull attacks** | Requires runtime behavior attestation | Future: tool attestation | | **Subprocess sandboxing** | OS-specific | Implementation-defined | | **Hardware tampering** | Physical security | Out of scope | | **Side-channel attacks** | Implementation-specific | Out of scope | | **Prompt injection prevention** | LLM-level defense | Complementary to AIP | #### 10.0.4 Security Assumptions AIP makes the following assumptions: 1. **Policy integrity**: The policy file has not been tampered with at load time (verified via signature when `metadata.signature` is present) 2. **Engine integrity**: The AIP implementation is correct and not compromised 3. **Cryptographic security**: SHA-256, Ed25519, and other algorithms remain secure 4. **Clock accuracy**: System clocks are reasonably synchronized (within TTL tolerance) 5. **TLS security**: Transport encryption prevents eavesdropping and tampering #### 10.0.5 Defense in Depth AIP implements multiple layers of defense: ``` Request Flow: Agent Request │ ▼ ┌────────────────┐ │ 1. Method │ Block unauthorized JSON-RPC methods │ Check │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ 2. Identity │ Validate token, session binding │ Check │ (v1alpha2) └───────┬────────┘ │ ▼ ┌────────────────┐ │ 3. Rate Limit │ Prevent resource exhaustion │ Check │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ 4. Tool │ Allowlist enforcement │ Check │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ 5. Argument │ Regex validation, protected paths │ Check │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ 6. HITL │ Human approval for sensitive ops │ (if ask) │ └───────┬────────┘ │ ▼ MCP Server │ ▼ ┌────────────────┐ │ 7. DLP │ Redact sensitive response data │ Scan │ └───────┬────────┘ │ ▼ Agent Response ``` ### 10.1 Policy File Protection The policy file itself MUST be protected from modification by the agent. Implementations MUST automatically add the policy file path to `protected_paths`. ### 10.2 Regex Denial of Service (ReDoS) Implementations MUST use a regex engine that guarantees linear-time matching (RE2 or equivalent). Pathological patterns like `(a+)+$` MUST NOT cause exponential execution time. ### 10.3 Unicode Normalization Implementations MUST apply NFKC normalization to prevent homoglyph attacks. However, implementers should be aware that NFKC does not normalize all visually similar characters (e.g., Cyrillic 'а' vs Latin 'a'). ### 10.4 Monitor Mode Risks Monitor mode allows all requests through. Implementations SHOULD warn users when monitor mode is enabled in production environments. ### 10.5 Audit Log Integrity Audit logs SHOULD be written to a location not writable by the agent. Implementations MAY support log signing or forwarding to external systems. ### 10.6 Identity Token Security (v1alpha2) #### 10.6.1 Token Storage Identity tokens SHOULD be stored in memory only, not persisted to disk. If persistence is required, tokens MUST be encrypted at rest. #### 10.6.2 Token Transmission Tokens transmitted over the network MUST use TLS 1.2 or later. Implementations MUST NOT send tokens over unencrypted connections. #### 10.6.3 Token Lifetime Short token lifetimes (5-15 minutes) limit the window for token theft. Implementations SHOULD NOT allow token\_ttl greater than 1 hour. #### 10.6.4 Replay Prevention Implementations MUST track nonces to prevent token replay within the `nonce_window` duration. **Atomic Operation Requirement (v1alpha2)**: Nonce validation MUST be performed as an **atomic check-and-record** operation to prevent race conditions in concurrent environments: ``` ATOMIC_CHECK_AND_RECORD_NONCE(nonce, window): # This MUST be atomic - no gap between check and record # Implementation options: # - Redis: SET nonce 1 NX EX window_seconds # - PostgreSQL: INSERT ... ON CONFLICT DO NOTHING # - In-memory: sync.Map with CompareAndSwap IF ATOMIC_SET_IF_NOT_EXISTS(nonce, ttl=window): RETURN TRUE # Nonce was new, now recorded ELSE: RETURN FALSE # Nonce already existed (replay attempt) ``` ⚠️ **Critical**: Non-atomic check-then-record implementations have a race condition window where concurrent requests with the same nonce could both pass validation. **Storage strategies**: | Strategy | Pros | Cons | Recommended For | | ------------------------------- | ------------------- | ----------------------- | ---------------------------- | | In-memory (sync.Map) | Fast, simple | Lost on restart | Single-instance, short TTL | | Redis (SET NX EX) | Atomic, distributed | Latency, dependency | Multi-instance (RECOMMENDED) | | PostgreSQL (INSERT ON CONFLICT) | Atomic, durable | Higher latency | Multi-instance with DB | | Bloom filter | Space efficient | False positives, no TTL | NOT RECOMMENDED | **Nonce pruning**: Implementations MUST prune nonces older than `nonce_window` to bound storage: ``` MAX_NONCES = (expected_requests_per_second * nonce_window_seconds) ``` Example: 100 req/s with 5m window = 30,000 nonces maximum. **Distributed deployment requirements**: In multi-instance deployments: 1. **Shared storage is REQUIRED** - Local-only nonce tracking allows replay across instances 2. **Atomic operations are REQUIRED** - Use storage primitives that guarantee atomicity (Redis `SET NX`, DB unique constraints) 3. **TTL-based expiration** - Set storage TTL to `nonce_window + clock_skew_tolerance` (recommended: 30 seconds tolerance) 4. **Clock synchronization** - All instances SHOULD use NTP with drift \< 1 second **Configuration for distributed deployments**: ```yaml theme={null} identity: enabled: true nonce_window: "5m" nonce_storage: # OPTIONAL (v1alpha2) type: "redis" # redis | postgres | memory address: "redis://localhost:6379" key_prefix: "aip:nonce:" clock_skew_tolerance: "30s" # Added to TTL for safety ``` A token with a previously-seen nonce MUST be rejected with error code -32009 (`replay_detected`). #### 10.6.5 Session Binding Session binding prevents stolen tokens from being used in different contexts. The `strict` binding mode provides the strongest guarantees but may cause issues with process restarts. ### 10.7 Server Endpoint Security (v1alpha2) #### 10.7.1 Authentication Validation endpoints MUST require authentication. Unauthenticated endpoints allow attackers to probe policy configurations. #### 10.7.2 Rate Limiting Validation endpoints SHOULD implement rate limiting to prevent denial of service attacks. #### 10.7.3 Information Disclosure Error responses SHOULD NOT reveal detailed policy configuration. The `reason` field SHOULD provide minimal information needed to diagnose issues. *** ## 11. IANA Considerations This specification requests registration of the following: ### 11.1 Media Type * Type name: application * Subtype name: vnd.aip.policy+yaml * Required parameters: None * File extension: .yaml, .yml ### 11.2 URI Scheme This specification uses the `aip.io` namespace for versioning: * `aip.io/v1alpha1` - Previous specification * `aip.io/v1alpha2` - This specification *** ## Appendix A: Complete Schema Reference ```yaml theme={null} # Complete AgentPolicy schema (v1alpha2) apiVersion: aip.io/v1alpha2 # REQUIRED kind: AgentPolicy # REQUIRED metadata: # REQUIRED name: string # REQUIRED - Policy identifier version: string # OPTIONAL - Semantic version owner: string # OPTIONAL - Contact email signature: string # OPTIONAL - Policy signature (v1alpha2) spec: # REQUIRED mode: enforce | monitor # OPTIONAL, default: enforce allowed_tools: # OPTIONAL - string allowed_methods: # OPTIONAL - string denied_methods: # OPTIONAL - string protected_paths: # OPTIONAL - string strict_args_default: boolean # OPTIONAL, default: false tool_rules: # OPTIONAL - tool: string # REQUIRED action: allow|block|ask # OPTIONAL, default: allow rate_limit: string # OPTIONAL, format: "N/period" strict_args: boolean # OPTIONAL schema_hash: string # OPTIONAL - Tool schema integrity (v1alpha2) allow_args: # OPTIONAL : dlp: # OPTIONAL enabled: boolean # OPTIONAL, default: true scan_requests: boolean # OPTIONAL, default: false (v1alpha2) scan_responses: boolean # OPTIONAL, default: true (v1alpha2) detect_encoding: boolean # OPTIONAL, default: false filter_stderr: boolean # OPTIONAL, default: false max_scan_size: string # OPTIONAL, default: "1MB" (v1alpha2) on_request_match: string # OPTIONAL, default: "block" (v1alpha2) on_redaction_failure: string # OPTIONAL, default: "block" (v1alpha2) log_original_on_failure: boolean # OPTIONAL, default: false (v1alpha2) patterns: # REQUIRED if dlp present - name: string # REQUIRED regex: string # REQUIRED scope: string # OPTIONAL, default: "all" (v1alpha2) identity: # OPTIONAL (v1alpha2) enabled: boolean # OPTIONAL, default: false token_ttl: string # OPTIONAL, default: "5m" rotation_interval: string # OPTIONAL, default: "4m" (must be < token_ttl) require_token: boolean # OPTIONAL, default: false session_binding: string # OPTIONAL, default: "process" nonce_window: string # OPTIONAL, default: equals token_ttl policy_transition_grace: string # OPTIONAL, default: "0s" audience: string # OPTIONAL, default: metadata.name nonce_storage: # OPTIONAL (v1alpha2) type: string # memory | redis | postgres address: string # Connection string (if not memory) key_prefix: string # default: "aip:nonce:" clock_skew_tolerance: string # default: "30s" keys: # OPTIONAL (v1alpha2) signing_algorithm: string # default: "ES256" key_source: string # generate | file | external key_path: string # Required if key_source is "file" rotation_period: string # default: "7d" jwks_endpoint: string # default: "/v1/jwks" server: # OPTIONAL (v1alpha2) enabled: boolean # OPTIONAL, default: false listen: string # OPTIONAL, default: "127.0.0.1:9443" failover_mode: string # OPTIONAL, default: "fail_closed" timeout: string # OPTIONAL, default: "5s" tls: # OPTIONAL cert: string # Path to TLS certificate key: string # Path to TLS private key fail_open_constraints: # OPTIONAL (recommended if fail_open) allowed_tools: # Only these tools fail-open - string max_duration: string # Auto-revert after duration max_requests: integer # Auto-revert after N requests alert_webhook: string # Notify on fail_open activation require_local_policy: boolean # Require valid local policy endpoints: # OPTIONAL validate: string # default: "/v1/validate" revoke: string # default: "/v1/revoke" jwks: string # default: "/v1/jwks" (v1alpha2) health: string # default: "/health" metrics: string # default: "/metrics" ``` *** ## Appendix B: Changelog ### v1alpha2 (2026-01-24) **Identity and Session Management** * Added `identity` configuration section * Token generation and rotation with configurable TTL * Session binding (`process`, `policy`, `strict`) * Policy hash computation for integrity * `nonce_window` for bounded replay prevention storage * `policy_transition_grace` for gradual policy rollouts * `audience` for token audience binding (RFC 8707 alignment) * `nonce_storage` for distributed nonce tracking (Redis, PostgreSQL) * `keys` for JWT signing key management and rotation * Added token revocation mechanism (Section 5.6) * Session and token-level revocation * Revocation storage and pruning * Added Section 5.8 Key Management * Signing algorithm selection (ES256, EdDSA, RS256) * Key rotation with grace periods * JWKS endpoint for remote verification * Key compromise response procedures * Added Section 5.3.2 Binding Object * Hostname normalization for containers and Kubernetes * Container ID and Pod UID binding support **Server-Side Validation** * Added `server` configuration section * HTTP validation endpoint (`/v1/validate`) * Revocation endpoint (`/v1/revoke`) * JWKS endpoint (`/v1/jwks`) for key distribution * Health and metrics endpoints * `failover_mode`: `fail_closed`, `fail_open`, `local_policy` * `fail_open_constraints` for safer fail\_open deployments * Configurable `timeout` for validation requests * Mandated JWT encoding when `server.enabled: true` * Token transmission via Authorization header only (RFC 6750) **Tool Security** * Added `schema_hash` to tool\_rules (Section 3.5.4) * Cryptographic verification of tool definitions * Tool poisoning attack prevention * SHA-256/384/512 algorithm support **DLP Enhancements** * Added `scan_requests` for request-side DLP scanning * Added `max_scan_size` to prevent ReDoS * Added `on_request_match` action (`block`, `redact`, `warn`) * Added `on_redaction_failure` handling (`block`, `allow_original`, `reject`) * Added `log_original_on_failure` for forensics * Added `scope` to patterns (`request`, `response`, `all`) **Security** * Added Section 10.0 Threat Model * Trust boundaries diagram * Threats in scope / out of scope * Defense in depth layers * Added `metadata.signature` for policy integrity (Ed25519) * Atomic nonce operations required for replay prevention * Tool poisoning now addressed via schema hashing * Enhanced replay prevention documentation with distributed storage **Configuration Validation** * Added rotation\_interval validation (must be \< token\_ttl) * Policy load failures for invalid configurations **Error Codes** * Added -32008 Token Required * Added -32009 Token Invalid (with detailed error types) * Added -32010 Policy Signature Invalid * Added -32011 Token Revoked (distinct from -32009) * Added -32012 Audience Mismatch * Added -32013 Schema Mismatch (tool poisoning detection) * Added -32014 DLP Redaction Failed **Conformance** * Added Identity conformance level * Added Server conformance level * Added identity and server tests to conformance suite ### v1alpha1 (2026-01-20) * Initial draft specification * Defined core policy schema * Defined evaluation semantics * Defined error codes * Defined audit log format *** ## Appendix C: References * [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) * [MCP Authorization (2025-06-18)](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization) * [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification) * [RFC 2119 - Key words for use in RFCs](https://www.rfc-editor.org/rfc/rfc2119) * [RFC 7519 - JSON Web Token (JWT)](https://www.rfc-editor.org/rfc/rfc7519) * [RFC 8785 - JSON Canonicalization Scheme (JCS)](https://www.rfc-editor.org/rfc/rfc8785) * [Unicode NFKC Normalization](https://unicode.org/reports/tr15/) * [RE2 Syntax](https://github.com/google/re2/wiki/Syntax) * [Agentic JWT (draft-goswami-agentic-jwt-00)](https://datatracker.ietf.org/doc/html/draft-goswami-agentic-jwt-00) *** ## Appendix D: Future Extensions This appendix describes features under consideration for future versions of AIP. ### D.1 Network Egress Control **Status:** Proposed for v1beta1 *\[Content unchanged from v1alpha1]* ### D.2 Policy Inheritance **Status:** Under Discussion Allow policies to extend base policies: ```yaml theme={null} apiVersion: aip.io/v1beta1 kind: AgentPolicy metadata: name: team-policy spec: extends: "org-base-policy" # Inherit from another policy allowed_tools: - additional_tool # Add to parent's list ``` ### D.3 External Identity Federation **Status:** Proposed for v1beta1 Allow policies to integrate with external identity providers: ```yaml theme={null} spec: identity: federation: type: oidc issuer: "https://accounts.google.com" client_id: "aip-agent" required_claims: email_verified: true hd: "company.com" ``` Supported federation types: * `oidc` - OpenID Connect providers * `spiffe` - SPIFFE/SPIRE workload identity ### D.4 Telemetry and Metrics **Status:** Partially implemented in v1alpha2 (metrics endpoint) Full telemetry specification: ```yaml theme={null} spec: telemetry: metrics: enabled: true format: "prometheus" # prometheus | otlp traces: enabled: true endpoint: "http://jaeger:14268/api/traces" format: "otlp" sampling_rate: 0.1 ``` ### D.5 Advanced Policy Expressions **Status:** Under Discussion Support for CEL (Common Expression Language) or Rego for complex validation: ```yaml theme={null} tool_rules: - tool: file_write action: allow when: | args.path.startsWith("/allowed/") && !args.path.contains("..") && size(args.content) < 1048576 ``` ### D.6 Agentic JWT Compatibility **Status:** Under Discussion for v1beta1 Full compatibility with the Agentic JWT specification: ```yaml theme={null} spec: identity: agentic_jwt: enabled: true # Agent checksum computed from: # - policy content (tools, rules) # - metadata (name, version) include_tool_definitions: true # Support for workflow binding workflow: id: "data-processing-v1" steps: - analyze - transform - export ``` Mapping to Agentic JWT claims: | AIP Field | Agentic JWT Claim | | --------------- | ---------------------------- | | `policy_hash` | `agent_proof.agent_checksum` | | `session_id` | `intent.workflow_id` | | `metadata.name` | `sub` (subject) | | `tool_rules` | Workflow steps | *** ## Appendix E: Implementation Notes ### E.1 Reference Implementation The reference implementation is available at: [https://github.com/ArangoGutierrez/agent-identity-protocol](https://github.com/ArangoGutierrez/agent-identity-protocol) It provides: * Go-based proxy (`aip-proxy`) * Policy engine (`pkg/policy`) * DLP scanner (`pkg/dlp`) * Audit logger (`pkg/audit`) * Identity manager (`pkg/identity`) *(v1alpha2)* * HTTP server (`pkg/server`) *(v1alpha2)* ### E.2 Testing Against Conformance Suite ```bash theme={null} # Clone the spec repository git clone https://github.com/ArangoGutierrez/agent-identity-protocol # Run conformance tests against your implementation cd agent-identity-protocol/spec/conformance ./run-tests.sh --impl "your-aip-binary" --level "identity" ``` ### E.3 Token Implementation Guidance #### Generating Secure Nonces ```go theme={null} import "crypto/rand" func generateNonce() string { b := make([]byte, 16) rand.Read(b) return hex.EncodeToString(b) } ``` #### Computing Policy Hash ```go theme={null} import ( "crypto/sha256" "encoding/json" ) func computePolicyHash(policy *AgentPolicy) string { // Remove signature for hashing policyCopy := *policy policyCopy.Metadata.Signature = "" // Canonical JSON (keys sorted) canonical, _ := json.Marshal(policyCopy) hash := sha256.Sum256(canonical) return hex.EncodeToString(hash[:]) } ``` ### E.4 Registering Your Implementation Implementations that pass the conformance suite may be listed in the official registry. Submit a PR to the AIP repository with: * Implementation name and URL * Conformance level achieved (Basic/Full/Extended/Identity/Server) * Platform support matrix # AIP v1alpha3 Source: https://agentidentityprotocol.io/specs/aip-v1alpha3 # Agent Identity Protocol (AIP) Specification **Version:** v1alpha3\ **Status:** Draft\ **Last Updated:** 2026-02-19\ **Authors:** Eduardo Arango ([arangogutierrez@gmail.com](mailto:arangogutierrez@gmail.com)),\ James Cao ([james@montcao.com](mailto:james@montcao.com)) *** ## Abstract The Agent Identity Protocol (AIP) defines a standard for identity, authentication, and policy-based authorization of AI agent tool calls. AIP enables runtime environments to enforce fine-grained access control over Model Context Protocol (MCP) tool invocations, providing a security boundary between AI agents and external resources. This specification defines: 1. The policy document schema (`AgentPolicy`) 2. Evaluation semantics for authorization decisions 3. Agent identity and session management 4. Server-side validation endpoints 5. **Agent Authentication Token (AAT) and the identity layer** *(new in v1alpha3)* 6. **AIP Registry and Token Issuer** *(new in v1alpha3)* 7. **User binding and delegation** *(new in v1alpha3)* 8. Error codes for denied requests 9. Audit log format for compliance AIP is designed to be implementation-agnostic. Any MCP-compatible runtime (Cursor, Claude Desktop, VS Code, custom implementations) can implement this specification. *** ## Table of Contents 1. [Introduction](#1-introduction) 2. [Terminology](#2-terminology) 3. [Policy Document Schema](#3-policy-document-schema) 4. [Evaluation Semantics](#4-evaluation-semantics) 5. [Agent Identity](#5-agent-identity) 6. [Server-Side Validation](#6-server-side-validation) 7. [Agent Authentication Token (AAT)](#7-agent-authentication-token-aat) *(new in v1alpha3)* 8. [AIP Registry](#8-aip-registry) *(new in v1alpha3)* 9. [Token Issuer](#9-token-issuer) *(new in v1alpha3)* 10. [User Binding and Delegation](#10-user-binding-and-delegation) *(new in v1alpha3)* 11. [Error Codes](#11-error-codes) 12. [Audit Log Format](#12-audit-log-format) 13. [Conformance](#13-conformance) 14. [Security Considerations](#14-security-considerations) 15. [IANA Considerations](#15-iana-considerations) **Appendices** * [Appendix A: Complete Schema Reference](#appendix-a-complete-schema-reference) * [Appendix B: Changelog](#appendix-b-changelog) * [Appendix C: References](#appendix-c-references) * [Appendix D: Future Extensions](#appendix-d-future-extensions) * [Appendix E: Implementation Notes](#appendix-e-implementation-notes) *** ## 1. Introduction ### 1.1 Motivation AI agents operating through the Model Context Protocol (MCP) have access to powerful tools: file systems, databases, APIs, and cloud infrastructure. Without a policy layer, agents operate with unrestricted access to any tool the MCP server exposes. Today, agents are granted full permissions to API keys, secrets, and system resources, running as the user with no distinction between human and non-human actions. There is no universal way to distinguish an AI agent from a human actor. This creates systemic gaps: * **No audit trail** -- agent actions are indistinguishable from human actions in logs * **No revocation** -- once an agent has credentials, there is no standard way to revoke them * **No authorization granularity** -- access is all-or-nothing at the API key level * **Compliance blind spots** -- SOC 2, GDPR, HIPAA, and SOX requirements are unmet for agentic actions AIP addresses these gaps through two interconnected layers: * **Layer 1 -- Identity**: Establishes *who* the agent is via cryptographic identities, Agent Authentication Tokens, and a registry-based root of trust *(formalized in v1alpha3)* * **Layer 2 -- Enforcement**: Decides *what* the agent is allowed to do via the AIP Proxy, policy engine, DLP scanning, and audit logging The **Agent Authentication Token (AAT)** bridges these two layers -- issued by Layer 1, enforced by Layer 2. AIP introduces: * **Capability declaration**: Explicit allowlists of permitted tools * **Argument validation**: Regex-based constraints on tool parameters * **Human-in-the-loop**: Interactive approval for sensitive operations * **Audit trail**: Immutable logging of all authorization decisions * **Agent identity**: Cryptographic binding of policies to agent sessions * **Server-side validation**: Optional HTTP endpoints for distributed policy enforcement * **Agent Authentication Tokens**: Signed tokens carrying agent identity, user delegation, and capabilities *(new in v1alpha3)* * **Registry-based root of trust**: Centralized agent registration with revocation *(new in v1alpha3)* * **User binding**: Cryptographic proof of which human authorized the agent *(new in v1alpha3)* ### 1.2 Goals 1. **Interoperability**: Any MCP runtime can implement AIP 2. **Simplicity**: YAML-based policies readable by security teams 3. **Defense in depth**: Multiple layers (method, tool, argument, identity, AAT) 4. **Fail-closed**: Unknown tools are denied by default 5. **Zero-trust ready**: Support for token-based identity verification 6. **Agent-human distinction**: Separate authentication for agents vs. users *(new in v1alpha3)* 7. **Cryptographic delegation**: Provable chain from user to agent to action *(new in v1alpha3)* ### 1.3 Non-Goals The following are explicitly out of scope for **this version** of the specification: * Network egress control (see [Appendix D: Future Extensions](#appendix-d-future-extensions)) * Subprocess sandboxing (implementation-defined) * Rate limiting algorithms (implementation-defined) * Policy expression languages beyond regex (CEL/Rego - see [Appendix D](#d5-advanced-policy-expressions)) * Full OIDC/SPIFFE federation (see [Appendix D](#d3-external-identity-federation)); basic integration points are defined ### 1.4 Relationship to MCP AIP is designed as a security layer for MCP. It intercepts `tools/call` requests and applies policy checks before forwarding to the MCP server. ``` ┌─────────┐ ┌─────────────┐ ┌─────────────┐ │ Agent │────▶│ AIP Policy │────▶│ MCP Server │ │ │◀────│ Engine │◀────│ │ └─────────┘ └─────────────┘ └─────────────┘ │ ▼ ┌─────────────┐ │ AIP Server │ (optional) │ Endpoint │ └─────────────┘ ``` ### 1.5 Relationship to MCP Authorization MCP defines an optional OAuth 2.1-based authorization layer (MCP 2025-06-18 and later). AIP is **complementary** to MCP authorization: | Concern | MCP Authorization | AIP | | -------------------- | ------------------------------ | --------------------------------- | | **Scope** | Transport-level authentication | Tool-level authorization | | **What it protects** | Access to MCP server | Access to specific tools | | **Token type** | OAuth 2.1 access tokens | Agent Authentication Tokens (AAT) | | **Policy language** | OAuth scopes | YAML policy documents | | **Identity model** | User identity (OAuth) | Agent + user identity (AAT) | Implementations MAY use both MCP authorization (for server access) and AIP (for tool access) simultaneously. ### 1.6 Two-Layer Architecture (v1alpha3) v1alpha3 formalizes the complete two-layer architecture: ``` LAYER 1 -- IDENTITY LAYER 2 -- ENFORCEMENT (Who is this agent?) (What can it do?) +-------------------+ +-------------------+ | AIP Registry | (Root of Trust) | AI Client | | Registers Agents | | Cursor / Claude | | Signs Certs | +---------+---------+ +--------+----------+ | tool call + AAT | Issues Attestation v v +---------------------------+ +-------------------+ | AIP Proxy | | Agent Identity | | | | Document (AID) | | 1. Verify AAT signature | <-- Registry | (Public Key) | | 2. Check revocation list | (revocation) +---------+---------+ | 3. Validate user binding | | Signs Token Requests | 4. Check AAT capabilities | v | 5. Evaluate local policy | +-------------------+ | 6. DLP scan | | Token Issuer | | 7. Audit log | | Validates ID | AAT +---------+-----------------+ | Binds User | ----------------------> | ALLOW / DENY | Issues AAT | v +-------------------+ +-------------------+ | Real Tool | | Docker/Postgres | | GitHub / etc. | +-------------------+ ``` **Flow**: 1. Agent registers with the AIP Registry, receiving a key pair and Agent Identity Document (AID) 2. When a user authorizes an agent, the Token Issuer validates the agent's identity and issues an AAT encoding agent ID, user binding, and capabilities 3. On every tool call, the AIP Proxy verifies the AAT, checks the registry revocation list, evaluates policy, performs DLP scanning, and writes an audit log entry 4. A hijacked agent fails at Layer 2 -- its AAT claims do not match the attempted action 5. A revoked agent fails at Layer 2 -- the proxy checks the registry revocation list on every call 6. A legitimate agent passes through both layers with a full audit trail tied to its verified identity *** ## 2. Terminology The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). | Term | Definition | | ------------------------------------ | --------------------------------------------------------------------------------------------------- | | **Agent** | An AI system that invokes MCP tools on behalf of a user | | **Agent Identity Document (AID)** | JSON structure defining an agent's cryptographic identity *(new)* | | **Agent Authentication Token (AAT)** | A signed token proving agent identity, user delegation, and capabilities at runtime *(new)* | | **AIP Registry** | Central directory of registered agents and their public keys *(new)* | | **Token Issuer** | Service that validates agent identity and issues AATs *(new)* | | **Policy** | A document specifying authorization rules (AgentPolicy) | | **Tool** | An MCP tool exposed by an MCP server | | **Decision** | The result of policy evaluation: ALLOW, BLOCK, or ASK | | **Violation** | A policy rule was triggered (may or may not block) | | **Session** | A bounded period of agent activity with consistent identity | | **Identity Token** | A cryptographic token binding policy to session (v1alpha2; superseded by AAT for cross-service use) | | **Policy Hash** | SHA-256 hash of the canonical policy document | | **User Binding** | Cryptographic proof linking an agent's actions to an authorizing user *(new)* | | **Capability** | A declared permission encoded in an AAT (e.g., tool access, resource scope) *(new)* | | **Delegation Chain** | The provable path from user authorization through agent to action *(new)* | | **Revocation List** | Registry-maintained list of revoked agents, AATs, and sessions *(new)* | *** ## 3. Policy Document Schema ### 3.1 Document Structure An AIP policy document is a YAML file with the following top-level structure: ```yaml theme={null} apiVersion: aip.io/v1alpha3 kind: AgentPolicy metadata: name: version: # OPTIONAL owner: # OPTIONAL signature: # OPTIONAL spec: mode: # OPTIONAL, default: "enforce" allowed_tools: [] # OPTIONAL allowed_methods: [] # OPTIONAL denied_methods: [] # OPTIONAL tool_rules: [] # OPTIONAL protected_paths: [] # OPTIONAL strict_args_default: # OPTIONAL, default: false dlp: # OPTIONAL identity: # OPTIONAL server: # OPTIONAL registry: # OPTIONAL (v1alpha3) aat: # OPTIONAL (v1alpha3) ``` ### 3.2 Required Fields | Field | Type | Description | | --------------- | ------ | --------------------------------- | | `apiVersion` | string | MUST be `aip.io/v1alpha3` | | `kind` | string | MUST be `AgentPolicy` | | `metadata.name` | string | Unique identifier for this policy | ### 3.3 Metadata ```yaml theme={null} metadata: name: # REQUIRED - Policy identifier version: # OPTIONAL - Semantic version (e.g., "1.0.0") owner: # OPTIONAL - Contact email signature: # OPTIONAL - Policy signature ``` #### 3.3.1 Policy Signature The `signature` field provides cryptographic integrity verification for the policy document. Format: `:` Supported algorithms: * `ed25519` - Ed25519 signature (RECOMMENDED) Example: ```yaml theme={null} metadata: name: production-agent signature: "ed25519:YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo..." ``` When present, implementations MUST verify the signature before applying the policy. Signature verification failure MUST result in policy rejection. The signature is computed over the **canonical form** of the policy document (see Section 5.2.1). ### 3.4 Spec Fields *\[Sections 3.4.1 through 3.4.6 remain unchanged from v1alpha2]* #### 3.4.1 mode Controls enforcement behavior. | Value | Behavior | | --------- | --------------------------------- | | `enforce` | Violations are blocked (default) | | `monitor` | Violations are logged but allowed | Implementations MUST support both modes. #### 3.4.2 allowed\_tools A list of tool names that the agent MAY invoke. ```yaml theme={null} allowed_tools: - github_get_repo - read_file - list_directory ``` Tool names are subject to normalization (see Section 4.1). #### 3.4.3 allowed\_methods A list of JSON-RPC methods that are permitted. If not specified, implementations MUST use the default safe list: ```yaml theme={null} # Default allowed methods (when not specified) allowed_methods: - initialize - initialized - ping - tools/call - tools/list - completion/complete - notifications/initialized - notifications/progress - notifications/message - notifications/resources/updated - notifications/resources/list_changed - notifications/tools/list_changed - notifications/prompts/list_changed - cancelled ``` The wildcard `*` MAY be used to allow all methods. #### 3.4.4 denied\_methods A list of JSON-RPC methods that are explicitly denied. Denied methods take precedence over allowed methods. ```yaml theme={null} denied_methods: - resources/read - resources/write ``` #### 3.4.5 protected\_paths A list of file paths that tools MUST NOT access. Any tool argument containing a protected path MUST be blocked. ```yaml theme={null} protected_paths: - ~/.ssh - ~/.aws/credentials - .env ``` Implementations MUST: * Expand `~` to the user's home directory * Automatically protect the policy file itself #### 3.4.6 strict\_args\_default When `true`, tool rules reject any arguments not explicitly declared in `allow_args`. Default: `false` ### 3.5 Tool Rules Tool rules provide fine-grained control over specific tools. ```yaml theme={null} tool_rules: - tool: # REQUIRED - Tool name action: # OPTIONAL - allow|block|ask (default: allow) rate_limit: # OPTIONAL - e.g., "10/minute" strict_args: # OPTIONAL - Override strict_args_default schema_hash: # OPTIONAL - Tool schema integrity allow_args: # OPTIONAL : ``` #### 3.5.1 Actions | Action | Behavior | | ------- | --------------------------------------- | | `allow` | Permit (subject to argument validation) | | `block` | Deny unconditionally | | `ask` | Require interactive user approval | #### 3.5.2 Rate Limiting Format: `/` | Period | Aliases | | -------- | ---------- | | `second` | `sec`, `s` | | `minute` | `min`, `m` | | `hour` | `hr`, `h` | Example: `"10/minute"`, `"100/hour"`, `"5/second"` Rate limiting algorithm is implementation-defined (token bucket, sliding window, etc.). #### 3.5.3 Argument Validation The `allow_args` field maps argument names to regex patterns. ```yaml theme={null} allow_args: url: "^https://github\\.com/.*" query: "^SELECT\\s+.*" ``` Implementations MUST: * Use a regex engine with linear-time guarantees (RE2 or equivalent) * Match against the string representation of the argument value * Treat missing constrained arguments as a violation #### 3.5.4 Tool Schema Hashing The `schema_hash` field provides cryptographic verification of tool definitions to prevent tool poisoning attacks. **Format**: `:` **Supported algorithms**: * `sha256` (RECOMMENDED) * `sha384` * `sha512` **Hash computation**: ``` TOOL_SCHEMA_HASH(tool): schema = { "name": tool.name, "description": tool.description, "inputSchema": tool.inputSchema } canonical = JSON_CANONICALIZE(schema) # RFC 8785 hash = SHA256(canonical) RETURN "sha256:" + hex_encode(hash) ``` | Condition | Behavior | | -------------------- | --------------------------------------------- | | `schema_hash` absent | No schema verification (backward compatible) | | Hash matches | Tool allowed (proceed to argument validation) | | Hash mismatch | Tool BLOCKED with error -32013 | | Tool not found | Tool BLOCKED with error -32001 | ### 3.6 DLP Configuration *\[Section 3.6 remains unchanged from v1alpha2]* Data Loss Prevention (DLP) scans for sensitive data in requests and responses. ```yaml theme={null} dlp: enabled: # OPTIONAL, default: true when dlp block present scan_requests: # OPTIONAL, default: false scan_responses: # OPTIONAL, default: true detect_encoding: # OPTIONAL, default: false filter_stderr: # OPTIONAL, default: false max_scan_size: # OPTIONAL, default: "1MB" on_request_match: # OPTIONAL, default: "block" on_redaction_failure: # OPTIONAL, default: "block" log_original_on_failure: # OPTIONAL, default: false patterns: - name: # REQUIRED - Rule identifier regex: # REQUIRED - Detection pattern scope: # OPTIONAL, default: "all" (request|response|all) ``` When a pattern matches, the matched content MUST be replaced with: ``` [REDACTED:] ``` ### 3.7 Identity Configuration *\[Section 3.7 remains unchanged from v1alpha2]* The `identity` section configures agent identity and token management. ```yaml theme={null} spec: identity: enabled: # OPTIONAL, default: false token_ttl: # OPTIONAL, default: "5m" rotation_interval: # OPTIONAL, default: "4m" require_token: # OPTIONAL, default: false session_binding: # OPTIONAL, default: "process" nonce_window: # OPTIONAL, default: equals token_ttl policy_transition_grace: # OPTIONAL, default: "0s" audience: # OPTIONAL, default: policy metadata.name nonce_storage: # OPTIONAL keys: # OPTIONAL ``` ### 3.8 Server Configuration *\[Section 3.8 remains unchanged from v1alpha2]* ```yaml theme={null} spec: server: enabled: # OPTIONAL, default: false listen: # OPTIONAL, default: "127.0.0.1:9443" failover_mode: # OPTIONAL, default: "fail_closed" timeout: # OPTIONAL, default: "5s" tls: cert: key: endpoints: validate: # default: "/v1/validate" revoke: # default: "/v1/revoke" jwks: # default: "/v1/jwks" health: # default: "/health" metrics: # default: "/metrics" ``` ### 3.9 Registry Configuration (v1alpha3) The `registry` section configures how the AIP Proxy connects to the AIP Registry for agent verification and revocation checks. ```yaml theme={null} spec: registry: enabled: # OPTIONAL, default: false endpoint: # REQUIRED if enabled - Registry URL tls: # OPTIONAL ca_cert: # Path to CA certificate for registry client_cert: # Path to client certificate (mTLS) client_key: # Path to client key (mTLS) cache: # OPTIONAL enabled: # default: true ttl: # default: "5m" max_entries: # default: 10000 revocation: # OPTIONAL check_interval: # default: "30s" mode: # "online" | "cached" | "crl" crl_path: # Path to local CRL file (if mode=crl) auth: # OPTIONAL type: # "bearer" | "mtls" | "api_key" token: # Bearer token for registry access api_key: # API key for registry access ``` #### 3.9.1 enabled When `true`, the AIP Proxy connects to an AIP Registry for agent identity verification and revocation checking. Default: `false` When `registry.enabled: true`, the proxy MUST verify AATs against the registry's known agent keys before applying local policy. #### 3.9.2 endpoint The URL of the AIP Registry. Format: `https://:` Example: ```yaml theme={null} registry: enabled: true endpoint: "https://registry.aip.example.com" ``` #### 3.9.3 Revocation Modes | Mode | Description | Latency | Freshness | | -------- | ----------------------------------------------------- | ------- | --------- | | `online` | Check registry on every AAT validation | High | Real-time | | `cached` | Cache revocation list, refresh periodically (default) | Low | Eventual | | `crl` | Load Certificate Revocation List from local file | Lowest | Manual | **online** (highest security): ```yaml theme={null} registry: revocation: mode: "online" ``` * Every AAT validation queries the registry * Highest security, highest latency * Requires reliable network connectivity **cached** (RECOMMENDED for production): ```yaml theme={null} registry: revocation: mode: "cached" check_interval: "30s" ``` * Background refresh of revocation list * Trades freshness for performance * Revocations take effect within `check_interval` **crl** (air-gapped or offline environments): ```yaml theme={null} registry: revocation: mode: "crl" crl_path: "/etc/aip/revocation.crl" check_interval: "5m" # Re-read CRL file ``` * CRL file updated by external process * No network dependency * Manual revocation propagation #### 3.9.4 Cache Configuration ```yaml theme={null} registry: cache: enabled: true ttl: "5m" max_entries: 10000 ``` | Field | Type | Default | Description | | ------------- | -------- | ------- | --------------------------------------------- | | `enabled` | bool | `true` | Cache agent public keys and revocation status | | `ttl` | duration | `"5m"` | Cache entry time-to-live | | `max_entries` | int | `10000` | Maximum cached entries (LRU eviction) | ### 3.10 AAT Configuration (v1alpha3) The `aat` section configures how the AIP Proxy validates and uses Agent Authentication Tokens. ```yaml theme={null} spec: aat: enabled: # OPTIONAL, default: false require: # OPTIONAL, default: false validation: # OPTIONAL verify_signature: # default: true verify_user_binding: # default: true verify_capabilities: # default: true max_token_age: # default: "1h" clock_skew: # default: "30s" capabilities_mode: # OPTIONAL, default: "intersect" trusted_issuers: [] # OPTIONAL - List of trusted Token Issuer IDs header_name: # OPTIONAL, default: "X-AIP-AAT" ``` #### 3.10.1 enabled When `true`, the proxy accepts and validates AATs on incoming requests. Default: `false` #### 3.10.2 require When `true`, all tool calls MUST include a valid AAT. Calls without AATs are rejected with error code -32015. Default: `false` This enables gradual rollout: start with `require: false` to validate AATs when present without blocking requests that lack them. #### 3.10.3 capabilities\_mode Determines how AAT capabilities interact with local policy. | Value | Behavior | | ------------- | ------------------------------------------------------------------------ | | `intersect` | Tool must be allowed by BOTH AAT capabilities and local policy (default) | | `aat_only` | AAT capabilities replace local `allowed_tools` | | `policy_only` | Local policy only; AAT used for identity/audit only | **intersect** (RECOMMENDED): ```yaml theme={null} aat: capabilities_mode: "intersect" ``` * Most restrictive: requires both AAT and local policy to allow the tool * Defense in depth: neither AAT compromise nor policy compromise alone grants access **aat\_only** (registry-managed environments): ```yaml theme={null} aat: capabilities_mode: "aat_only" ``` * Centralized capability management via Token Issuer * Local policy still enforces argument validation, DLP, and rate limiting * `allowed_tools` in local policy is IGNORED **policy\_only** (audit-focused deployments): ```yaml theme={null} aat: capabilities_mode: "policy_only" ``` * AAT used only for identity verification and audit trail enrichment * Local policy controls all authorization decisions * Simplest migration path from v1alpha2 #### 3.10.4 trusted\_issuers A list of Token Issuer identifiers whose AATs are accepted by this proxy. ```yaml theme={null} aat: trusted_issuers: - "https://issuer.aip.example.com" - "https://issuer.aip.corp.internal" ``` When specified, the proxy MUST reject AATs from issuers not in this list. When not specified, the proxy accepts AATs from any issuer whose public key can be verified via the registry. #### 3.10.5 header\_name The HTTP header or JSON-RPC extension field used to transmit AATs. Default: `"X-AIP-AAT"` For JSON-RPC transport (stdio), the AAT is included in the request params: ```json theme={null} { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "read_file", "arguments": {"path": "/etc/hosts"}, "_aip_aat": "" } } ``` For HTTP transport, the AAT is sent as a header: ```http theme={null} POST /v1/validate HTTP/1.1 X-AIP-AAT: ``` The `_aip_aat` parameter is reserved by this specification and MUST NOT be forwarded to the MCP server. *** ## 4. Evaluation Semantics ### 4.1 Name Normalization Tool names and method names MUST be normalized before comparison using the following algorithm: ``` NORMALIZE(input): 1. Apply NFKC Unicode normalization 2. Convert to lowercase 3. Trim leading/trailing whitespace 4. Remove non-printable and control characters 5. Return result ``` ### 4.2 Method-Level Authorization Method authorization is the FIRST line of defense, evaluated BEFORE tool-level checks. ``` IS_METHOD_ALLOWED(method): normalized = NORMALIZE(method) IF normalized IN denied_methods: RETURN DENY IF "*" IN allowed_methods: RETURN ALLOW IF normalized IN allowed_methods: RETURN ALLOW RETURN DENY ``` ### 4.3 Tool-Level Authorization Tool authorization applies to `tools/call` requests. ``` IS_TOOL_ALLOWED(tool_name, arguments, identity_token, aat): normalized = NORMALIZE(tool_name) # Step 0: Verify identity token (if configured) IF identity.require_token: IF identity_token IS EMPTY OR NOT valid_token(identity_token): RETURN TOKEN_REQUIRED # Step 0b: Verify AAT (v1alpha3) IF aat.require: IF aat IS EMPTY: RETURN AAT_REQUIRED IF NOT valid_aat(aat): RETURN AAT_INVALID ELSE IF aat IS PRESENT: # Validate opportunistically even when not required IF NOT valid_aat(aat): LOG_WARNING("Invalid AAT presented but not required") # Step 0c: Check AAT capabilities (v1alpha3) IF aat IS PRESENT AND aat IS VALID: IF aat.capabilities_mode == "intersect": IF normalized NOT IN aat.capabilities: RETURN AAT_CAPABILITY_DENIED ELSE IF aat.capabilities_mode == "aat_only": IF normalized NOT IN aat.capabilities: RETURN AAT_CAPABILITY_DENIED SKIP local allowed_tools check (Step 4) # Step 1: Check rate limiting IF rate_limiter_exceeded(normalized): RETURN RATE_LIMITED # Step 2: Check protected paths IF arguments_contain_protected_path(arguments): RETURN PROTECTED_PATH # Step 3: Check tool rules rule = find_rule(normalized) IF rule EXISTS: IF rule.action == "block": RETURN BLOCK IF rule.action == "ask": IF validate_arguments(rule, arguments): RETURN ASK ELSE: RETURN BLOCK # Step 4: Check allowed_tools list (skipped if aat_only mode) IF aat.capabilities_mode != "aat_only": IF normalized NOT IN allowed_tools: RETURN BLOCK # Step 5: Validate arguments (if rule exists) IF rule EXISTS AND rule.allow_args NOT EMPTY: IF NOT validate_arguments(rule, arguments): RETURN BLOCK # Step 6: Strict args check IF strict_args_enabled(rule): IF arguments has undeclared keys: RETURN BLOCK RETURN ALLOW ``` ### 4.4 Decision Outcomes | Decision | Mode=enforce | Mode=monitor | | ----------------------- | --------------- | -------------------------------------- | | ALLOW | Forward request | Forward request | | BLOCK | Return error | Forward request, log violation | | ASK | Prompt user | Prompt user | | RATE\_LIMITED | Return error | Return error (always enforced) | | PROTECTED\_PATH | Return error | Return error (always enforced) | | TOKEN\_REQUIRED | Return error | Return error (always enforced) | | TOKEN\_INVALID | Return error | Return error (always enforced) | | AAT\_REQUIRED | Return error | Return error (always enforced) *(new)* | | AAT\_INVALID | Return error | Return error (always enforced) *(new)* | | AAT\_CAPABILITY\_DENIED | Return error | Forward request, log violation *(new)* | ### 4.5 Argument Validation ``` VALIDATE_ARGUMENTS(rule, arguments): FOR EACH (arg_name, pattern) IN rule.allow_args: IF arg_name NOT IN arguments: RETURN FALSE # Required argument missing value = STRING(arguments[arg_name]) IF NOT REGEX_MATCH(pattern, value): RETURN FALSE RETURN TRUE ``` The `STRING()` function converts values to string representation: * String -> as-is * Number -> decimal representation * Boolean -> "true" or "false" * Null -> empty string * Array/Object -> JSON serialization *** ## 5. Agent Identity *\[Section 5 remains unchanged from v1alpha2]* This section defines the local agent identity model introduced in v1alpha2. In v1alpha3, local identity tokens and AATs serve complementary roles: identity tokens bind the proxy session, while AATs carry cross-service agent identity. ### 5.1 Overview Agent identity provides: 1. **Session binding**: Cryptographic proof that requests belong to the same session 2. **Policy integrity**: Verification that the policy hasn't changed mid-session 3. **Replay prevention**: Nonces prevent token reuse across sessions 4. **Audit correlation**: Session IDs link related audit events ### 5.2 Policy Hash #### 5.2.1 Canonical Form ``` CANONICALIZE(policy): 1. Remove metadata.signature field (if present) 2. Serialize to JSON using RFC 8785 (JSON Canonicalization Scheme) 3. Return UTF-8 encoded bytes ``` #### 5.2.2 Hash Computation ``` POLICY_HASH(policy): canonical = CANONICALIZE(policy) hash = SHA-256(canonical) RETURN hex_encode(hash) ``` ### 5.3 Identity Token Structure *\[Remains unchanged from v1alpha2]* ### 5.4 Token Lifecycle *\[Remains unchanged from v1alpha2]* ### 5.5 Session Management *\[Remains unchanged from v1alpha2]* ### 5.6 Token and Session Revocation *\[Remains unchanged from v1alpha2]* ### 5.7 Compatibility with Agentic JWT *\[Remains unchanged from v1alpha2]* ### 5.8 Key Management *\[Remains unchanged from v1alpha2]* *** ## 6. Server-Side Validation *\[Section 6 remains unchanged from v1alpha2, with the addition of AAT-aware endpoints]* ### 6.1 Overview The AIP server provides: 1. **Remote validation**: Validate tool calls from external systems 2. **Health checks**: Integration with load balancers and orchestrators 3. **Metrics**: Prometheus-compatible metrics export 4. **AAT validation**: Verify Agent Authentication Tokens *(new in v1alpha3)* ### 6.2 Validation Endpoint #### 6.2.1 Request Format ```http theme={null} POST /v1/validate HTTP/1.1 Host: aip-server:9443 Content-Type: application/json Authorization: Bearer X-AIP-AAT: { "tool": "", "arguments": { ... } } ``` When both an identity token and an AAT are present, the proxy MUST validate both. The identity token authenticates the proxy session; the AAT authenticates the agent. #### 6.2.2 Response Format ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json { "decision": "allow|block|ask", "reason": "", "violations": [ { "type": "", "field": "", "message": "" } ], "token_status": { "valid": true, "expires_in": 240 }, "aat_status": { "valid": true, "agent_id": "", "user_id": "", "capabilities_checked": true, "issuer": "" } } ``` ### 6.3 Health Endpoint *\[Remains unchanged from v1alpha2]* ### 6.4 Metrics Endpoint Updated metrics (v1alpha3 additions): | Metric | Type | Description | | ------------------------------ | --------- | ------------------------------------------------------- | | `aip_requests_total` | counter | Total validation requests | | `aip_decisions_total` | counter | Decisions by type (allow/block/ask) | | `aip_violations_total` | counter | Policy violations by type | | `aip_token_validations_total` | counter | Identity token validations (valid/invalid) | | `aip_aat_validations_total` | counter | AAT validations (valid/invalid/expired/revoked) *(new)* | | `aip_registry_checks_total` | counter | Registry revocation checks *(new)* | | `aip_registry_latency_seconds` | histogram | Registry check latency *(new)* | | `aip_revocations_total` | counter | Revocation events by type | | `aip_active_sessions` | gauge | Currently active sessions | | `aip_active_agents` | gauge | Currently active agents (by AAT) *(new)* | | `aip_request_duration_seconds` | histogram | Request latency | | `aip_policy_hash` | gauge | Current policy hash (as label) | ### 6.5 Revocation Endpoint *\[Remains unchanged from v1alpha2]* ### 6.6 Authentication *\[Remains unchanged from v1alpha2]* *** ## 7. Agent Authentication Token (AAT) (v1alpha3) This section defines the Agent Authentication Token -- the core credential bridging Layer 1 (Identity) and Layer 2 (Enforcement). ### 7.1 Overview The AAT carries signed claims about an agent: 1. **Who issued its identity** -- the Token Issuer 2. **Which user it is acting on behalf of** -- user binding 3. **What capabilities it declared** -- tools and resource scopes 4. **When it was issued and when it expires** -- temporal bounds The AAT enables: * **Per-agent identity**: Every agent has a distinct cryptographic identity, separate from the user * **User delegation**: Actions are provably linked to the authorizing human * **Capability-based authorization**: AAT capabilities can drive policy decisions * **Cross-service portability**: AATs are verifiable by any party with access to the issuer's public key ### 7.2 AAT Structure An AAT is a signed JWT (RFC 7519) with the following claims: ```json theme={null} { "aat_version": "aip/v1alpha3", "iss": "", "sub": "", "aud": "", "iat": 1708300800, "exp": 1708304400, "nbf": 1708300800, "jti": "", "agent": { "id": "", "name": "", "public_key_thumbprint": "", "aid_hash": "" }, "user_binding": { "user_id": "", "auth_method": "", "auth_time": 1708300000, "delegation_scope": "" }, "capabilities": { "tools": ["", ...], "resource_scopes": ["", ...], "max_calls_per_session": , "allowed_servers": ["", ...] }, "context": { "session_id": "", "policy_hash": "<64-char-hex>", "registry_id": "" } } ``` ### 7.3 AAT Claims #### 7.3.1 Standard JWT Claims | Claim | Type | Required | Description | | ------------- | ------------ | -------- | -------------------------------------------- | | `aat_version` | string | Yes | MUST be `aip/v1alpha3` | | `iss` | string | Yes | Token Issuer identifier (URI) | | `sub` | string | Yes | Agent identifier (from AID) | | `aud` | string/array | Yes | Intended recipient(s) -- proxy or MCP server | | `iat` | number | Yes | Issued-at time (Unix timestamp) | | `exp` | number | Yes | Expiration time (Unix timestamp) | | `nbf` | number | No | Not-before time (Unix timestamp) | | `jti` | string | Yes | Unique token identifier (UUID v4) | #### 7.3.2 Agent Claims The `agent` object identifies the AI agent: | Field | Type | Required | Description | | ----------------------- | ------ | -------- | -------------------------------------------------------- | | `id` | string | Yes | Unique agent identifier (from registry) | | `name` | string | No | Human-readable name (e.g., "Cursor IDE Agent") | | `public_key_thumbprint` | string | Yes | SHA-256 of agent's public key (JWK Thumbprint, RFC 7638) | | `aid_hash` | string | No | SHA-256 of the Agent Identity Document | The `public_key_thumbprint` allows the proxy to verify that the AAT was issued for the specific agent key pair, preventing AAT theft across agents. #### 7.3.3 User Binding Claims The `user_binding` object links the agent's actions to the authorizing human: | Field | Type | Required | Description | | ------------------ | ------ | -------- | ----------------------------------------------- | | `user_id` | string | Yes | User identifier (email, OIDC sub, or opaque ID) | | `auth_method` | string | Yes | How the user authenticated (see below) | | `auth_time` | number | Yes | When the user authenticated (Unix timestamp) | | `delegation_scope` | string | No | Scope of delegation granted to the agent | **auth\_method values**: | Value | Description | | ------------- | --------------------------------- | | `oidc` | OpenID Connect authentication | | `oauth2` | OAuth 2.0 authorization code flow | | `api_key` | API key associated with a user | | `local` | Local system user (process owner) | | `saml` | SAML assertion | | `attestation` | Hardware or platform attestation | **delegation\_scope values**: | Value | Description | | ---------------- | ------------------------------------------------------------- | | `full` | Agent can perform any action the user could (NOT RECOMMENDED) | | `tools` | Agent can use specific tools listed in `capabilities.tools` | | `read_only` | Agent can only use read operations | | `session` | Delegation valid for this session only | | `custom:` | Implementation-defined scope | #### 7.3.4 Capabilities Claims The `capabilities` object declares what the agent is authorized to do: | Field | Type | Required | Description | | ----------------------- | --------- | -------- | ------------------------------------------------------ | | `tools` | \[]string | No | List of tool names the agent may invoke | | `resource_scopes` | \[]string | No | Resource access scopes (e.g., `repo:read`, `db:write`) | | `max_calls_per_session` | int | No | Maximum tool calls allowed in this session | | `allowed_servers` | \[]string | No | MCP server identifiers this AAT is valid for | **Capability resolution** (how AAT capabilities interact with local policy): ``` RESOLVE_CAPABILITIES(aat, local_policy, mode): IF mode == "intersect": RETURN INTERSECTION(aat.capabilities.tools, local_policy.allowed_tools) IF mode == "aat_only": RETURN aat.capabilities.tools IF mode == "policy_only": RETURN local_policy.allowed_tools ``` **resource\_scopes**: Resource scopes follow a `:` format: ```json theme={null} { "resource_scopes": [ "repo:read", "repo:write", "db:read", "file:/home/user/**:read", "api:github.com:*" ] } ``` Resource scopes are advisory in v1alpha3. Future versions MAY make them enforceable. #### 7.3.5 Context Claims The `context` object provides operational context: | Field | Type | Required | Description | | ------------- | ------ | -------- | ---------------------------------------------- | | `session_id` | string | Yes | Session UUID (matches identity token session) | | `policy_hash` | string | No | SHA-256 hash of the policy the issuer approved | | `registry_id` | string | No | Registry that holds the agent's registration | ### 7.4 AAT Signing AATs MUST be signed using one of the following algorithms (in order of preference): | Algorithm | Key Type | Recommendation | | --------- | ----------- | -------------------------- | | `ES256` | ECDSA P-256 | **Default, RECOMMENDED** | | `ES384` | ECDSA P-384 | High-security environments | | `EdDSA` | Ed25519 | Performance-critical | | `RS256` | RSA 2048+ | Legacy compatibility | AATs MUST NOT use symmetric algorithms (`HS256`) as they require shared secrets. JWT Header: ```json theme={null} { "alg": "ES256", "typ": "aat+jwt", "kid": "" } ``` The `kid` MUST reference a key in the Token Issuer's JWKS endpoint or the AIP Registry's key store. ### 7.5 AAT Lifecycle ``` ┌──────────────┐ │ User Grants │ │ Delegation │ └──────┬───────┘ │ ▼ ┌──────────────┐ ┌──────────────┐ │ Token Issuer │────▶│ Active │ │ Issues AAT │ │ AAT │ └──────────────┘ └──────┬───────┘ │ ┌────────────────────┼────────────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Refresh │ │ Expired │ │ Revoked │ │ (new AAT) │ │ (reject) │ │ (by registry│ └──────────────┘ └──────────────┘ │ or issuer) │ └──────────────┘ ``` #### 7.5.1 AAT Issuance 1. Agent authenticates to Token Issuer using its private key (proof of possession) 2. User authorization is verified (OAuth flow, API key lookup, or local attestation) 3. Token Issuer retrieves agent registration from AIP Registry 4. Token Issuer constructs AAT with agent identity, user binding, and capabilities 5. Token Issuer signs AAT with its private key 6. AAT returned to agent #### 7.5.2 AAT Refresh AATs SHOULD be refreshed before expiry. The refresh flow: 1. Agent presents current AAT and proof of possession (signed challenge) 2. Token Issuer verifies current AAT is valid (not expired, not revoked) 3. Token Issuer checks registry for revocation 4. New AAT issued with same `session_id`, fresh `jti`, updated `exp` **Constraints**: * Refreshed AAT MUST preserve the `session_id` * Refreshed AAT MUST have a new `jti` * Refreshed AAT MAY have different `capabilities` (if policy changed) * Refresh MUST fail if the agent or session is revoked #### 7.5.3 AAT Validation The AIP Proxy MUST validate AATs using the following algorithm: ``` VALIDATE_AAT(aat): # Step 1: Parse and verify JWT structure IF NOT valid_jwt_structure(aat): RETURN (INVALID, "malformed_aat") # Step 2: Verify version IF aat.aat_version != "aip/v1alpha3": RETURN (INVALID, "unsupported_version") # Step 3: Verify issuer IF trusted_issuers IS CONFIGURED: IF aat.iss NOT IN trusted_issuers: RETURN (INVALID, "untrusted_issuer") # Step 4: Verify signature issuer_key = GET_ISSUER_PUBLIC_KEY(aat.iss, aat.header.kid) IF issuer_key IS NULL: RETURN (INVALID, "unknown_signing_key") IF NOT verify_signature(aat, issuer_key): RETURN (INVALID, "signature_invalid") # Step 5: Check temporal validity now = current_time() IF now < aat.nbf - clock_skew: RETURN (INVALID, "not_yet_valid") IF now > aat.exp + clock_skew: RETURN (INVALID, "aat_expired") # Step 6: Check audience IF aat.aud does not match expected_audience: RETURN (INVALID, "audience_mismatch") # Step 7: Check revocation (via registry) IF registry.enabled: revocation_status = CHECK_REGISTRY_REVOCATION(aat) IF revocation_status == REVOKED: RETURN (INVALID, "aat_revoked") # Step 8: Verify agent identity IF registry.enabled: agent_record = GET_AGENT_FROM_REGISTRY(aat.agent.id) IF agent_record IS NULL: RETURN (INVALID, "unknown_agent") IF agent_record.public_key_thumbprint != aat.agent.public_key_thumbprint: RETURN (INVALID, "agent_key_mismatch") IF agent_record.status != "active": RETURN (INVALID, "agent_inactive") # Step 9: Verify JTI uniqueness (replay prevention) IF NOT ATOMIC_CHECK_AND_RECORD_JTI(aat.jti): RETURN (INVALID, "replay_detected") RETURN (VALID, nil) ``` ### 7.6 AAT Transport #### 7.6.1 JSON-RPC (stdio) Transport For MCP connections over stdio, the AAT is included in the `tools/call` params as a reserved field: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "read_file", "arguments": {"path": "/etc/hosts"}, "_aip_aat": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImFhdCtqd3QifQ..." } } ``` The `_aip_aat` field: * MUST be stripped by the AIP Proxy before forwarding to the MCP server * MUST NOT be logged in audit trails (log `jti` instead) * Is OPTIONAL when `aat.require: false` #### 7.6.2 HTTP Transport For HTTP-based MCP connections, the AAT is sent as a header: ```http theme={null} POST /mcp HTTP/1.1 X-AIP-AAT: eyJhbGciOiJFUzI1NiIsInR5cCI6ImFhdCtqd3QifQ... Content-Type: application/json { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "read_file", "arguments": {"path": "/etc/hosts"} } } ``` *** ## 8. AIP Registry (v1alpha3) The AIP Registry is the root of trust for agent identities. ### 8.1 Overview The AIP Registry provides: 1. **Agent registration**: Agents register their public keys and metadata 2. **Key attestation**: Registry signs agent certificates (Agent Identity Documents) 3. **Revocation**: Registry maintains lists of revoked agents, AATs, and sessions 4. **Discovery**: Token Issuers and proxies look up agent identities ### 8.2 Agent Identity Document (AID) The AID is a JSON document that defines an agent's cryptographic identity: ```json theme={null} { "aid_version": "aip/v1alpha3", "agent_id": "", "name": "", "description": "", "created_at": "", "status": "active", "public_key": { "kty": "EC", "crv": "P-256", "x": "...", "y": "...", "kid": "", "use": "sig" }, "metadata": { "platform": "", "version": "", "owner": "", "tags": ["", ...] }, "registry_attestation": { "registry_id": "", "signed_at": "", "expires_at": "", "signature": "" } } ``` #### 8.2.1 AID Fields | Field | Type | Required | Description | | ---------------------- | ------ | -------- | ------------------------------------------- | | `aid_version` | string | Yes | MUST be `aip/v1alpha3` | | `agent_id` | string | Yes | Globally unique identifier (UUID v4 or URI) | | `name` | string | Yes | Human-readable name | | `description` | string | No | Agent description | | `created_at` | string | Yes | Registration time (ISO 8601) | | `status` | string | Yes | `active`, `suspended`, or `revoked` | | `public_key` | JWK | Yes | Agent's public key in JWK format (RFC 7517) | | `metadata` | object | No | Additional agent metadata | | `registry_attestation` | object | Yes | Registry's signature over the AID | #### 8.2.2 Agent Status | Status | Description | AAT Issuance | AAT Validation | | ----------- | -------------------------------------------- | ------------ | -------------- | | `active` | Agent is registered and operational | Allowed | Valid | | `suspended` | Temporarily disabled (e.g., security review) | Blocked | Rejected | | `revoked` | Permanently deactivated | Blocked | Rejected | #### 8.2.3 Registry Attestation The `registry_attestation` provides the registry's cryptographic endorsement of the AID: ``` ATTEST_AID(aid, registry_key): aid_copy = COPY(aid) REMOVE aid_copy.registry_attestation canonical = JSON_CANONICALIZE(aid_copy) # RFC 8785 signature = SIGN(registry_key, canonical) RETURN { "registry_id": registry.id, "signed_at": now(), "expires_at": now() + attestation_ttl, "signature": base64url_encode(signature) } ``` ### 8.3 Registry API The AIP Registry exposes the following HTTP endpoints: #### 8.3.1 Agent Registration ```http theme={null} POST /v1/agents HTTP/1.1 Host: registry.aip.example.com Content-Type: application/json Authorization: Bearer { "name": "My AI Agent", "description": "Development assistant for code review", "public_key": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." }, "metadata": { "platform": "cursor", "version": "1.0.0", "owner": "dev@example.com" } } ``` **Response**: ```http theme={null} HTTP/1.1 201 Created Content-Type: application/json { "agent_id": "ag_550e8400-e29b-41d4-a716-446655440000", "aid": { ... }, "registration_token_expires_at": "2026-03-19T00:00:00Z" } ``` #### 8.3.2 Agent Lookup ```http theme={null} GET /v1/agents/{agent_id} HTTP/1.1 Host: registry.aip.example.com ``` **Response**: ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json { "agent_id": "ag_550e8400-e29b-41d4-a716-446655440000", "status": "active", "aid": { ... }, "public_key": { ... } } ``` #### 8.3.3 Revocation List ```http theme={null} GET /v1/revocations HTTP/1.1 Host: registry.aip.example.com If-None-Match: "" ``` **Response**: ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json ETag: "" Cache-Control: max-age=30 { "version": 42, "updated_at": "2026-02-19T10:30:00Z", "revoked_agents": [ { "agent_id": "ag_...", "revoked_at": "2026-02-19T10:00:00Z", "reason": "Security incident" } ], "revoked_aats": [ { "jti": "...", "revoked_at": "2026-02-19T10:15:00Z", "reason": "Token compromise" } ], "revoked_sessions": [ { "session_id": "...", "revoked_at": "2026-02-19T10:20:00Z", "reason": "User logout" } ] } ``` The revocation list supports conditional requests (ETag/If-None-Match) for efficient polling. #### 8.3.4 Agent Key Rotation ```http theme={null} POST /v1/agents/{agent_id}/rotate-key HTTP/1.1 Host: registry.aip.example.com Content-Type: application/json Authorization: Bearer { "new_public_key": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." } } ``` Key rotation: 1. Agent generates new key pair 2. Agent signs rotation request with current private key (proof of possession) 3. Registry verifies signature, updates stored public key 4. Old key remains valid for `key_grace_period` (default: 1 hour) 5. New AID attestation issued #### 8.3.5 Registry JWKS ```http theme={null} GET /v1/jwks HTTP/1.1 Host: registry.aip.example.com ``` Returns the registry's public keys for verifying AID attestations: ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json Cache-Control: public, max-age=3600 { "keys": [ { "kty": "EC", "crv": "P-256", "kid": "registry-key-2026-02", "use": "sig", "alg": "ES256", "x": "...", "y": "..." } ] } ``` ### 8.4 Registry Security #### 8.4.1 Authentication All registry API calls (except JWKS and health) MUST be authenticated. | Endpoint | Required Auth | Description | | --------------------------------- | -------------------- | -------------------------- | | `POST /v1/agents` | Registration token | Initial agent registration | | `GET /v1/agents/{id}` | Bearer token or mTLS | Agent lookup | | `GET /v1/revocations` | Bearer token or mTLS | Revocation list | | `POST /v1/agents/{id}/rotate-key` | Proof of possession | Key rotation | | `GET /v1/jwks` | None (public) | Registry public keys | #### 8.4.2 Rate Limiting Implementations MUST rate-limit registration endpoints to prevent abuse. #### 8.4.3 Data Integrity The registry MUST: * Store agent public keys with integrity protection (e.g., signed records) * Maintain an append-only audit log of all registration events * Never expose private keys (the registry never holds agent private keys) *** ## 9. Token Issuer (v1alpha3) The Token Issuer validates agent identities and issues AATs. ### 9.1 Overview The Token Issuer is a service that: 1. Validates an agent's proof of possession (private key ownership) 2. Verifies the agent is registered and active in the AIP Registry 3. Binds the agent's AAT to the authorizing user 4. Issues signed AATs with capabilities derived from policy ### 9.2 Token Issuance Flow ``` ┌─────────┐ ┌──────────────┐ ┌──────────────┐ │ Agent │ │ Token Issuer │ │ AIP Registry │ └────┬─────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ │ 1. Token Request │ │ │ (signed challenge) │ │ ├──────────────────────▶│ │ │ │ 2. Verify agent in │ │ │ registry │ │ ├────────────────────────▶│ │ │ │ │ │ 3. Agent record + │ │ │ revocation status │ │ │◀────────────────────────┤ │ │ │ │ │ 4. Verify proof of │ │ │ possession │ │ │ │ │ │ 5. Verify user │ │ │ authorization │ │ │ │ │ 6. AAT │ │ │◀──────────────────────┤ │ │ │ │ ``` ### 9.3 Token Request #### 9.3.1 Request Format ```http theme={null} POST /v1/token HTTP/1.1 Host: issuer.aip.example.com Content-Type: application/json { "grant_type": "agent_authentication", "agent_id": "", "proof": { "type": "signed_challenge", "challenge": "", "signature": "", "algorithm": "ES256" }, "user_authorization": { "type": "oauth2_token", "token": "" }, "requested_capabilities": { "tools": ["read_file", "list_directory", "git_status"], "resource_scopes": ["repo:read"], "allowed_servers": ["mcp://localhost:8080"] }, "audience": "" } ``` #### 9.3.2 Grant Types | Grant Type | Description | Use Case | | ---------------------- | ------------------------------------------ | ----------------- | | `agent_authentication` | Agent proves identity + user authorization | Standard flow | | `aat_refresh` | Refresh existing AAT | Token renewal | | `agent_attestation` | Platform attestation (no user) | Autonomous agents | #### 9.3.3 Proof of Possession The agent proves ownership of its private key by signing a challenge: ``` GENERATE_PROOF(agent_key, challenge): payload = { "challenge": challenge, "agent_id": agent.id, "timestamp": now() } canonical = JSON_CANONICALIZE(payload) signature = SIGN(agent_key.private, canonical) RETURN { "type": "signed_challenge", "challenge": challenge, "signature": base64url_encode(signature), "algorithm": agent_key.algorithm } ``` The Token Issuer: 1. Retrieves the agent's public key from the AIP Registry 2. Verifies the challenge signature using that public key 3. Confirms the challenge was recently issued (prevents replay) #### 9.3.4 User Authorization Methods | Method | `user_authorization.type` | Description | | ----------------- | ------------------------- | --------------------------------------- | | OAuth 2.0 | `oauth2_token` | User's OAuth access token (RECOMMENDED) | | OIDC ID Token | `oidc_id_token` | OpenID Connect ID token | | API Key | `api_key` | User's API key | | Local Attestation | `local_user` | OS-level user identity (localhost only) | **OAuth 2.0 flow** (RECOMMENDED): ```json theme={null} { "user_authorization": { "type": "oauth2_token", "token": "ya29.A0ARrdaM..." } } ``` The Token Issuer validates the OAuth token against the identity provider and extracts the `user_id` claim. **Local attestation** (development / localhost): ```json theme={null} { "user_authorization": { "type": "local_user", "uid": 501, "username": "developer", "hostname": "dev-machine.local" } } ``` ### 9.4 Token Response #### 9.4.1 Success Response ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json { "aat": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImFhdCtqd3QiLCJraWQiOiJpc3N1ZXIta2V5LTIwMjYtMDIifQ...", "token_type": "aat+jwt", "expires_in": 3600, "refresh_token": "", "capabilities_granted": { "tools": ["read_file", "list_directory", "git_status"], "resource_scopes": ["repo:read"] }, "capabilities_denied": { "tools": [], "reason": "All requested capabilities granted" } } ``` | Field | Type | Description | | ---------------------- | ------ | ------------------------------------------- | | `aat` | string | The signed Agent Authentication Token (JWT) | | `token_type` | string | MUST be `aat+jwt` | | `expires_in` | int | Token lifetime in seconds | | `refresh_token` | string | Opaque token for AAT refresh (OPTIONAL) | | `capabilities_granted` | object | Capabilities included in the AAT | | `capabilities_denied` | object | Requested capabilities that were denied | #### 9.4.2 Error Response ```http theme={null} HTTP/1.1 400 Bad Request Content-Type: application/json { "error": "", "error_description": "", "error_details": { ... } } ``` | Error Code | HTTP Status | Description | | --------------------- | ----------- | --------------------------------------- | | `invalid_request` | 400 | Malformed request | | `invalid_proof` | 401 | Proof of possession verification failed | | `agent_not_found` | 404 | Agent not registered in registry | | `agent_suspended` | 403 | Agent is suspended | | `agent_revoked` | 403 | Agent is revoked | | `user_auth_failed` | 401 | User authorization verification failed | | `capabilities_denied` | 403 | All requested capabilities denied | | `issuer_error` | 500 | Internal issuer error | ### 9.5 Capability Determination The Token Issuer determines AAT capabilities based on: ``` DETERMINE_CAPABILITIES(requested, agent_record, user_permissions): # Start with requested capabilities granted = requested.tools # Intersect with agent's registered permissions (from registry) IF agent_record.allowed_tools IS NOT EMPTY: granted = INTERSECTION(granted, agent_record.allowed_tools) # Intersect with user's delegated permissions IF user_permissions.delegatable_tools IS NOT EMPTY: granted = INTERSECTION(granted, user_permissions.delegatable_tools) RETURN granted ``` The AAT capabilities represent the **maximum** set of tools the agent can invoke. The AIP Proxy MAY further restrict this based on local policy. ### 9.6 Token Issuer JWKS Token Issuers MUST expose a JWKS endpoint for AAT signature verification: ```http theme={null} GET /v1/jwks HTTP/1.1 Host: issuer.aip.example.com ``` ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json Cache-Control: public, max-age=3600 { "keys": [ { "kty": "EC", "crv": "P-256", "kid": "issuer-key-2026-02", "use": "sig", "alg": "ES256", "x": "...", "y": "..." } ] } ``` Proxies MUST cache JWKS responses and refresh when encountering unknown `kid` values. *** ## 10. User Binding and Delegation (v1alpha3) This section defines how agent actions are cryptographically linked to the authorizing user. ### 10.1 Motivation Without user binding: * Agent actions are indistinguishable from each other and from human actions * Compliance frameworks (SOC 2, GDPR, HIPAA) cannot attribute actions to responsible parties * Revocation of user access does not revoke their agents' access User binding solves these by embedding a verifiable link from every agent action back to the authorizing human. ### 10.2 Delegation Model ``` ┌────────────┐ Grants Delegation ┌────────────┐ │ User │─────────────────────────▶│ Agent │ │ (Human) │ │ (AI) │ │ │ AAT carries: │ │ │ user_id │ - user_id │ agent_id │ │ auth_time │ - auth_method │ public_key│ │ │ - delegation_scope │ │ └────────────┘ └─────┬──────┘ │ Uses AAT for tool calls │ ▼ ┌────────────┐ │ AIP Proxy │ │ │ │ Verifies: │ │ - agent_id │ │ - user_id │ │ - scope │ └────────────┘ ``` ### 10.3 Delegation Chain Verification The AIP Proxy MUST verify the complete delegation chain: ``` VERIFY_DELEGATION_CHAIN(aat): # 1. Verify agent identity (AAT signature + registry check) IF NOT valid_aat_signature(aat): RETURN (INVALID, "signature_invalid") # 2. Verify user binding is present IF aat.user_binding IS EMPTY: RETURN (INVALID, "missing_user_binding") # 3. Verify user authentication freshness max_auth_age = configured_max_auth_age OR 86400 # 24h default IF now() - aat.user_binding.auth_time > max_auth_age: RETURN (INVALID, "user_auth_stale") # 4. Verify delegation scope covers the requested action IF aat.user_binding.delegation_scope == "read_only": IF requested_tool is write_operation: RETURN (INVALID, "delegation_scope_exceeded") RETURN (VALID, nil) ``` ### 10.4 Audit Trail Integration When an AAT with user binding is present, audit log entries MUST include: ```json theme={null} { "timestamp": "2026-02-19T10:30:45.123Z", "direction": "upstream", "method": "tools/call", "tool": "write_file", "decision": "ALLOW", "policy_mode": "enforce", "violation": false, "agent_id": "ag_550e8400-e29b-41d4-a716-446655440000", "agent_name": "Cursor IDE Agent", "user_id": "user@example.com", "user_auth_method": "oidc", "delegation_scope": "tools", "aat_jti": "aat_660e8400-e29b-41d4-a716-446655440001", "aat_issuer": "https://issuer.aip.example.com", "session_id": "550e8400-e29b-41d4-a716-446655440000", "policy_hash": "a3c7f2e8d9b4f1e2c8a7d6f3e9b2c4f1..." } ``` This audit record establishes: * **Who** authorized the action (user\_id, auth\_method) * **What** agent performed the action (agent\_id, agent\_name) * **How** the delegation was granted (delegation\_scope) * **When** it happened (timestamp) * **What** was done (tool, arguments) * **By whose authority** the AAT was issued (aat\_issuer) ### 10.5 User Binding Revocation When a user's authorization is revoked: 1. Token Issuer marks all AATs for that user as revoked 2. Registry propagates revocation to all proxies (via revocation list) 3. Proxies reject tool calls from revoked AATs ``` REVOKE_USER_DELEGATION(user_id): # Find all active AATs for this user affected_aats = FIND_AATS_BY_USER(user_id) FOR EACH aat IN affected_aats: ADD_TO_REVOCATION_LIST(aat.jti, "user_delegation_revoked") # Also revoke all sessions affected_sessions = FIND_SESSIONS_BY_USER(user_id) FOR EACH session IN affected_sessions: ADD_TO_REVOCATION_LIST(session.session_id, "user_delegation_revoked") LOG_REVOCATION_EVENT(user_id, affected_aats.count, affected_sessions.count) ``` *** ## 11. Error Codes AIP defines the following JSON-RPC error codes: | Code | Name | Description | | ------ | ------------------------ | ------------------------------------------------------------ | | -32001 | Forbidden | Tool not in allowed\_tools list | | -32002 | Rate Limited | Rate limit exceeded | | -32004 | User Denied | User rejected approval prompt | | -32005 | User Timeout | Approval prompt timed out | | -32006 | Method Not Allowed | JSON-RPC method not permitted | | -32007 | Protected Path | Access to protected path blocked | | -32008 | Token Required | Identity token required but not provided | | -32009 | Token Invalid | Identity token validation failed | | -32010 | Policy Signature Invalid | Policy signature verification failed | | -32011 | Token Revoked | Token or session explicitly revoked | | -32012 | Audience Mismatch | Token audience does not match expected value | | -32013 | Schema Mismatch | Tool schema hash does not match policy | | -32014 | DLP Redaction Failed | Request redaction produced invalid content | | -32015 | AAT Required | Agent Authentication Token required but not provided *(new)* | | -32016 | AAT Invalid | AAT validation failed *(new)* | | -32017 | AAT Capability Denied | Requested tool not in AAT capabilities *(new)* | | -32018 | Agent Not Registered | Agent not found in AIP Registry *(new)* | | -32019 | Delegation Expired | User delegation has expired or been revoked *(new)* | | -32020 | Issuer Untrusted | AAT issuer not in trusted\_issuers list *(new)* | ### 11.1 Error Response Format ```json theme={null} { "jsonrpc": "2.0", "id": "", "error": { "code": "", "message": "", "data": { "tool": "", "reason": "" } } } ``` ### 11.2 New Error Codes (v1alpha3) #### -32015 AAT Required Returned when `aat.require: true` and no AAT is provided. ```json theme={null} { "code": -32015, "message": "AAT required", "data": { "tool": "write_file", "reason": "Agent Authentication Token required for this proxy" } } ``` #### -32016 AAT Invalid Returned when AAT validation fails. ```json theme={null} { "code": -32016, "message": "AAT invalid", "data": { "tool": "write_file", "reason": "AAT signature verification failed", "aat_error": "signature_invalid" } } ``` Possible `aat_error` values: * `malformed_aat` - JWT structure invalid * `unsupported_version` - `aat_version` not recognized * `untrusted_issuer` - Issuer not in trusted list * `unknown_signing_key` - Issuer key not found * `signature_invalid` - Cryptographic signature failed * `not_yet_valid` - Token `nbf` is in the future * `aat_expired` - Token past expiration * `audience_mismatch` - Token audience wrong * `aat_revoked` - Token or session revoked * `unknown_agent` - Agent not in registry * `agent_key_mismatch` - Agent public key doesn't match * `agent_inactive` - Agent suspended or revoked * `replay_detected` - JTI reuse detected #### -32017 AAT Capability Denied Returned when the requested tool is not in the AAT's capabilities. ```json theme={null} { "code": -32017, "message": "AAT capability denied", "data": { "tool": "delete_database", "reason": "Tool not in AAT capabilities", "agent_id": "ag_550e8400...", "granted_capabilities": ["read_file", "list_directory"] } } ``` #### -32018 Agent Not Registered Returned when the agent in the AAT is not found in the registry. ```json theme={null} { "code": -32018, "message": "Agent not registered", "data": { "agent_id": "ag_unknown", "reason": "Agent not found in AIP Registry" } } ``` #### -32019 Delegation Expired Returned when the user binding in the AAT has expired. ```json theme={null} { "code": -32019, "message": "Delegation expired", "data": { "tool": "write_file", "reason": "User delegation has expired", "user_auth_time": "2026-02-18T10:00:00Z", "max_auth_age": 86400 } } ``` #### -32020 Issuer Untrusted Returned when the AAT was issued by an issuer not in the `trusted_issuers` list. ```json theme={null} { "code": -32020, "message": "Issuer untrusted", "data": { "issuer": "https://unknown-issuer.example.com", "reason": "AAT issuer not in trusted_issuers configuration" } } ``` *** ## 12. Audit Log Format ### 12.1 Required Fields | Field | Type | Description | | ------------- | -------- | ------------------------------------------------------------ | | `timestamp` | ISO 8601 | Time of the decision | | `direction` | string | `upstream` (client->server) or `downstream` (server->client) | | `decision` | string | `ALLOW`, `BLOCK`, `ALLOW_MONITOR`, `RATE_LIMITED` | | `policy_mode` | string | `enforce` or `monitor` | | `violation` | boolean | Whether a policy violation was detected | ### 12.2 Optional Fields | Field | Type | Description | | ------------------ | ------ | ---------------------------------------------- | | `method` | string | JSON-RPC method name | | `tool` | string | Tool name (for tools/call) | | `args` | object | Tool arguments (SHOULD be redacted) | | `failed_arg` | string | Argument that failed validation | | `failed_rule` | string | Regex pattern that failed | | `session_id` | string | Session identifier | | `token_id` | string | Identity token nonce | | `policy_hash` | string | Policy hash at decision time | | `agent_id` | string | Agent identifier (from AAT) *(new)* | | `agent_name` | string | Human-readable agent name (from AAT) *(new)* | | `user_id` | string | Authorizing user identifier (from AAT) *(new)* | | `user_auth_method` | string | User authentication method (from AAT) *(new)* | | `delegation_scope` | string | Delegation scope (from AAT) *(new)* | | `aat_jti` | string | AAT unique identifier *(new)* | | `aat_issuer` | string | Token Issuer identifier *(new)* | ### 12.3 Example ```json theme={null} { "timestamp": "2026-02-19T10:30:45.123Z", "direction": "upstream", "method": "tools/call", "tool": "delete_file", "args": {"path": "/etc/passwd"}, "decision": "BLOCK", "policy_mode": "enforce", "violation": true, "failed_arg": "path", "failed_rule": "^/home/.*", "session_id": "550e8400-e29b-41d4-a716-446655440000", "agent_id": "ag_550e8400-e29b-41d4-a716-446655440000", "agent_name": "Cursor IDE Agent", "user_id": "dev@example.com", "user_auth_method": "oidc", "delegation_scope": "tools", "aat_jti": "aat_660e8400-e29b-41d4-a716-446655440001", "aat_issuer": "https://issuer.aip.example.com", "policy_hash": "a3c7f2e8d9b4f1e2c8a7d6f3e9b2c4f1a8e7d3c2b5f4e9a7c3d8f2b6e1a9c4f7" } ``` ### 12.4 Identity Events *\[Token issued/rotated/failed events remain unchanged from v1alpha2]* #### AAT Validated (v1alpha3) ```json theme={null} { "timestamp": "2026-02-19T10:30:00.000Z", "event": "AAT_VALIDATED", "agent_id": "ag_550e8400...", "user_id": "dev@example.com", "aat_jti": "aat_660e8400...", "issuer": "https://issuer.aip.example.com", "capabilities_granted": ["read_file", "list_directory"] } ``` #### AAT Rejected (v1alpha3) ```json theme={null} { "timestamp": "2026-02-19T10:30:00.000Z", "event": "AAT_REJECTED", "agent_id": "ag_550e8400...", "aat_jti": "aat_660e8400...", "error": "aat_expired", "tool": "write_file" } ``` #### Registry Revocation Check (v1alpha3) ```json theme={null} { "timestamp": "2026-02-19T10:30:00.000Z", "event": "REGISTRY_REVOCATION_CHECK", "mode": "cached", "revocation_list_version": 42, "agents_revoked": 3, "aats_revoked": 1, "sessions_revoked": 2 } ``` *** ## 13. Conformance ### 13.1 Conformance Levels | Level | Requirements | | -------------- | -------------------------------------------------------------- | | **Basic** | Method authorization, tool allowlist, error codes | | **Full** | Basic + argument validation, rate limiting, DLP, audit logging | | **Extended** | Full + Human-in-the-Loop (action=ask) | | **Identity** | Full + Identity tokens, session management | | **Server** | Identity + Server-side validation endpoints | | **AAT** | Server + AAT validation, registry integration *(new)* | | **Federation** | AAT + Token Issuer, user binding, delegation chain *(new)* | ### 13.2 Conformance Testing Implementations MUST pass the conformance test suite to claim AIP compliance. The test suite consists of: 1. **Schema validation tests**: Verify policy parsing 2. **Decision tests**: Input -> expected decision 3. **Normalization tests**: Verify Unicode handling 4. **Error format tests**: Verify JSON-RPC errors 5. **Identity tests**: Token lifecycle, rotation, validation 6. **Server tests**: HTTP endpoint behavior 7. **AAT tests**: AAT structure, validation, capability checking *(new)* 8. **Registry tests**: Agent lookup, revocation checking *(new)* 9. **Delegation tests**: User binding verification, scope enforcement *(new)* See `spec/conformance/` for test vectors. ### 13.3 Implementation Requirements Implementations MUST: * Parse `apiVersion: aip.io/v1alpha3` documents * Reject documents with unknown `apiVersion` * Apply NFKC normalization to names * Return specified error codes * Support `enforce` and `monitor` modes Implementations SHOULD: * Log decisions in the specified format * Support DLP scanning * Support rate limiting * Support identity tokens (for Identity conformance level) * Support AAT validation (for AAT conformance level) Implementations MAY: * Use any regex engine with RE2 semantics * Implement additional security features (egress control, sandboxing) * Implement server-side validation (for Server conformance level) * Implement a Token Issuer (for Federation conformance level) * Implement a Registry (for Federation conformance level) *** ## 14. Security Considerations ### 14.0 Threat Model *\[Section 14.0.1-14.0.3 remain unchanged from v1alpha2 with AAT additions]* #### 14.0.1 Trust Boundaries ``` ┌─────────────────────────────────────────────────────────────────┐ │ UNTRUSTED │ │ ┌──────────┐ │ │ │ Agent │ AI agent may be manipulated via prompt injection │ │ └────┬─────┘ │ │ │ │ ├───────┼─────────────────────────────────────────────────────────┤ │ │ TRUST BOUNDARY (AIP) │ │ ▼ │ │ ┌──────────────┐ │ │ │ AIP Policy │ Policy engine is TRUSTED │ │ │ Engine │ Policy file integrity assumed │ │ └──────┬───────┘ │ │ │ │ ├─────────┼───────────────────────────────────────────────────────┤ │ │ TRUST BOUNDARY (MCP) │ │ ▼ │ │ ┌──────────────┐ │ │ │ MCP Server │ Server behavior is UNTRUSTED │ │ │ │ Tool definitions may be malicious │ │ └──────────────┘ │ │ UNTRUSTED │ └─────────────────────────────────────────────────────────────────┘ ``` | Component | Trust Level | Rationale | | -------------------- | --------------------- | ----------------------------------------------------- | | **User** | Trusted | Defines policy, approves sensitive operations | | **Policy file** | Trusted | Integrity verified via signature | | **AIP Engine** | Trusted | Assumed correctly implemented | | **AIP Registry** | Trusted | Root of trust for agent identities *(new)* | | **Token Issuer** | Trusted | Issues AATs based on verified identity *(new)* | | **Agent (LLM)** | Untrusted | Subject to prompt injection, jailbreaks | | **Agent's AAT** | Conditionally Trusted | Trusted only after cryptographic verification *(new)* | | **MCP Server** | Untrusted | May be malicious or compromised | | **Tool definitions** | Untrusted | May contain poisoned descriptions | #### 14.0.2 Threats In Scope (v1alpha3 additions) | Threat | Attack Vector | AIP Mitigation | | ------------------------- | -------------------------------------- | -------------------------------------------------------- | | **AAT theft** | Stolen AAT used by different agent | Agent key thumbprint verification, JTI replay prevention | | **AAT forgery** | Attacker creates fake AAT | Cryptographic signature verification via issuer JWKS | | **User impersonation** | Agent claims different user | User binding verified through Token Issuer's auth flow | | **Capability escalation** | Agent requests tools beyond delegation | Capability intersection with local policy | | **Registry poisoning** | Attacker modifies agent records | Registry attestation signatures, mTLS | | **Issuer compromise** | Attacker issues unauthorized AATs | Trusted issuer list, key rotation, revocation | | **Delegation abuse** | Agent acts beyond user's intent | Delegation scope enforcement, short-lived AATs | #### 14.0.3 Defense in Depth (v1alpha3) ``` Request Flow: Agent Request (with AAT) │ ▼ ┌────────────────┐ │ 1. Method │ Block unauthorized JSON-RPC methods │ Check │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ 2. AAT │ Verify signature, issuer, expiry, │ Validation │ agent identity, user binding │ (v1alpha3) │ Check registry revocation list └───────┬────────┘ │ ▼ ┌────────────────┐ │ 3. AAT │ Check AAT capabilities against │ Capability │ requested tool │ Check │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ 4. Identity │ Validate session token, binding │ Check │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ 5. Rate Limit │ Prevent resource exhaustion │ Check │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ 6. Tool │ Allowlist enforcement │ Check │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ 7. Argument │ Regex validation, protected paths │ Check │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ 8. HITL │ Human approval for sensitive ops │ (if ask) │ └───────┬────────┘ │ ▼ MCP Server │ ▼ ┌────────────────┐ │ 9. DLP │ Redact sensitive response data │ Scan │ └───────┬────────┘ │ ▼ Agent Response ``` ### 14.1 Policy File Protection The policy file itself MUST be protected from modification by the agent. Implementations MUST automatically add the policy file path to `protected_paths`. ### 14.2 Regex Denial of Service (ReDoS) Implementations MUST use a regex engine that guarantees linear-time matching (RE2 or equivalent). ### 14.3 Unicode Normalization Implementations MUST apply NFKC normalization to prevent homoglyph attacks. ### 14.4 Monitor Mode Risks Monitor mode allows all requests through. Implementations SHOULD warn users when monitor mode is enabled in production environments. ### 14.5 Audit Log Integrity Audit logs SHOULD be written to a location not writable by the agent. ### 14.6 Identity Token Security *\[Remains unchanged from v1alpha2]* ### 14.7 Server Endpoint Security *\[Remains unchanged from v1alpha2]* ### 14.8 AAT Security (v1alpha3) #### 14.8.1 AAT Storage AATs SHOULD be stored in memory only, not persisted to disk. If persistence is required, AATs MUST be encrypted at rest. The `_aip_aat` field in JSON-RPC params MUST be stripped before any logging or forwarding. #### 14.8.2 AAT Transmission AATs transmitted over the network MUST use TLS 1.2 or later. Implementations MUST NOT send AATs over unencrypted connections. For HTTP transport, AATs MUST be sent as headers (not query parameters or request body) to prevent leakage in server logs. #### 14.8.3 AAT Lifetime AAT lifetimes SHOULD be limited: | Environment | Recommended `exp - iat` | Rationale | | -------------------- | ------------------------- | --------------------- | | Interactive (IDE) | 1 hour | Session-length | | Batch processing | Duration of job | Tight scoping | | Long-running service | 15 minutes (with refresh) | Minimize theft window | | CI/CD pipeline | Duration of pipeline | Job-scoped | Implementations SHOULD reject AATs with lifetime greater than 24 hours. #### 14.8.4 Replay Prevention AAT replay prevention uses the `jti` (JWT ID) claim: ``` ATOMIC_CHECK_AND_RECORD_JTI(jti): # Same atomic semantics as nonce checking IF ATOMIC_SET_IF_NOT_EXISTS(jti, ttl=max_token_age): RETURN TRUE # JTI was new ELSE: RETURN FALSE # JTI already seen (replay attempt) ``` JTI storage requirements follow the same guidelines as nonce storage (Section 5 / v1alpha2). #### 14.8.5 Registry Trust The AIP Registry is a high-value target. Implementations MUST: * Use TLS for all registry communications * Verify the registry's TLS certificate * Support mTLS for registry authentication * Cache registry responses with bounded TTL * Have a fallback strategy when the registry is unreachable (see `failover_mode`) #### 14.8.6 Token Issuer Trust Token Issuers control what capabilities agents receive. Compromised issuers can grant excessive permissions. Mitigations: * Use `trusted_issuers` to limit accepted issuers * Intersect AAT capabilities with local policy (`capabilities_mode: "intersect"`) * Monitor `aip_aat_validations_total` for anomalies * Implement issuer key rotation and revocation *** ## 15. IANA Considerations This specification requests registration of the following: ### 15.1 Media Type * Type name: application * Subtype name: vnd.aip.policy+yaml * Required parameters: None * File extension: .yaml, .yml ### 15.2 URI Scheme This specification uses the `aip.io` namespace for versioning: * `aip.io/v1alpha1` - Initial specification * `aip.io/v1alpha2` - Identity and server-side validation * `aip.io/v1alpha3` - This specification (AAT, Registry, Token Issuer) ### 15.3 JWT Type Header (v1alpha3) * `typ`: `aat+jwt` for Agent Authentication Tokens *** ## Appendix A: Complete Schema Reference ```yaml theme={null} # Complete AgentPolicy schema (v1alpha3) apiVersion: aip.io/v1alpha3 # REQUIRED kind: AgentPolicy # REQUIRED metadata: # REQUIRED name: string # REQUIRED - Policy identifier version: string # OPTIONAL - Semantic version owner: string # OPTIONAL - Contact email signature: string # OPTIONAL - Policy signature spec: # REQUIRED mode: enforce | monitor # OPTIONAL, default: enforce allowed_tools: # OPTIONAL - string allowed_methods: # OPTIONAL - string denied_methods: # OPTIONAL - string protected_paths: # OPTIONAL - string strict_args_default: boolean # OPTIONAL, default: false tool_rules: # OPTIONAL - tool: string # REQUIRED action: allow|block|ask # OPTIONAL, default: allow rate_limit: string # OPTIONAL, format: "N/period" strict_args: boolean # OPTIONAL schema_hash: string # OPTIONAL - Tool schema integrity allow_args: # OPTIONAL : dlp: # OPTIONAL enabled: boolean # OPTIONAL, default: true scan_requests: boolean # OPTIONAL, default: false scan_responses: boolean # OPTIONAL, default: true detect_encoding: boolean # OPTIONAL, default: false filter_stderr: boolean # OPTIONAL, default: false max_scan_size: string # OPTIONAL, default: "1MB" on_request_match: string # OPTIONAL, default: "block" on_redaction_failure: string # OPTIONAL, default: "block" log_original_on_failure: boolean # OPTIONAL, default: false patterns: # REQUIRED if dlp present - name: string # REQUIRED regex: string # REQUIRED scope: string # OPTIONAL, default: "all" identity: # OPTIONAL enabled: boolean # OPTIONAL, default: false token_ttl: string # OPTIONAL, default: "5m" rotation_interval: string # OPTIONAL, default: "4m" require_token: boolean # OPTIONAL, default: false session_binding: string # OPTIONAL, default: "process" nonce_window: string # OPTIONAL, default: equals token_ttl policy_transition_grace: string # OPTIONAL, default: "0s" audience: string # OPTIONAL, default: metadata.name nonce_storage: # OPTIONAL type: string # memory | redis | postgres address: string key_prefix: string # default: "aip:nonce:" clock_skew_tolerance: string # default: "30s" keys: # OPTIONAL signing_algorithm: string # default: "ES256" key_source: string # generate | file | external key_path: string rotation_period: string # default: "7d" jwks_endpoint: string # default: "/v1/jwks" server: # OPTIONAL enabled: boolean # OPTIONAL, default: false listen: string # OPTIONAL, default: "127.0.0.1:9443" failover_mode: string # OPTIONAL, default: "fail_closed" timeout: string # OPTIONAL, default: "5s" tls: cert: string key: string fail_open_constraints: # OPTIONAL allowed_tools: - string max_duration: string max_requests: integer alert_webhook: string require_local_policy: boolean endpoints: # OPTIONAL validate: string # default: "/v1/validate" revoke: string # default: "/v1/revoke" jwks: string # default: "/v1/jwks" health: string # default: "/health" metrics: string # default: "/metrics" registry: # OPTIONAL (v1alpha3) enabled: boolean # OPTIONAL, default: false endpoint: string # REQUIRED if enabled tls: ca_cert: string client_cert: string client_key: string cache: enabled: boolean # default: true ttl: string # default: "5m" max_entries: integer # default: 10000 revocation: check_interval: string # default: "30s" mode: string # online | cached | crl crl_path: string auth: type: string # bearer | mtls | api_key token: string api_key: string aat: # OPTIONAL (v1alpha3) enabled: boolean # OPTIONAL, default: false require: boolean # OPTIONAL, default: false validation: verify_signature: boolean # default: true verify_user_binding: boolean # default: true verify_capabilities: boolean # default: true max_token_age: string # default: "1h" clock_skew: string # default: "30s" capabilities_mode: string # intersect | aat_only | policy_only trusted_issuers: # OPTIONAL - string header_name: string # default: "X-AIP-AAT" ``` *** ## Appendix B: Changelog ### v1alpha3 (2026-02-19) **Agent Authentication Token (AAT)** * Added Section 7: Agent Authentication Token specification * JWT-based AAT structure with agent, user\_binding, capabilities, and context claims * AAT signing requirements (ES256, EdDSA, RS256; no symmetric algorithms) * AAT lifecycle: issuance, refresh, validation, revocation * AAT transport via `_aip_aat` JSON-RPC param or `X-AIP-AAT` HTTP header * AAT validation algorithm with 9-step verification * JTI-based replay prevention * Added `aat` configuration section (Section 3.10) * `capabilities_mode`: intersect, aat\_only, policy\_only * `trusted_issuers` for issuer allowlisting * Configurable validation strictness **AIP Registry** * Added Section 8: AIP Registry specification * Agent Identity Document (AID) structure with registry attestation * Registry API: registration, lookup, revocation list, key rotation, JWKS * Agent status lifecycle: active, suspended, revoked * Registry attestation via Ed25519/ES256 signatures * Added `registry` configuration section (Section 3.9) * Revocation modes: online, cached, CRL * Cache configuration for agent keys and revocation status * mTLS and bearer token authentication **Token Issuer** * Added Section 9: Token Issuer specification * Token issuance flow with proof of possession * Grant types: agent\_authentication, aat\_refresh, agent\_attestation * User authorization methods: OAuth 2.0, OIDC, API key, local attestation * Capability determination from agent record, user permissions, and requested capabilities * Token Issuer JWKS endpoint **User Binding and Delegation** * Added Section 10: User Binding and Delegation specification * Delegation model connecting users to agents to actions * Delegation chain verification algorithm * Delegation scope enforcement (full, tools, read\_only, session, custom) * User binding revocation propagation * Audit trail integration with user identity **Evaluation Semantics** * Updated tool-level authorization (Section 4.3) with AAT capability checks * Step 0b: AAT validation * Step 0c: AAT capability intersection * Added AAT\_REQUIRED, AAT\_INVALID, AAT\_CAPABILITY\_DENIED decision outcomes **Error Codes** * Added -32015 AAT Required * Added -32016 AAT Invalid (with 13 detailed error subtypes) * Added -32017 AAT Capability Denied * Added -32018 Agent Not Registered * Added -32019 Delegation Expired * Added -32020 Issuer Untrusted **Metrics** * Added `aip_aat_validations_total` counter * Added `aip_registry_checks_total` counter * Added `aip_registry_latency_seconds` histogram * Added `aip_active_agents` gauge **Audit Logging** * Added AAT-enriched audit log fields (agent\_id, agent\_name, user\_id, user\_auth\_method, delegation\_scope, aat\_jti, aat\_issuer) * Added AAT\_VALIDATED, AAT\_REJECTED, REGISTRY\_REVOCATION\_CHECK event types **Security** * Updated threat model with AAT-specific threats (theft, forgery, impersonation, capability escalation, registry poisoning, issuer compromise, delegation abuse) * Added Section 14.8: AAT Security (storage, transmission, lifetime, replay prevention, registry trust, issuer trust) * Defense in depth expanded to 9 layers (added AAT validation, AAT capability check) **Conformance** * Added AAT conformance level * Added Federation conformance level * Added AAT, registry, and delegation test categories ### v1alpha2 (2026-01-24) * Added identity configuration section (token generation, rotation, session binding) * Added server-side validation endpoints * Added `schema_hash` for tool poisoning prevention * Added DLP enhancements (scan\_requests, max\_scan\_size, on\_request\_match) * Added threat model (Section 10.0) * Added policy signature (metadata.signature) * Added error codes -32008 through -32014 * Added Identity and Server conformance levels ### v1alpha1 (2026-01-20) * Initial draft specification * Defined core policy schema * Defined evaluation semantics * Defined error codes * Defined audit log format *** ## Appendix C: References * [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) * [MCP Authorization (2025-06-18)](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization) * [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification) * [RFC 2119 - Key words for use in RFCs](https://www.rfc-editor.org/rfc/rfc2119) * [RFC 6750 - The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://www.rfc-editor.org/rfc/rfc6750) * [RFC 7517 - JSON Web Key (JWK)](https://www.rfc-editor.org/rfc/rfc7517) * [RFC 7519 - JSON Web Token (JWT)](https://www.rfc-editor.org/rfc/rfc7519) * [RFC 7638 - JSON Web Key (JWK) Thumbprint](https://www.rfc-editor.org/rfc/rfc7638) * [RFC 8707 - Resource Indicators for OAuth 2.0](https://www.rfc-editor.org/rfc/rfc8707) * [RFC 8785 - JSON Canonicalization Scheme (JCS)](https://www.rfc-editor.org/rfc/rfc8785) * [Unicode NFKC Normalization](https://unicode.org/reports/tr15/) * [RE2 Syntax](https://github.com/google/re2/wiki/Syntax) * [Agentic JWT (draft-goswami-agentic-jwt-00)](https://datatracker.ietf.org/doc/html/draft-goswami-agentic-jwt-00) *** ## Appendix D: Future Extensions This appendix describes features under consideration for future versions of AIP. ### D.1 Network Egress Control **Status:** Proposed for v1beta1 ### D.2 Policy Inheritance **Status:** Under Discussion Allow policies to extend base policies: ```yaml theme={null} apiVersion: aip.io/v1beta1 kind: AgentPolicy metadata: name: team-policy spec: extends: "org-base-policy" allowed_tools: - additional_tool ``` ### D.3 External Identity Federation **Status:** Proposed for v1beta1 Allow policies to integrate with external identity providers: ```yaml theme={null} spec: identity: federation: type: oidc issuer: "https://accounts.google.com" client_id: "aip-agent" required_claims: email_verified: true hd: "company.com" ``` Supported federation types: * `oidc` - OpenID Connect providers * `spiffe` - SPIFFE/SPIRE workload identity ### D.4 Telemetry and Metrics **Status:** Partially implemented in v1alpha2/v1alpha3 (metrics endpoint) ### D.5 Advanced Policy Expressions **Status:** Under Discussion Support for CEL (Common Expression Language) or Rego for complex validation: ```yaml theme={null} tool_rules: - tool: file_write action: allow when: | args.path.startsWith("/allowed/") && !args.path.contains("..") && size(args.content) < 1048576 ``` ### D.6 Agentic JWT Compatibility **Status:** Under Discussion for v1beta1 Full compatibility with the Agentic JWT specification. Mapping to Agentic JWT claims: | AIP Field | Agentic JWT Claim | | -------------------------- | ---------------------------- | | `aat.agent.id` | `sub` (subject) | | `aat.context.policy_hash` | `agent_proof.agent_checksum` | | `aat.context.session_id` | `intent.workflow_id` | | `aat.capabilities.tools` | Workflow steps | | `aat.user_binding.user_id` | `azp` (authorized party) | ### D.7 Multi-Agent Delegation (v1alpha3 future) **Status:** Under Discussion Support for agent-to-agent delegation chains, where Agent A (with user authorization) delegates a subset of capabilities to Agent B: ```json theme={null} { "delegation_chain": [ { "delegator": "user@example.com", "delegatee": "ag_agent-a", "scope": "tools" }, { "delegator": "ag_agent-a", "delegatee": "ag_agent-b", "scope": "read_only" } ] } ``` Constraints: * Each delegation step MUST reduce or maintain scope (never escalate) * Maximum chain depth: 3 (user -> agent -> sub-agent) * All delegators must be active and non-revoked ### D.8 Registry Federation **Status:** Under Discussion Allow multiple registries to federate, enabling cross-organization agent identity verification: ```yaml theme={null} registry: federation: trusted_registries: - id: "partner-registry" endpoint: "https://registry.partner.com" trust_level: "tools_only" # Only trust tool capabilities ``` *** ## Appendix E: Implementation Notes ### E.1 Reference Implementation The reference implementation is available at: [https://github.com/openagentidentityprotocol/aip-go](https://github.com/openagentidentityprotocol/aip-go) It provides: * Go-based proxy (`aip-proxy`) * Policy engine (`pkg/policy`) * DLP scanner (`pkg/dlp`) * Audit logger (`pkg/audit`) * Identity manager (`pkg/identity`) * HTTP server (`pkg/server`) * AAT validator (`pkg/aat`) *(v1alpha3)* * Registry client (`pkg/registry`) *(v1alpha3)* ### E.2 Testing Against Conformance Suite ```bash theme={null} # Clone the spec repository git clone https://github.com/openagentidentityprotocol/agentidentityprotocol # Run conformance tests against your implementation cd agentidentityprotocol/spec/conformance ./run-tests.sh --impl "your-aip-binary" --level "aat" ``` ### E.3 AAT Implementation Guidance #### Generating Agent Key Pair ```go theme={null} import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" ) func generateAgentKeyPair() (*ecdsa.PrivateKey, error) { return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } ``` #### Computing JWK Thumbprint (RFC 7638) ```go theme={null} import ( "crypto/sha256" "encoding/json" ) func jwkThumbprint(jwk map[string]interface{}) string { // For EC keys: {"crv":"...","kty":"EC","x":"...","y":"..."} required := map[string]interface{}{ "crv": jwk["crv"], "kty": jwk["kty"], "x": jwk["x"], "y": jwk["y"], } canonical, _ := json.Marshal(required) hash := sha256.Sum256(canonical) return base64url(hash[:]) } ``` #### Validating an AAT ```go theme={null} func validateAAT(tokenString string, trustedIssuers []string, registry RegistryClient) (*AATClaims, error) { // 1. Parse JWT header (don't verify yet) header, err := parseJWTHeader(tokenString) if err != nil { return nil, fmt.Errorf("malformed_aat: %w", err) } // 2. Get issuer's public key via JWKS issuerKey, err := getIssuerKey(header.Kid, trustedIssuers) if err != nil { return nil, fmt.Errorf("unknown_signing_key: %w", err) } // 3. Verify signature claims, err := jwt.ParseWithClaims(tokenString, &AATClaims{}, func(t *jwt.Token) (interface{}, error) { return issuerKey, nil }) if err != nil { return nil, fmt.Errorf("signature_invalid: %w", err) } // 4. Check registry for agent status and revocation agent, err := registry.GetAgent(claims.Agent.ID) if err != nil { return nil, fmt.Errorf("unknown_agent: %w", err) } if agent.Status != "active" { return nil, fmt.Errorf("agent_inactive: %s", agent.Status) } // 5. Verify agent key thumbprint if agent.PublicKeyThumbprint != claims.Agent.PublicKeyThumbprint { return nil, fmt.Errorf("agent_key_mismatch") } // 6. Check JTI for replay if !atomicRecordJTI(claims.JTI) { return nil, fmt.Errorf("replay_detected") } return claims, nil } ``` ### E.4 Registering Your Implementation Implementations that pass the conformance suite may be listed in the official registry. Submit a PR to the AIP repository with: * Implementation name and URL * Conformance level achieved (Basic/Full/Extended/Identity/Server/AAT/Federation) * Platform support matrix # Why AIP? The Problem with AI Agent Security Source: https://agentidentityprotocol.io/why-aip This document explains the security challenges that AIP addresses and why existing solutions are insufficient. ## The Problem: God Mode by Default Modern AI agents operate with **unrestricted access** to powerful tools. When you grant an LLM access to your GitHub account, database, or cloud infrastructure, you're not just giving it an API key—you're granting **unbounded intent execution** with no policy layer. ## The Threat Model | Threat | Description | Real-World Example | | ----------------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | **Indirect Prompt Injection** | Malicious instructions embedded in data the agent processes | [*GeminiJack*](https://embrace-the-red.com/blog/gemini-jack/) (2024): Attackers embedded prompts in Google Docs that hijacked Gemini's actions | | **Consent Fatigue** | Users approve broad permissions without understanding scope | "Allow GitHub access" grants `repo:delete`, not just `repo:read` | | **Shadow AI** | Agents operating outside enterprise security boundaries | Developers running local Copilot instances with production credentials | | **Privilege Escalation** | Agents accumulating permissions across tool calls | Agent chains: Slack → Calendar → Email → sends unauthorized messages | | **Data Exfiltration** | Sensitive data leaving through unmonitored egress | Agent "summarizing" code by posting to external APIs | ## API Keys Are for Code. AIP Is for Intent. Traditional security assumes **deterministic code execution**. API keys authenticate the *application*. But LLMs are non-deterministic systems executing *user intent* through *model interpretation*. ``` Traditional: Code → API Key → Resource └── Deterministic, auditable, predictable Agent World: User Intent → LLM Interpretation → Tool Call → Resource └── Non-deterministic, opaque, emergent behavior ``` **We need a security primitive that authenticates and authorizes *intent*, not just identity.** ## Security Comparison | Aspect | Standard MCP | AIP-Enabled MCP | | --------------------- | ------------------------------ | --------------------------------- | | **Authentication** | Static API keys | Short-lived OIDC tokens | | **Authorization** | None (full access) | Per-action policy check | | **Scope** | Implicit (whatever key allows) | Explicit manifest declaration | | **Audit** | Application logs (if any) | Immutable, structured audit trail | | **Egress Control** | None | Network-level filtering (planned) | | **Revocation** | Rotate API keys | Instant token/session revocation | | **Human-in-the-Loop** | Not supported | Configurable approval gates | | **Blast Radius** | Unlimited | Scoped to manifest | | **Compliance** | Manual attestation | Policy-as-code, auditable | ## Why Not Just Use...? ### OAuth Scopes? OAuth scopes are: * **Coarse-grained**: "repo access" vs "read pull requests from org X" * **Static**: Granted at install time, can't change per-session * **User-facing**: Leads to consent fatigue AIP policies are: * **Fine-grained**: Per-tool, per-argument validation * **Dynamic**: Can change without re-authentication * **Developer-controlled**: Defined in config files, version-controlled ### Service Mesh (Istio)? Service meshes operate at the **service level**, not the **action level**. They can say "Service A can call Service B" but not "Agent can only call `repos.get` with `org:mycompany/*`". AIP operates at the **tool call level** within a service. ### Container Sandboxing? Containers provide **process isolation** but not **semantic authorization**. A containerized agent with network access can still exfiltrate data. AIP provides **policy-based authorization** that understands what the agent is *trying to do*. ## Comparison Table | Approach | Authentication | Authorization | Audit | Revocation | | ------------------------ | ---------------- | ----------------- | --------------- | -------------------- | | **Raw API Keys** | Static token | None | App logs | Rotate everywhere | | **OAuth Scopes** | Token-based | Coarse-grained | Varies | Token expiry | | **Service Mesh (Istio)** | mTLS | Service-level | Yes | Certificate rotation | | **AIP** | Short-lived OIDC | Per-action policy | Immutable trail | Instant session kill | AIP is purpose-built for the unique challenge of non-deterministic AI agents executing user intent. ## Architecture Principles 1. **Defense in Depth**: Multiple independent security layers (identity, policy, egress, audit) 2. **Least Privilege by Default**: Agents start with zero capabilities; everything is opt-in 3. **Fail Closed**: Unknown actions are denied; network errors = deny 4. **Immutable Audit**: All decisions logged; logs cannot be modified by agents 5. **Human Sovereignty**: Critical actions require human approval 6. **Manifest Portability**: Same manifest works across runtimes (local, Kubernetes, serverless) ## Prior Art & Inspiration AIP builds on established security patterns: * **[SPIFFE/SPIRE](https://spiffe.io/)**: Workload identity framework — AIP extends this to agent identity * **[Open Policy Agent](https://www.openpolicyagent.org/)**: Policy-as-code — AIP's policy engine draws from OPA's design * **[Istio](https://istio.io/)**: Service mesh authorization — AIP applies mesh principles to agent traffic * **[AWS IAM](https://aws.amazon.com/iam/)**: Fine-grained permissions — AIP manifests are IAM policies for agents * **[OAuth 2.0 / OIDC](https://openid.net/connect/)**: Token-based identity — AIP leverages OIDC for federation ## Next Steps * **Read the specification**: [AIP-v1alpha3 Specs](specs/aip-v1alpha3) * **Try the reference implementation**: [AIP Go Implementation](https://github.com/openagentidentityprotocol/aip-go) * **Write your first policy**: [Policy Reference](layer-2-enforcement/policy-reference)