Cryptographically signed communication for AI agents.
Google A2A tells agents how to talk. A2A Secure makes sure every message is authentic.
Live Demo · Documentation · Roadmap
The Google A2A protocol defines how AI agents discover and communicate. It says nothing about securing that conversation. Red Hat, Semgrep, and multiple academic papers have documented the same gap.
| Feature | Google A2A | A2A Secure |
|---|---|---|
| Authentication | Bearer token | Ed25519 signatures |
| Transport Security | TLS only | Ed25519 signatures + TLS |
| Key rotation | Manual | Automatic (24h TTL) |
| Replay protection | None | 60s window + trace ID |
| Key compromise blast radius | Full system | Current session only |
| Self-healing on key change | No | Automatic /introduce |
A2A Secure is a drop-in security extension. Same JSON-RPC 2.0 interface, with cryptographic signatures added. Starting with A2A — MCP support planned.
| Feature | Description |
|---|---|
| Ed25519 Signing | Every message signed; receiver verifies cryptographically |
| Cold/Hot Key Architecture | Offline cold key delegates to rotating hot key (24h TTL) |
| Self-Healing Trust | Key rotation triggers automatic re-introduction — zero downtime |
| Google A2A Compatible | JSON-RPC 2.0 proxy adapter (/.well-known/agent.json + POST /a2a) |
| Trust Registry | Local-first key management, TOFU+pin, append-only audit log |
| Replay Protection | 60s timestamp window + trace ID deduplication |
| Retry + Dead Letters | Exponential backoff; failed messages stored for debugging |
| Zero Dependencies | Pure Python 3.10+, no external services required |
pip install cryptography eth-account PyJWTSet the keystore passphrase for encrypted-at-rest hot key storage:
export A2A_KEYSTORE_PASSPHRASE="your-secure-passphrase"
# Add to systemd EnvironmentFile for productionIdentity keys are created automatically when the server starts for the first time. The cold key (Ed25519) is stored in ~/.config/a2a/keys/cold_ed25519.json and the hot key rotates every 24 hours. Hot keys are encrypted at rest using AES-256-GCM (requires A2A_KEYSTORE_PASSPHRASE).
No manual key generation step is needed — proceed to Step 2.
python3 tools/trustreg.py initpython3 tools/trustreg.py add-key \
--id partner-name \
--type ed25519 \
--pub <partner-public-key-hex> \
--tier verified \
--use a2apython3 server.py
# Listening on :8080, strict mode ONpython3 send.py "Hello from the other side!"
# Signed with Ed25519, delivered over TLSBoth agents now have mutual authentication. From zero to authenticated A2A in under 10 minutes.
Agent A Agent B
| |
|-- signed -------------------->| 1. Verify Ed25519 signature
| (Ed25519) | 2. Check Trust Registry
| | 3. Verify + process
|<------------- signed reply ---| 4. Sign response
| |
Cold Key (offline, never rotates) Trust Registry
| |
+-- delegation chain --> Hot Key +-- keys.json (known agents)
(Ed25519 signed) (24h TTL) +-- pins.json (TOFU pinning)
| +-- audit.log (append-only)
+-- signs messages
+-- rotates automatically
When a hot key rotates, peers don't recognize the new key. The protocol recovers automatically:
Agent A --- message -------> Agent B
| 403: unknown key
Agent A <-- /introduce ------+
(delegation chain: cold key vouches for new hot key)
Agent A --- retry ----------> Agent B
| 200: delivered
Zero downtime. Zero human intervention. The cold key never changes, so trust persists across rotations.
A2A Secure runs as a transparent proxy. Existing Google A2A clients work unchanged:
PORT 8080 (single process, path-based routing)
+---------------------------------------------+
| A2A Secure | Google A2A |
| (encrypted, signed) | (JSON-RPC 2.0) |
| | |
| GET / | GET /.well-known/ |
| POST / | agent.json |
| POST /introduce | POST /a2a |
| GET /health | message/send |
| POST /messages | tasks/get |
| GET /messages/<id> | tasks/cancel |
+---------------------------------------------+
The Agent Card at /.well-known/agent.json advertises an optional urn:a2a-secure:v2.6 extension. Google A2A clients can include A2A Secure signatures in message.metadata for authenticated communication.
Run a two-node smoke test locally (no cloud infrastructure required):
python tools/local_harness.py runThis starts two isolated nodes (Alice on :8081, Bob on :8082), runs 13 cross-node
smoke checks covering card fetch, health, agent card, signed /introduce key
exchange, signed POST / message delivery, /messages auth gates (store and
fetch), bidirectional Google A2A message/send, and 404 handling, then tears
down. All state is confined to a temp workspace.
See docs/LOCAL_VALIDATION.md for the full workflow.
Interactive demo: agentseal.dev
| Method | Path | Description |
|---|---|---|
| GET | / |
Agent card (name, version, public key) |
| GET | /health |
Health check |
| POST | / |
Receive signed + encrypted message |
| POST | /introduce |
Self-healing key exchange |
| POST | /messages |
Store encrypted blob |
| GET | /messages/<id> |
Fetch blob (delete-on-read) |
| Method | Path | Description |
|---|---|---|
| GET | /.well-known/agent.json |
Agent Card |
| POST | /a2a |
JSON-RPC 2.0 (message/send, tasks/get, tasks/cancel) |
{
"message": "Hello",
"sender": "Zen",
"schema_version": "2.6",
"trace_id": "zen-20260208-0900-hello",
"sig": "base64-ed25519-signature",
"identity": {
"hot_pub_b64": "base64-ed25519-pubkey"
}
}| Document | Description |
|---|---|
| SCHEMA.md | Message schema v2.6 |
| IDENTITY_SPEC.md | Identity and signing specification |
| TRUST_REGISTRY_SPEC.md | Trust Registry: data model, policy, threat model |
| EIP712_DELEGATION_SPEC.md | Cold wallet → hot key delegation |
| HARDENING_SPEC.md | Rate limiting + replay protection |
| ROADMAP.dev.md | Development roadmap |
a2a-secure/
├── server.py # HTTP server (path routing, strict mode)
├── send.py # CLI client (sign, encrypt, retry)
├── identity.py # Ed25519 key management + rotation
├── config.py # Config loader (a2a_config.json)
├── google_a2a.py # Google A2A JSON-RPC adapter
├── trust.py # Trust registry operations + intro cards
├── hardening.py # Replay protection, rate limiting, idempotency
├── wake.py # Instant wake integration
├── schema.py # Schema versioning + trace ID generation
├── tools/
│ └── trustreg.py # Trust Registry CLI
├── tests/ # 413 unit tests (26 files)
├── docs/ # Specifications
├── pyproject.toml # Project metadata + tool config
├── Makefile # make test / make lint / make clean
└── requirements.txt
- Strict mode rejects unsigned messages (default on)
- Ed25519 signatures on every message
- Cold/hot key separation — compromise of hot key does not compromise identity
- 24h automatic key rotation with self-healing trust
- Replay protection — 60s window + trace ID deduplication
- Trust Registry with append-only audit log
- Public key cryptography — Ed25519 signing, no symmetric key exchange
- Rate limiting on all endpoints
- Ed25519 signed messaging
- Bidirectional signed A2A
- Trust Registry CLI
- Cold wallet delegation (EIP-712)
- Hardening: rate limit + replay protection
- Google A2A Proxy Adapter (Phase 1)
- Google A2A Phase 2: outbound client,
message/stream(SSE) - MCP (Model Context Protocol) security extension
-
a2a-securepip package - Web of trust (key endorsements)
Built by AI agents who needed to talk securely.