AIP for A2A

Add cryptographically verifiable identity and scoped delegation to your A2A agents.

What this is

The Agent-to-Agent (A2A) protocol carries tasks between agents over HTTP. AIP adds an identity layer: every task carries a chained token in metadata.aip_token, the receiver verifies the signature chain and scope, and the agent card declares the receiver's AIP identifier so callers know who they're talking to. No shared secrets. No bearer tokens that can be stolen and replayed.

Install

pip install agent-identity-protocol>=0.3.0

Generate an Ed25519 keypair for your agent:

python -c "from aip_core.crypto import KeyPair; kp = KeyPair.generate(); print('pub:', kp.public_key_bytes().hex()); open('agent.sk','wb').write(kp.private_key_bytes())"

Server side — verify incoming tasks

Wrap your task handler in A2AVerifyMiddleware. The middleware extracts the token from metadata.aip_token, verifies the chain, checks audience and scope, and only then calls your handler.

from aip_a2a import A2AVerifyMiddleware

OWN_ID = "aip:web:acme.com/researcher"
ROOT_PUBKEY = bytes.fromhex("...")  # the orchestrator's pubkey

def handle_task(body, *, context):
    # context.subject == OWN_ID, context.chain_depth, context.issuer
    print(f"verified caller chain depth={context.chain_depth}")
    return {"jsonrpc": "2.0", "result": "..."}

middleware = A2AVerifyMiddleware(
    handle_task,
    own_aip_id=OWN_ID,
    root_public_key_bytes=ROOT_PUBKEY,
    required_scope="research:read",
)

# Use middleware(parsed_request_body) inside your A2A server.

Client side — forward a task with delegation

Before calling another A2A agent, append a delegation block that attenuates scope:

from aip_a2a import append_delegation_block

extended = append_delegation_block(
    incoming_token,                      # your verified token
    delegator="aip:web:acme.com/researcher",
    delegate="aip:web:acme.com/writer",  # the next agent
    scopes=["write:draft"],              # narrowed
    context="draft-summary",
    budget_cents=50,
)

body = {
  "jsonrpc": "2.0",
  "method": "tasks/send",
  "params": {
    "task_id": "task-2",
    "message": {"role": "user", "parts": [{"text": "..."}]},
    "metadata": {"aip_token": extended.to_base64()},
  },
}

Agent card extension

Your agent card MUST include an aip_identity extension so callers can resolve your identity document and verify mutual auth (when used).

{
  "name": "Researcher",
  "skills": ["research"],
  "aip_identity": {
    "id": "aip:web:acme.com/researcher",
    "document_url": "https://acme.com/.well-known/aip/agents/researcher.json"
  }
}

For the full normative requirements, see spec §A2A binding.

Try to break it

Run the working example:

git clone https://github.com/sunilp/aip
cd aip/examples/a2a-multi-agent
./run.sh

Then try these:

Why this works: AIP fails closed. Any tampering, scope-widening, audience mismatch, or expired block rejects the request before your handler runs.

Next

For multi-hop delegation, see the chained delegation guide. For the full normative spec, see /aip/spec/.