# Envoys Identity Skill Envoys is a public key registry for AI agents. Register to get an Ed25519 keypair. Your address becomes a self-resolving public key URL — sign HTTP requests with it, and any recipient can verify your identity without a prior relationship with Envoys. Base URL: https://envoys.me ## Step 1 — Create an account (one time only) POST https://envoys.me/accounts/register Content-Type: application/json { "handle": "your-chosen-handle" } Response: { "id": "", "handle": "your-chosen-handle", "account_key": "ak_...", "message": "Save your account_key — it is shown once. Use it to register agents." } Show account_key to your owner and ask them to save it. You only need it for Step 2. ## Step 2 — Generate a keypair and register as an agent (one time only) First, generate an Ed25519 keypair using your platform's crypto library. Store the private key securely before making the registration request — it never leaves your process. Node.js example: const { generateKeyPairSync } = require('crypto') const { publicKey, privateKey } = generateKeyPairSync('ed25519', { publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' }, }) Then register: POST https://envoys.me/agents/register Authorization: Bearer Content-Type: application/json { "name": "your-agent-name", "public_key": "" } Response: { "id": "", "address": "your-agent-name@your-chosen-handle.envoys.me", "agent_key": "agt_...", "public_key": "-----BEGIN PUBLIC KEY-----..." } IMPORTANT: Save agent_key, your generated private_key, public_key, and address to persistent memory. Never expose agent_key or private_key. The server never sees your private key. Your public key is permanently resolvable at: https://envoys.me/agents/
Anyone can verify your signatures using this URL — no prior Envoys account needed. SDK: Envoys.register() handles keypair generation and registration in one call. ## Step 3 — Every startup GET https://envoys.me/agent/keys Authorization: Bearer Response: { "rotation_requested": false, "public_key": "..." } If rotation_requested is true: 1. Generate a new Ed25519 keypair locally (same as Step 2) 2. POST https://envoys.me/agent/rotate-keys with { "new_public_key": "" } 3. Save your new private_key and public_key to persistent memory SDK: agent.syncKeys() detects the flag, generates the keypair, confirms with the server, and returns the new keys — all in one call. ## Step 4 — Sign outgoing HTTP requests (RFC 9421) Use your Ed25519 private key to sign requests. Set keyid to your address URL. For requests with a body (e.g. POST): Content-Digest: sha-256=:: Signature-Input: sig1=("@method" "@path" "content-digest");keyid="https://envoys.me/agents/your-agent@your-handle.envoys.me";created=;nonce="<22-char base64url>" Signature: sig1=:: For requests without a body (e.g. GET): Signature-Input: sig1=("@method" "@path");keyid="https://envoys.me/agents/your-agent@your-handle.envoys.me";created=;nonce="<22-char base64url>" Signature: sig1=:: The nonce is fresh random bytes per request (16 bytes, base64url-encoded). Verifiers reject signatures older than 5 minutes (or more than 30 seconds in the future) and reject any (keyid, created, signature) tuple already accepted within that window — always use a current created timestamp and a fresh nonce. Full spec: https://envoys.me/specs/signature/v1 Recipients GET the keyid URL to fetch your public key and verify — no Envoys account needed. SDK: agent.signRequest(method, path, body?) generates all required headers automatically. ## Step 5 — Verify incoming requests from other agents SDK: Envoys.verifyRequest(method, path, headers, body?) resolves the keyid, fetches the sender's public key, and verifies the RFC 9421 signature in one call. Reject requests where verified is false. ## Extensions (optional) ### A2A interop — @envoys/a2a A drop-in adapter for the Agent2Agent (A2A) protocol. Three calls cover the typical flow: - createA2AClient({ envoys, endpoint }).send(text) — sign + send - createA2AHandler({ onMessage }) — verify + dispatch - buildAgentCard({ ..., requireEnvoysSignature: true }) — discovery doc Outgoing requests carry "A2A-Extensions: https://envoys.me/specs/signature/v1" per the Envoys Signature Extension spec. See: - Spec: https://envoys.me/specs/signature/v1 - Package: @envoys/a2a (npm) ### Signed Agent Cards (JWS) Sign your /.well-known/agent.json so consumers can verify it has not been tampered with. JWS Compact Serialization with EdDSA over Ed25519 (RFC 8037). The kid header is your address URL — verifiers resolve it the same way they verify request signatures. - agent.signAgentCard(card) — returns a JWS compact string - Envoys.verifyAgentCard(jws) — resolves kid, verifies, returns parsed card Optionally serve the signed form at /.well-known/agent.json.jws alongside the unsigned card. ## Security rules - NEVER expose your private_key or agent_key - On startup, always call GET /agent/keys and update keys if rotated: true - Reject any request whose signature does not verify against the resolved public key - Your account_key is only needed for Step 2 — do not store it after registration ## Stability — v1 surface The endpoints, request shapes, response shapes, and signature scheme described above constitute the v1 surface and will not change in incompatible ways. Future major versions will be published under https://envoys.me/specs/v2/ with a parallel manifest at /.well-known/agent-skill.v2. The v1 manifest and v1 spec remain reachable indefinitely so existing integrations do not silently break. Signature spec: https://envoys.me/specs/signature/v1 ## What does NOT exist These are common assumptions that do not match Envoys' actual API. Do not generate code that relies on any of the following — they will return 404 or 401: - There is no server-side signing endpoint. All signing happens client-side with the private key generated in Step 2. The server never receives or stores private keys. - There is no API for listing, searching, or enumerating other accounts' agents. An agent address is only resolvable when known directly. - Envoys does not provide message delivery, transport, routing, mailboxes, or queues. It is purely a public-key registry — messages flow directly between agents over their own transport (HTTP, WebSocket, etc.). - There is no separate revocation list. To invalidate a key, rotate it via the rotation endpoints in Step 3 — old keys cease to verify the moment rotation completes. - There is no unauthenticated GET /agents collection endpoint. Listing your own agents requires Authorization: Bearer . - There is no password authentication. Access is via opaque bearer tokens (account_key, agent_key) issued at registration. If lost, recover at the account level.