Skip to main content

Agent keys

When you register an agent via POST /v1/agents, 1Claw automatically generates three types of cryptographic material:

KeyAlgorithmPurposeStorage
API keyocv_... (random, argon2-hashed)Authentication — token exchange via POST /v1/auth/agent-tokenHash in DB; plaintext returned once at creation
Signing keyEd25519Message signing, identity verificationPrivate key in __agent-keys vault; public key on agent record (ssh_public_key)
ECDH keyP-256 (secp256r1)Key agreement — derive shared secrets for encrypted agent-to-agent messagingPrivate key in __agent-keys vault; public key on agent record (ecdh_public_key)

How keys are created

All three keys are generated server-side during POST /v1/agents:

  1. API key — 32 random bytes, base64url-encoded with ocv_ prefix. The plaintext is returned in the response and never stored; only the argon2 hash is persisted.

  2. Ed25519 signing keypair — Generated via ed25519-dalek. The 32-byte private key (base64) is stored as an encrypted secret in the org's __agent-keys vault at agents/{agent_id}/ssh/private_key. The 32-byte public key (base64) is stored on the agent record.

  3. P-256 ECDH keypair — Generated via the p256 crate. The 32-byte private scalar (base64) is stored in __agent-keys at agents/{agent_id}/ecdh/private_key. The 65-byte uncompressed SEC1 public point (base64) is stored on the agent record.

The __agent-keys vault is auto-created per organization on first agent creation. All secrets in it are HSM-encrypted with the same envelope encryption used for regular vaults.

Accessing keys via the API

Public keys (no special access needed)

Public keys are returned on the agent record:

# Human (list/get agents)
curl -H "Authorization: Bearer <token>" \
https://api.1claw.xyz/v1/agents/<agent_id>

# Agent (get own profile)
curl -H "Authorization: Bearer <agent-jwt>" \
https://api.1claw.xyz/v1/agents/me

Response includes:

{
"id": "ec7e0226-...",
"name": "Alice",
"ssh_public_key": "m+Z6jV5W86WMTV27cpk9QGXIo+fP1OX88dHxdj6DHUI=",
"ecdh_public_key": "BDq8k3Lw...base64...65bytes..."
}

Private keys (requires access policy)

Private keys are stored as secrets in the __agent-keys vault. To let an agent read its own keys, grant it a policy:

# Grant agent read access to its own keys in __agent-keys
curl -X POST "https://api.1claw.xyz/v1/vaults/<agent-keys-vault-id>/policies" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"principal_type": "agent",
"principal_id": "<agent_id>",
"permissions": ["read"],
"secret_path_pattern": "agents/<agent_id>/**"
}'

Then the agent can read its private keys:

# Ed25519 signing key
GET /v1/vaults/<agent-keys-vault-id>/secrets/agents/<agent_id>/ssh/private_key

# P-256 ECDH key
GET /v1/vaults/<agent-keys-vault-id>/secrets/agents/<agent_id>/ecdh/private_key
tip

The ecdh:setup-agents script in the Google A2A example automates this: it creates agents and grants each one read access to its own keys.

Key formats

KeyFormatSize
Ed25519 privateRaw 32-byte seed, base6444 chars
Ed25519 publicRaw 32-byte verifying key, base6444 chars
P-256 ECDH privateRaw 32-byte scalar, base6444 chars
P-256 ECDH publicUncompressed SEC1 point (04 || x || y), base6488 chars

Design rationale

  • Ed25519 for signing — Deterministic nonces (no catastrophic nonce-reuse bugs), fast, compact signatures. Widely used in SSH, TLS, and JWT.
  • P-256 ECDH for key agreement — Standard curve for Diffie-Hellman key exchange (TLS, ECIES, A2A messaging). Distinct from the signing key because signing and key agreement are separate cryptographic operations.
  • Separate keys for separate purposes — Signing keys prove identity; ECDH keys establish shared secrets. Using the same key for both is a known anti-pattern that can leak information about the private key.