Version: v1alpha2
Status: Draft
Last Updated: 2026-01-24
Authors: Eduardo Arango (arangogutierrez@gmail.com)
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:
- The policy document schema (
AgentPolicy) - Evaluation semantics for authorization decisions
- Agent identity and session management (new in v1alpha2)
- Server-side validation endpoints (new in v1alpha2)
- Error codes for denied requests
- 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.
- Introduction
- Terminology
- Policy Document Schema
- Evaluation Semantics
- Agent Identity (new in v1alpha2)
- Server-Side Validation (new in v1alpha2)
- Error Codes
- Audit Log Format
- Conformance
- Security Considerations
- IANA Considerations
Appendices
- Appendix A: Complete Schema Reference
- Appendix B: Changelog
- Appendix C: References
- Appendix D: Future Extensions
- Appendix E: Implementation Notes
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)
- Interoperability: Any MCP runtime can implement AIP
- Simplicity: YAML-based policies readable by security teams
- Defense in depth: Multiple layers (method, tool, argument, identity)
- Fail-closed: Unknown tools are denied by default
- Zero-trust ready: Support for token-based identity verification (new in v1alpha2)
The following are explicitly out of scope for this version of the specification:
- Network egress control (see Appendix D: Future Extensions)
- Subprocess sandboxing (implementation-defined)
- External identity federation (OIDC/SPIFFE - see Appendix D)
- Rate limiting algorithms (implementation-defined)
- Policy expression languages beyond regex (CEL/Rego - see Appendix D)
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 │
└─────────────┘
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.
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.
| 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) |
An AIP policy document is a YAML file with the following top-level structure:
apiVersion: aip.io/v1alpha2
kind: AgentPolicy
metadata:
name: <string>
version: <string> # OPTIONAL
owner: <string> # OPTIONAL
signature: <string> # OPTIONAL (v1alpha2)
spec:
mode: <string> # OPTIONAL, default: "enforce"
allowed_tools: [<string>] # OPTIONAL
allowed_methods: [<string>] # OPTIONAL
denied_methods: [<string>] # OPTIONAL
tool_rules: [<ToolRule>] # OPTIONAL
protected_paths: [<string>] # OPTIONAL
strict_args_default: <bool> # OPTIONAL, default: false
dlp: <DLPConfig> # OPTIONAL
identity: <IdentityConfig> # OPTIONAL (v1alpha2)
server: <ServerConfig> # OPTIONAL (v1alpha2)| Field | Type | Description |
|---|---|---|
apiVersion |
string | MUST be aip.io/v1alpha2 |
kind |
string | MUST be AgentPolicy |
metadata.name |
string | Unique identifier for this policy |
metadata:
name: <string> # REQUIRED - Policy identifier
version: <string> # OPTIONAL - Semantic version (e.g., "1.0.0")
owner: <string> # OPTIONAL - Contact email
signature: <string> # OPTIONAL - Policy signature (v1alpha2)The signature field provides cryptographic integrity verification for the policy document.
Format: <algorithm>:<base64-encoded-signature>
Supported algorithms:
ed25519- Ed25519 signature (RECOMMENDED)
Example:
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).
[Sections 3.4.1 through 3.6 remain unchanged from v1alpha1]
Controls enforcement behavior.
| Value | Behavior |
|---|---|
enforce |
Violations are blocked (default) |
monitor |
Violations are logged but allowed |
Implementations MUST support both modes.
A list of tool names that the agent MAY invoke.
allowed_tools:
- github_get_repo
- read_file
- list_directoryTool names are subject to normalization (see Section 4.1).
A list of JSON-RPC methods that are permitted. If not specified, implementations MUST use the default safe list:
# 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
- cancelledThe wildcard * MAY be used to allow all methods.
A list of JSON-RPC methods that are explicitly denied. Denied methods take precedence over allowed methods.
denied_methods:
- resources/read
- resources/writeA list of file paths that tools MUST NOT access. Any tool argument containing a protected path MUST be blocked.
protected_paths:
- ~/.ssh
- ~/.aws/credentials
- .envImplementations MUST:
- Expand
~to the user's home directory - Automatically protect the policy file itself
When true, tool rules reject any arguments not explicitly declared in allow_args.
Default: false
Tool rules provide fine-grained control over specific tools.
tool_rules:
- tool: <string> # REQUIRED - Tool name
action: <string> # OPTIONAL - allow|block|ask (default: allow)
rate_limit: <string> # OPTIONAL - e.g., "10/minute"
strict_args: <bool> # OPTIONAL - Override strict_args_default
schema_hash: <string> # OPTIONAL - Tool schema integrity (v1alpha2)
allow_args: # OPTIONAL
<arg_name>: <regex>| Action | Behavior |
|---|---|
allow |
Permit (subject to argument validation) |
block |
Deny unconditionally |
ask |
Require interactive user approval |
Format: <count>/<period>
| 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.).
The allow_args field maps argument names to regex patterns.
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
The schema_hash field provides cryptographic verification of tool definitions to prevent tool poisoning attacks.
Format: <algorithm>:<hex-digest>
Supported algorithms:
sha256(RECOMMENDED)sha384sha512
Example:
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:
- Tool poisoning prevention: Detect when an MCP server changes a tool's behavior after policy approval
- Compliance auditing: Prove that approved tools haven't been modified
- Supply chain security: Pin specific tool versions in policy
Generating schema hashes:
# 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_fileOperational 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) |
Data Loss Prevention (DLP) scans for sensitive data in requests and responses.
dlp:
enabled: <bool> # OPTIONAL, default: true when dlp block present
scan_requests: <bool> # OPTIONAL, default: false (v1alpha2)
scan_responses: <bool> # OPTIONAL, default: true
detect_encoding: <bool> # OPTIONAL, default: false
filter_stderr: <bool> # OPTIONAL, default: false
max_scan_size: <string> # OPTIONAL, default: "1MB" (v1alpha2)
on_request_match: <string> # OPTIONAL, default: "block" (v1alpha2)
patterns:
- name: <string> # REQUIRED - Rule identifier
regex: <string> # REQUIRED - Detection pattern
scope: <string> # OPTIONAL, default: "all" (request|response|all)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).
When true, DLP patterns are applied to tool responses.
Default: true
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_sizebytes) - MUST log a warning
Purpose: Prevents ReDoS and memory exhaustion on large payloads.
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:
- Invalid JSON: Redaction in nested structures may break JSON parsing
- Schema validation failure: Redacted values may violate tool argument schemas
- Tool execution failure: The MCP server may reject redacted arguments
Configuration for redaction failure behavior:
dlp:
scan_requests: true
on_request_match: "redact"
on_redaction_failure: <string> # OPTIONAL, default: "block" (v1alpha2)
log_original_on_failure: <bool> # 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:
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:
{
"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:
{
"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"
}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
Patterns can be scoped to requests, responses, or both:
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:<name>]
The identity section configures agent identity and token management.
spec:
identity:
enabled: <bool> # OPTIONAL, default: false
token_ttl: <duration> # OPTIONAL, default: "5m"
rotation_interval: <duration> # OPTIONAL, default: "4m"
require_token: <bool> # OPTIONAL, default: false
session_binding: <string> # OPTIONAL, default: "process"
nonce_window: <duration> # OPTIONAL, default: equals token_ttl (v1alpha2)
policy_transition_grace: <duration> # OPTIONAL, default: "0s" (v1alpha2)
audience: <string> # OPTIONAL, default: policy metadata.name (v1alpha2)
nonce_storage: <NonceStorageConfig> # OPTIONAL (v1alpha2)
keys: <KeyConfig> # OPTIONAL (v1alpha2)When true, the AIP engine generates and manages identity tokens for the session.
Default: false
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.
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:
# INVALID: rotation_interval >= token_ttl
identity:
enabled: true
token_ttl: "5m"
rotation_interval: "6m" # ERROR: must be < 5mPolicy load error response:
{
"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.
identity:
enabled: true
token_ttl: "5m"
rotation_interval: "0s" # No automatic rotationWhen 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.
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 |
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_windowMUST be greater than or equal totoken_ttl- Setting
nonce_windowless thantoken_ttlis 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:
identity:
enabled: true
token_ttl: "5m"
nonce_window: "10m" # Retain nonces for 2x TTLThe 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:
- When policy is updated, the previous policy hash is retained in
recent_policy_hashes - Tokens with either current or recent policy hash are accepted during the grace period
- After grace period expires, only current policy hash is valid
Constraints:
policy_transition_graceSHOULD be less thantoken_ttlto ensure policy changes take effect within one token lifetime- Setting very long grace periods weakens security guarantees
Example:
identity:
enabled: true
token_ttl: "5m"
policy_transition_grace: "2m" # Accept old policy hash for 2 minutesUse 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 |
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:
identity:
enabled: true
audience: "https://mcp.example.com/api"Validation requirements:
- Implementations MUST reject tokens where
auddoes 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:
audienceMUST be a valid URI or the policymetadata.name- Empty string is NOT valid; use default (metadata.name) instead
Configuration for distributed nonce storage, required for multi-instance deployments.
spec:
identity:
nonce_storage:
type: <string> # OPTIONAL, default: "memory"
address: <string> # REQUIRED if type != "memory"
key_prefix: <string> # OPTIONAL, default: "aip:nonce:"
clock_skew_tolerance: <duration> # 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:
# 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_"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.
The server section configures optional HTTP endpoints for server-side validation.
spec:
server:
enabled: <bool> # OPTIONAL, default: false
listen: <string> # OPTIONAL, default: "127.0.0.1:9443"
failover_mode: <string> # OPTIONAL, default: "fail_closed" (v1alpha2)
timeout: <duration> # OPTIONAL, default: "5s" (v1alpha2)
tls: # OPTIONAL
cert: <string> # Path to TLS certificate
key: <string> # Path to TLS private key
endpoints: # OPTIONAL
validate: <string> # Validation endpoint path (default: "/v1/validate")
revoke: <string> # Revocation endpoint path (default: "/v1/revoke")
health: <string> # Health check path (default: "/health")
metrics: <string> # Metrics endpoint path (default: "/metrics")When true, the AIP engine starts an HTTP server for remote validation.
Default: false
The address and port to bind the HTTP server.
Format: <host>:<port> or :<port>
Default: "127.0.0.1:9443" (localhost only)
0.0.0.0 exposes the validation endpoint to the network. Implementations MUST require TLS when listen address is not localhost.
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):
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):
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:
server:
failover_mode: "fail_open"
fail_open_constraints: # RECOMMENDED when fail_open
allowed_tools: [<string>] # Only these tools fail-open
max_duration: <duration> # Auto-revert to fail_closed
max_requests: <int> # Max requests before fail_closed
alert_webhook: <string> # Notify on fail_open activation
require_local_policy: <bool> # 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:
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: trueBehavior:
- When validation server becomes unreachable:
- Increment fail_open counter
- Check if
max_requestsexceeded → revert to fail_closed - Check if
max_durationexceeded → revert to fail_closed - If request tool NOT in
allowed_tools→ block with -32001 - If
require_local_policyand no valid local policy → block with -32001 - POST to
alert_webhook(async, fire-and-forget) - Allow request, log warning
Implementation requirements:
- Implementations SHOULD warn at policy load time if
fail_openis 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):
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
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:
server:
enabled: true
timeout: "3s" # Shorter timeout for latency-sensitive apps
failover_mode: "local_policy"When the listen address is not localhost (127.0.0.1 or ::1), TLS MUST be configured.
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)
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) |
[Sections 4.1 through 4.5 remain unchanged from v1alpha1]
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:
delete→delete
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
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
| 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) |
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
This section defines the agent identity model introduced in v1alpha2.
Agent identity provides:
- Session binding: Cryptographic proof that requests belong to the same session
- Policy integrity: Verification that the policy hasn't changed mid-session
- Replay prevention: Nonces prevent token reuse across sessions
- Audit correlation: Session IDs link related audit events
The policy hash uniquely identifies a policy configuration.
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
POLICY_HASH(policy):
canonical = CANONICALIZE(policy)
hash = SHA-256(canonical)
RETURN hex_encode(hash)
The policy hash is a 64-character lowercase hexadecimal string.
An AIP Identity Token is a JWT-like structure (but NOT necessarily JWT-encoded) with the following fields:
{
"version": "aip/v1alpha2",
"aud": "<audience-uri>",
"policy_hash": "<64-char-hex>",
"session_id": "<uuid>",
"agent_id": "<policy-metadata-name>",
"issued_at": "<ISO-8601>",
"expires_at": "<ISO-8601>",
"nonce": "<random-hex>",
"binding": {
"process_id": <int>,
"policy_path": "<string>",
"hostname": "<string>"
}
}| 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) |
The binding object ties tokens to their execution context:
{
"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:<id> |
container_id |
| Kubernetes | k8s:<pod-uid> |
pod_uid, container_id |
| Serverless | lambda:<request-id> |
Implementation-defined |
Kubernetes deployment:
For Kubernetes deployments, inject pod UID via downward API:
env:
- name: POD_UID
valueFrom:
fieldRef:
fieldPath: metadata.uid
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.nameSession 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:
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:
identity:
session_binding: "policy" # Don't bind to ephemeral pod identity
require_token: true
audience: "https://my-mcp-server.svc.cluster.local"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:
{
"alg": "ES256",
"typ": "aip+jwt"
}Supported signing algorithms (in order of preference):
ES256(ECDSA with P-256 and SHA-256) - RECOMMENDED for productionEdDSA(Ed25519) - RECOMMENDED for performanceHS256(HMAC-SHA256) - MAY be used only whenserver.enabled: false
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.
┌──────────────┐
│ Session │
│ Start │
└──────┬───────┘
│
▼
┌──────────────┐ ┌──────────────┐
│ Issue │────▶│ Active │
│ Token │ │ Token │
└──────────────┘ └──────┬───────┘
│
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Rotation │ │ Expired │ │ Session │
│ (new token)│ │ (reject) │ │ End │
└──────────────┘ └──────────────┘ └──────────────┘
Tokens are issued when:
- Session starts (first tool call with
identity.enabled: true) - Rotation interval elapsed
- Policy changes (new policy_hash)
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
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)
A session starts when:
- The AIP engine loads a policy with
identity.enabled: true - A new process starts with AIP configured
A session ends when:
- The AIP engine process terminates
- The policy is unloaded or changed significantly
- Explicit session termination (implementation-defined)
Session IDs MUST be:
- UUID v4 (random) - RECOMMENDED
- Globally unique
- Not predictable
Revocation allows immediate invalidation of tokens or sessions before their natural expiration.
| Target | Scope | Use Case |
|---|---|---|
| Token (by nonce) | Single token | Suspected token compromise |
| Session (by session_id) | All tokens in session | User logout, session termination |
Implementations MUST maintain a revocation set containing:
{
"revoked_sessions": ["<session_id>", ...],
"revoked_tokens": ["<nonce>", ...]
}Storage requirements:
- Revoked sessions SHOULD be retained for
max_session_duration(implementation-defined, default: 24h) - Revoked tokens SHOULD be retained for
nonce_windowduration (then naturally expire)
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)
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)
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:
- Computing
agent_checksumfrom policy content - Including
agent_proofclaims in JWT tokens - Supporting the
agent_checksumOAuth grant type
See Appendix D.6 for mapping details.
This section defines key management requirements for JWT signing when server.enabled: true.
spec:
identity:
keys: # OPTIONAL (v1alpha2)
signing_algorithm: <string> # OPTIONAL, default: "ES256"
key_source: <string> # OPTIONAL, default: "generate"
key_path: <string> # REQUIRED if key_source is "file"
rotation_period: <duration> # OPTIONAL, default: "7d"
jwks_endpoint: <string> # 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) |
| 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 |
HS256 uses symmetric keys and MUST NOT be used when server.enabled: true. Implementations MUST reject this configuration.
generate (default):
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:
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):
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
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:
- New keys MUST be added to JWKS before becoming active
- Old keys MUST remain in JWKS for at least
token_ttlafter rotation - Implementations MUST support at least 2 concurrent keys in JWKS
Configuration:
identity:
keys:
rotation_period: "7d" # Rotate weekly
grace_period: "1h" # Keep old key in JWKS for 1 hour extraWhen server.enabled: true, implementations MUST expose a JWKS endpoint for token verification.
Request:
GET /v1/jwks HTTP/1.1
Host: aip-server:9443Response:
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-Controlheader SHOULD indicate TTL (default: 1 hour)- Clients MUST refresh JWKS when encountering unknown
kid
If a signing key is compromised:
- Immediate: Remove compromised key from JWKS
- Generate: Create new signing key
- Revoke: Revoke all sessions that used compromised key
- Rotate: Force token rotation for all active sessions
- Audit: Log compromise event with forensic details
Emergency key revocation endpoint (implementation-defined):
POST /v1/keys/revoke HTTP/1.1
Host: aip-server:9443
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"kid": "key-2026-01-17",
"reason": "Key compromise detected",
"revoke_sessions": true
}This section defines the optional HTTP server for remote policy validation.
The AIP server provides:
- Remote validation: Validate tool calls from external systems
- Health checks: Integration with load balancers and orchestrators
- Metrics: Prometheus-compatible metrics export
POST /v1/validate HTTP/1.1
Host: aip-server:9443
Content-Type: application/json
Authorization: Bearer <identity-token>
{
"tool": "<tool-name>",
"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:
Authorization: Bearer <identity-token>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.
HTTP/1.1 200 OK
Content-Type: application/json
{
"decision": "allow|block|ask",
"reason": "<human-readable-reason>",
"violations": [
{
"type": "<violation-type>",
"field": "<field-name>",
"message": "<description>"
}
],
"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) |
| 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 |
GET /health HTTP/1.1
Host: aip-server:9443HTTP/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 |
When enabled, the metrics endpoint exposes Prometheus-compatible metrics.
GET /metrics HTTP/1.1
Host: aip-server:9443| 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) |
The revocation endpoint allows immediate invalidation of tokens or sessions.
POST /v1/revoke HTTP/1.1
Host: aip-server:9443
Content-Type: application/json
Authorization: Bearer <admin-token>
{
"type": "session|token",
"session_id": "<uuid>", // Required if type=session
"token_nonce": "<nonce>", // Required if type=token
"reason": "<human-readable>" // 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 |
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"
}| 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 |
The revocation endpoint MUST require elevated privileges:
- Separate admin token (not user identity token)
- mTLS with admin certificate
- Operator API key
Revocation events MUST be logged:
{
"timestamp": "2026-01-24T10:30:00.000Z",
"event": "REVOCATION",
"type": "session",
"target": "550e8400-e29b-41d4-a716-446655440000",
"reason": "Suspected compromise",
"admin": "operator@example.com"
}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)
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) |
{
"jsonrpc": "2.0",
"id": <request_id>,
"error": {
"code": <error_code>,
"message": "<error_message>",
"data": {
"tool": "<tool_name>",
"reason": "<human_readable_reason>"
}
}
}Returned when identity.require_token: true and no token is provided.
{
"code": -32008,
"message": "Token required",
"data": {
"tool": "file_write",
"reason": "Identity token required for this policy"
}
}Returned when token validation fails.
{
"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 timepolicy_changed- Policy hash mismatchsession_mismatch- Session binding mismatchbinding_mismatch- Strict binding validation failedreplay_detected- Nonce reuse detectedaudience_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.
Returned when policy signature verification fails.
{
"code": -32010,
"message": "Policy signature invalid",
"data": {
"policy": "production-agent",
"reason": "Signature verification failed"
}
}Returned when a token or its session has been explicitly revoked via the revocation endpoint.
{
"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).
Returned when the token's aud claim does not match the expected audience.
{
"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.
Returned when a tool's schema hash does not match the expected value in the policy.
{
"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:
- Alert security teams immediately
- Log full schema details for forensic analysis
- Consider blocking the MCP server until verified
[Section 8.1-8.3 remain unchanged from v1alpha1]
| 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 |
| 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) |
{
"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"
}Identity-related events SHOULD be logged:
{
"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..."
}{
"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"
}{
"timestamp": "2026-01-24T10:36:00.000Z",
"event": "TOKEN_VALIDATION_FAILED",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"token_id": "abc123def456",
"error": "token_expired"
}| 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) |
Implementations MUST pass the conformance test suite to claim AIP compliance.
The test suite consists of:
- Schema validation tests: Verify policy parsing
- Decision tests: Input → expected decision
- Normalization tests: Verify Unicode handling
- Error format tests: Verify JSON-RPC errors
- Identity tests: Token lifecycle, rotation, validation (new)
- Server tests: HTTP endpoint behavior (new)
See spec/conformance/ for test vectors.
Implementations MUST:
- Parse
apiVersion: aip.io/v1alpha2documents - Reject documents with unknown
apiVersion - Apply NFKC normalization to names
- Return specified error codes
- Support
enforceandmonitormodes
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)
This section defines the security assumptions and threat model for AIP.
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 |
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 |
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 |
AIP makes the following assumptions:
- Policy integrity: The policy file has not been tampered with at load time (verified via signature when
metadata.signatureis present) - Engine integrity: The AIP implementation is correct and not compromised
- Cryptographic security: SHA-256, Ed25519, and other algorithms remain secure
- Clock accuracy: System clocks are reasonably synchronized (within TTL tolerance)
- TLS security: Transport encryption prevents eavesdropping and tampering
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
The policy file itself MUST be protected from modification by the agent. Implementations MUST automatically add the policy file path to protected_paths.
Implementations MUST use a regex engine that guarantees linear-time matching (RE2 or equivalent). Pathological patterns like (a+)+$ MUST NOT cause exponential execution time.
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').
Monitor mode allows all requests through. Implementations SHOULD warn users when monitor mode is enabled in production environments.
Audit logs SHOULD be written to a location not writable by the agent. Implementations MAY support log signing or forwarding to external systems.
Identity tokens SHOULD be stored in memory only, not persisted to disk. If persistence is required, tokens MUST be encrypted at rest.
Tokens transmitted over the network MUST use TLS 1.2 or later. Implementations MUST NOT send tokens over unencrypted connections.
Short token lifetimes (5-15 minutes) limit the window for token theft. Implementations SHOULD NOT allow token_ttl greater than 1 hour.
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)
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:
- Shared storage is REQUIRED - Local-only nonce tracking allows replay across instances
- Atomic operations are REQUIRED - Use storage primitives that guarantee atomicity (Redis
SET NX, DB unique constraints) - TTL-based expiration - Set storage TTL to
nonce_window + clock_skew_tolerance(recommended: 30 seconds tolerance) - Clock synchronization - All instances SHOULD use NTP with drift < 1 second
Configuration for distributed deployments:
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 safetyA token with a previously-seen nonce MUST be rejected with error code -32009 (replay_detected).
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.
Validation endpoints MUST require authentication. Unauthenticated endpoints allow attackers to probe policy configurations.
Validation endpoints SHOULD implement rate limiting to prevent denial of service attacks.
Error responses SHOULD NOT reveal detailed policy configuration. The reason field SHOULD provide minimal information needed to diagnose issues.
This specification requests registration of the following:
- Type name: application
- Subtype name: vnd.aip.policy+yaml
- Required parameters: None
- File extension: .yaml, .yml
This specification uses the aip.io namespace for versioning:
aip.io/v1alpha1- Previous specificationaip.io/v1alpha2- This specification
# 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
<arg_name>: <regex>
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"Identity and Session Management
- Added
identityconfiguration section- Token generation and rotation with configurable TTL
- Session binding (
process,policy,strict) - Policy hash computation for integrity
nonce_windowfor bounded replay prevention storagepolicy_transition_gracefor gradual policy rolloutsaudiencefor token audience binding (RFC 8707 alignment)nonce_storagefor distributed nonce tracking (Redis, PostgreSQL)keysfor 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
serverconfiguration 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_policyfail_open_constraintsfor safer fail_open deployments- Configurable
timeoutfor validation requests
- HTTP validation endpoint (
- Mandated JWT encoding when
server.enabled: true - Token transmission via Authorization header only (RFC 6750)
Tool Security
- Added
schema_hashto 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_requestsfor request-side DLP scanning - Added
max_scan_sizeto prevent ReDoS - Added
on_request_matchaction (block,redact,warn) - Added
on_redaction_failurehandling (block,allow_original,reject) - Added
log_original_on_failurefor forensics - Added
scopeto 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.signaturefor 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
- Initial draft specification
- Defined core policy schema
- Defined evaluation semantics
- Defined error codes
- Defined audit log format
- Model Context Protocol (MCP)
- MCP Authorization (2025-06-18)
- JSON-RPC 2.0 Specification
- RFC 2119 - Key words for use in RFCs
- RFC 7519 - JSON Web Token (JWT)
- RFC 8785 - JSON Canonicalization Scheme (JCS)
- Unicode NFKC Normalization
- RE2 Syntax
- Agentic JWT (draft-goswami-agentic-jwt-00)
This appendix describes features under consideration for future versions of AIP.
Status: Proposed for v1beta1
[Content unchanged from v1alpha1]
Status: Under Discussion
Allow policies to extend base policies:
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 listStatus: Proposed for v1beta1
Allow policies to integrate with external identity providers:
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 providersspiffe- SPIFFE/SPIRE workload identity
Status: Partially implemented in v1alpha2 (metrics endpoint)
Full telemetry specification:
spec:
telemetry:
metrics:
enabled: true
format: "prometheus" # prometheus | otlp
traces:
enabled: true
endpoint: "http://jaeger:14268/api/traces"
format: "otlp"
sampling_rate: 0.1Status: Under Discussion
Support for CEL (Common Expression Language) or Rego for complex validation:
tool_rules:
- tool: file_write
action: allow
when: |
args.path.startsWith("/allowed/") &&
!args.path.contains("..") &&
size(args.content) < 1048576Status: Under Discussion for v1beta1
Full compatibility with the Agentic JWT specification:
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
- exportMapping 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 |
The reference implementation is available at: 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)
# 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"import "crypto/rand"
func generateNonce() string {
b := make([]byte, 16)
rand.Read(b)
return hex.EncodeToString(b)
}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[:])
}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