A2A-SDL: agent-to-agent self-describing protocol reference implementation
Project description
A2A-SDL (Reference Implementation)
A2A-SDL is a machine-optimized, self-describing protocol for cross-agent communication with strict validation, canonical encoding, and optional cryptographic security.
This repository implements:
- Envelope validation and limits (
max_bytes,max_depth,max_array_len) - JSON canonical encoding and optional canonical CBOR
- Embedded/URI schema descriptors with content-hash checks
- Optional JSON Schema validation (when
jsonschemais installed) - Ed25519 signing/verification
- X25519 + ChaCha20-Poly1305 payload encryption
- Replay nonce cache
- HTTP transport (stdlib server/client + optional FastAPI app)
- Local IPC transport binding (length-prefixed
uint32_beframes) - CLI for send/serve/keys/validation
- Structured error mapping for protocol validation failures (
UNSUPPORTED_CT,SCHEMA_INVALID,SECURITY_UNSUPPORTED) - HTTP client retries/backoff and configurable timeout (
a2a send --timeout --retry-attempts --retry-backoff-s) - Optional server-side replay enforcement (
a2a serve --replay-protection) - Trace enforcement with hop limits (
trace.root_id,trace.span_id,trace.hops) and derived child trace on responses - Optional automatic content-type negotiation fallback (
a2a send --auto-negotiate) - Secure policy mode for mandatory
enc+sig+replay, key-based authz, and allowlisted agents - Tamper-evident hash-chained audit log with optional Ed25519-signed audit receipts
- WebSocket transport processing now matches HTTP validation/security behavior with structured
error.v1responses - Built-in multi-buddy swarm command (
a2a swarm, 2+ buddies) that coordinates Codex buddies through A2A envelopes - Executable
toolcall.v1path with built-in tools (sys.ping,math.add) - Pluggable request handlers via
a2a serve --handler-spec <ct>=<module>:<callable> - Ingress hardening: oversized
Content-Lengthrejected before body read - URI-schema hardening: untrusted URI descriptors rejected on HTTP/WS/IPC ingress and transport client preflight
- Key lifecycle policy support: key rotation sets, revoked key IDs, and per-kid expiry checks
- Plugin safety controls: deny-by-default tool execution and strict custom handler module/manifest validation
- Stress and fuzz testing for malformed envelopes and concurrent HTTP load
- Durable replay protection via optional SQLite-backed nonce cache (
--replay-db-file) - Distributed replay protection via optional Redis backend (
--replay-redis-url) - Production transport controls: TLS/mTLS options and production deployment profile
- Outbound HTTPS client mTLS controls for
a2a send(--tls-ca-file, client cert/key options) - Admission control with concurrency + token-bucket rate limiting
- Optional admin observability endpoints (
/healthz,/readyz,/metrics) with Prometheus-compatible metrics output - Audit signature verification support for signed audit chains
- Optional external audit hash anchoring (
--audit-anchor-url) with fail-open/fail-closed modes - Runtime capability-version enforcement (
cap.a2a_sdl.v) for peer compatibility checks - Runtime migration/deprecation policy enforcement via
--version-policy-file - Dynamic trust lifecycle protocol (
trustsync.v1) with signed registry update proposals - Negotiated session binding handshake (
session.v1) with optional detached Ed25519 binding signature - Fine-grained tool authorization (per-agent grants and required scopes via
--tool-policy-file)
How It Works
At runtime, every message is an envelope with:
- Identity (
from,to) for routing and access-policy checks - Payload type (
ct) for handler dispatch and schema validation - Payload (
payload) with request/response body - Schema descriptor (
schema) for self-describing validation - Security (
sec) for signatures, encryption metadata, and replay tokens - Trace (
trace) for request lineage and hop control
Typical request flow:
- Build envelope with
build_envelope(...). - Canonically encode (
json, optionalcbor) with deterministic ordering. - Optionally encrypt payload and attach replay nonce/expiry.
- Optionally sign envelope.
- Send over transport (
HTTP,WS, or localIPC). - Receiver validates envelope, capability version compatibility, security policy, replay cache, and schema.
- Handler returns a typed response envelope (
toolresult.v1,task.v1, orerror.v1).
Project Structure
Core modules:
a2a_sdl/envelope.py: envelope construction, validation limits, trace checks.a2a_sdl/schema.py: builtin descriptors, hash checks, JSON schema integration.a2a_sdl/codec.py: canonical JSON/CBOR encode/decode.a2a_sdl/security.py: Ed25519 signatures and X25519+ChaCha20-Poly1305 encryption.a2a_sdl/policy.py: secure-required policy enforcement (enc+sig+replay, key allowlists).a2a_sdl/replay.py: nonce cache for replay protection.a2a_sdl/transport_http.py: stdlib server/client, retries, fallback negotiation.a2a_sdl/transport_ws.py: websocket payload processing with protocol-aligned errors.a2a_sdl/transport_ipc.py: length-prefixed local IPC transport.a2a_sdl/swarm.py: multi-buddy Codex orchestration over A2A envelopes.a2a_sdl/handlers.py: content-type router, builtin handlers, and tool registry.a2a_sdl/cli.py: operational entrypoints (send,serve,swarm,keygen, etc).
Tests:
tests/contains unit coverage for envelope rules, security, transports, policy, and swarm convergence behavior.
Installation
Recommended (isolated CLI install with uv):
uv tool install "a2acrpt[full]"
uv tool update-shell
From local checkout (editable dev install as a tool):
uv tool install --editable "/path/to/crpt[full]"
From GitHub (without publishing to PyPI):
uv tool install "git+https://github.com/psspssr/crpt.git"
One-off command execution without permanent install:
uv tool run --from "a2acrpt[full]" a2a --help
Alternative installers:
pip install "a2acrpt[full]"
pipx install "a2acrpt[full]"
Useful extras:
a2acrpt[cbor]for CBOR codec supporta2acrpt[schema]for JSON Schema validationa2acrpt[http]for FastAPI/uvicorn adaptera2acrpt[ws]for websocket transporta2acrpt[redis]for distributed replay cache backend
Quick Start
uv tool install --force "/root/crpt[full]"
a2a keygen --out-dir .keys
Send a task over HTTP:
a2a serve --host 127.0.0.1 --port 8080 --deployment-mode dev --allow-insecure-http
# in another shell:
a2a send --url http://127.0.0.1:8080/a2a --ct task.v1 --payload-file a2a_sdl/examples/task_min_payload.json --timeout 30 --retry-attempts 2 --retry-backoff-s 0.25 --auto-negotiate
Secure server mode (mandatory encrypted/signed inbound requests + audit log):
# trusted_signing_keys.json: { "<sender-kid>": "<ed25519-public-key>" }
# decrypt_keys.json: { "<server-recipient-kid>": "<x25519-private-key>" }
# agent_kid_map.json: { "<sender-agent-id>": "<sender-kid>" }
a2a serve \
--host 127.0.0.1 --port 8080 \
--deployment-mode prod \
--secure-required \
--tls-cert-file /etc/a2a/tls/server.crt \
--tls-key-file /etc/a2a/tls/server.key \
--replay-db-file /var/lib/a2a/replay.db \
--trusted-signing-keys-file trusted_signing_keys.json \
--decrypt-keys-file decrypt_keys.json \
--agent-kid-map-file agent_kid_map.json \
--allowed-agent did:key:sender-agent \
--audit-log-file /tmp/a2a_audit.log
Key lifecycle controls (rotation + revocation):
# key_registry.json may include:
# trusted_signing_keys, required_kid_by_agent, allowed_kids_by_agent,
# revoked_kids, kid_not_after, decrypt_private_keys
a2a serve \
--host 127.0.0.1 --port 8080 \
--deployment-mode prod \
--tls-cert-file /etc/a2a/tls/server.crt \
--tls-key-file /etc/a2a/tls/server.key \
--replay-db-file /var/lib/a2a/replay.db \
--key-registry-file key_registry.json
Secure sender mode (enc+sig+replay auto-applied):
a2a send \
--url https://127.0.0.1:8080/a2a \
--ct task.v1 \
--payload-file a2a_sdl/examples/task_min_payload.json \
--secure \
--sign-key .keys/ed25519_private.pem \
--sign-kid did:key:sender-agent#sig1 \
--encrypt-kid did:key:server#enc1 \
--encrypt-pub .keys/x25519_public.b64
Production hardening knobs:
a2a serve \
--deployment-mode prod \
--tls-cert-file /etc/a2a/tls/server.crt \
--tls-key-file /etc/a2a/tls/server.key \
--tls-ca-file /etc/a2a/tls/ca.crt \
--tls-require-client-cert \
--replay-db-file /var/lib/a2a/replay.db \
--admission-max-concurrent 256 \
--admission-rate-rps 400 \
--admission-burst 800
Distributed replay on Redis:
a2a serve \
--deployment-mode prod \
--tls-cert-file /etc/a2a/tls/server.crt \
--tls-key-file /etc/a2a/tls/server.key \
--replay-redis-url redis://redis.internal:6379/0 \
--replay-redis-prefix a2a:replay:prod
Sender-side HTTPS + mTLS:
a2a send \
--url https://a2a.example.com/a2a \
--ct task.v1 \
--payload-file a2a_sdl/examples/task_min_payload.json \
--tls-ca-file /etc/a2a/tls/ca.crt \
--tls-client-cert-file /etc/a2a/tls/client.crt \
--tls-client-key-file /etc/a2a/tls/client.key
Operational observability endpoints:
# Enable GET /healthz, /readyz, /metrics
a2a serve --deployment-mode dev --allow-insecure-http --admin-endpoints
# Protect /readyz and /metrics with a token
a2a serve --deployment-mode dev --allow-insecure-http --admin-token secret123
curl -H "Authorization: Bearer secret123" http://127.0.0.1:8080/readyz
curl -H "Authorization: Bearer secret123" http://127.0.0.1:8080/metrics
Transport Details
- HTTP: reference request/response transport, retry/backoff support, content-type negotiation fallback, optional TLS/mTLS, and admission control.
- WS: same protocol validation path as HTTP; protocol violations map to structured
error.v1. - IPC: local framed transport (
uint32_be+ payload bytes), useful for same-host agent orchestration.
All transports converge on shared envelope validation and handler semantics to keep behavior consistent.
Handler Extensibility (New)
Two extensibility layers are built in:
- Tool registry for
toolcall.v1:sys.ping: liveness + echo payloadmath.add: numeric aggregation fromargs.values
- Content-type router:
- default implementation for
task.v1,toolcall.v1,negotiation.v1,trustsync.v1,session.v1 - custom content types can be registered at server startup
- default implementation for
Protocol metadata improvement:
- response envelopes now advertise available tools in
cap.tools - negotiation responses include
payload.available_tools
Example custom handler wiring:
a2a serve \
--host 127.0.0.1 --port 8080 \
--allow-handler-module-prefix myproject.handlers \
--handler-spec artifact.v1=myproject.handlers:artifact_handler
artifact_handler(request) must return a valid A2A response envelope.
By default, tool execution is deny-by-default in a2a serve.
Allow tools explicitly:
a2a serve --allow-tool sys.ping --allow-tool math.add
Fine-grained tool authorization policy file:
{
"allowed_tools_by_agent": {
"did:key:planner-a": ["math.add", "sys.ping"]
},
"required_scopes_by_tool": {
"math.add": "tool:math.add"
}
}
a2a serve --tool-policy-file tool_policy.json
Use --unsafe-allow-unmanifested-handlers only for trusted local development.
Security Model
--secure-required mode on server enforces all of:
- encrypted payload (
enc) - valid trusted signature (
sig) - replay token with nonce + expiry (
replay) - sender identity/key mapping and optional allowlisted agent IDs
--deployment-mode prod behavior:
- treats secure mode as required
- requires TLS cert/key unless explicitly overridden with
--allow-insecure-http - requires durable replay storage (
--replay-db-file) unless overridden
Operational components:
ReplayCache(memory),SQLiteReplayCache(durable), orRedisReplayCache(distributed) blocks duplicate nonce usage.- Audit chain persists tamper-evident events, can verify Ed25519 signatures over entries, and can anchor entry hashes to external HTTP(S) sinks.
- Structured errors intentionally avoid leaking sensitive internals.
Trust lifecycle sync:
trustsync.v1supportsdiscoversnapshots and signedproposeregistry updates.- Configure update verification key with
--trust-sync-verify-key-file.
Session binding handshake:
session.v1establishes a negotiated binding ID over profile+nonce(+expiry).- Configure detached signature on binding acknowledgements with
--session-binding-signing-key-file.
Runtime migration/deprecation policy:
- Load with
--version-policy-file. - Supports min/max peer protocol bounds, required peer version presence, deprecation deadlines, and allowed content-type version ranges.
Programmatic local IPC usage:
from a2a_sdl.handlers import default_handler
from a2a_sdl.transport_ipc import IPCServer, send_ipc
from tests.test_helpers import make_task_envelope
server = IPCServer("127.0.0.1", 9099, handler=default_handler)
# run server.serve_forever() in a background thread/process
response = send_ipc("127.0.0.1", 9099, make_task_envelope(), encoding="json")
Multi-buddy autonomous swarm (Codex-backed, A2A toolcall envelopes):
a2a swarm \
--goal "Drive protocol to near-final feature completeness." \
--ports 8211,8212 \
--rounds 8 \
--near-final-rounds 2 \
--workdir /root/crpt
Swarm report fields:
converged=true, mode=near_final: majority of buddies reportednear_finalfor the configured streak.converged=true, mode=stabilized_working: buddies remainedworkingfor multiple rounds with no major new feature movement.converged=false, mode=not_converged: max rounds reached without either condition.
Swarm behavior notes:
- Each buddy runs as an A2A HTTP endpoint backed by a
codex execcall. - Coordinator sends
toolcall.v1requests round-by-round with handoff text chaining. - Near-final convergence uses majority voting across 2+ buddies.
- If buddies stabilize in
workingstate for repeated rounds, coordinator marksstabilized_workingto avoid endless loops.
Test
python3 -m unittest discover -s tests -v
Release (Maintainers)
One-time setup:
- Create a PyPI project named
a2acrpt. - In PyPI, add a Trusted Publisher for this GitHub repo/workflow.
- In GitHub repo settings, create environment
pypi(optional protection rules).
Automated release:
- Bump version in
pyproject.toml. - Commit and push to
main. - Create and push a version tag (for example
v0.1.1):git tag v0.1.1 && git push origin v0.1.1 - GitHub Actions workflow
.github/workflows/publish-pypi.ymlbuilds and publishes to PyPI.
CI hardening workflow:
.github/workflows/ci.ymlruns lint (ruff), type checks (mypy), unit tests (3.11/3.12), and validates thatv*tags matchpyproject.tomlversion.
Manual local release check:
python -m pip install -U build twine
python -m build
python -m twine check dist/*
Notes
- CBOR support requires
pip install -e .[cbor]. - JSON Schema validation requires
pip install -e .[schema]. - FastAPI server adapter requires
pip install -e .[http]. - Versioning policy details:
docs/versioning-policy.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file a2acrpt-0.1.0.tar.gz.
File metadata
- Download URL: a2acrpt-0.1.0.tar.gz
- Upload date:
- Size: 72.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6fd21df183229ebd0c00c747d94cb64d6e90cebe795f7d77c45bfc730fb27fea
|
|
| MD5 |
f4398c832882b1ca548d64ae4f712831
|
|
| BLAKE2b-256 |
23f81668a8632b9ab4d0cc727c5560a1fd99815e4b485985b1933f368a7dcee2
|
Provenance
The following attestation bundles were made for a2acrpt-0.1.0.tar.gz:
Publisher:
publish-pypi.yml on psspssr/crpt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
a2acrpt-0.1.0.tar.gz -
Subject digest:
6fd21df183229ebd0c00c747d94cb64d6e90cebe795f7d77c45bfc730fb27fea - Sigstore transparency entry: 943897965
- Sigstore integration time:
-
Permalink:
psspssr/crpt@eabe80c07a91c65edd26262b0fb6b2348228ce44 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/psspssr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@eabe80c07a91c65edd26262b0fb6b2348228ce44 -
Trigger Event:
push
-
Statement type:
File details
Details for the file a2acrpt-0.1.0-py3-none-any.whl.
File metadata
- Download URL: a2acrpt-0.1.0-py3-none-any.whl
- Upload date:
- Size: 58.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
34d9e9b308e0b8e25a375c19c77d6531d0d8c39b6b3ffeb0e101281ae86a42de
|
|
| MD5 |
18e879e0c696a6179fa768e9921eaaed
|
|
| BLAKE2b-256 |
60e27259da2259a43910c55e4ecd2fa54d76da32ac71803dd51ad6322df3fd1a
|
Provenance
The following attestation bundles were made for a2acrpt-0.1.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on psspssr/crpt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
a2acrpt-0.1.0-py3-none-any.whl -
Subject digest:
34d9e9b308e0b8e25a375c19c77d6531d0d8c39b6b3ffeb0e101281ae86a42de - Sigstore transparency entry: 943897966
- Sigstore integration time:
-
Permalink:
psspssr/crpt@eabe80c07a91c65edd26262b0fb6b2348228ce44 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/psspssr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@eabe80c07a91c65edd26262b0fb6b2348228ce44 -
Trigger Event:
push
-
Statement type: