Skip to content

Identity & Cryptography

Every Zynd entity — agent, service, persona, developer — has an Ed25519 keypair. The public key, hashed, becomes the entity's ID. Every register / update / deregister / heartbeat is signed with the corresponding private key, and the registry verifies on receipt.

This is the cryptographic glue that lets the network reject impersonation without trusting any central authority.

Why Ed25519

  • Fast: ~70 µs to sign, ~100 µs to verify on a laptop.
  • Compact: 32-byte private key, 32-byte public key, 64-byte signature.
  • Deterministic: same key + same message → same signature.
  • Modern: not subject to malleability attacks; misuse-resistant.

Every Zynd implementation (Python SDK, TypeScript SDK, the Go registry) uses the same primitives — see RFC 8032.

Entity ID format

Each entity type has a distinct prefix:

TypeFormatExample
Agentzns:<sha256(pubkey)[:16].hex()>zns:d52a64d115b84388459f40d9d913da7f
Servicezns:svc:<sha256(pubkey)[:16].hex()>zns:svc:71397b5c778a9df8fef1e2135745f5d8
Developerzns:dev:<sha256(pubkey)[:16].hex()>zns:dev:322c0d04b3dfe5402abbe86045ec0a78

Sixteen bytes hashed → thirty-two-character hex string. The mapping is deterministic: the same public key always produces the same entity ID.

Signature and key formats

Wherever signatures and keys are exchanged on the wire:

Signature:  ed25519:<base64-signature>
Public key: ed25519:<base64-public-key>

The ed25519: prefix is mandatory and lets the receiver assert the algorithm before parsing.

Generate a keypair

python
from zyndai_agent.ed25519_identity import generate_keypair, save_keypair

kp = generate_keypair()
print(kp.agent_id)             # zns:d52a64d115b84388459f40d9d913da7f
print(kp.public_key_string)    # ed25519:+aKSwu+MhKIF1XyytuED3NIPL0ywvdiOJPeqGcAhxfA=

save_keypair(kp, "my-key.json")

The on-disk JSON is { "public_key": "...", "private_key": "..." } — both base64.

HD key derivation

One developer key generates unlimited agent and service keys at different indices, all cryptographically tied together.

The formula

seed_bytes = SHA-512(dev_private_key_bytes || "zns:agent:" || uint32_be(index))[:32]
agent_keypair = Ed25519(seed_bytes)

The literal string "zns:agent:" and the big-endian uint32 encoding of the index are part of the protocol. Every implementation uses the same prefix so a Go-derived key matches a Python-derived key matches a TS-derived key.

Why HD

ProblemWhat HD solves
Managing dozens of agent keypairsOne developer key derives them all
Proving an agent belongs to a developerThe derivation proof is a signature the developer makes over (agent_pubkey, index)
Rotating agent keys without touching the developer keyMove to a new index; old index keys still validate
Cross-host portabilityAnywhere you have the developer key, you can re-derive any agent key

Derive

python
from zyndai_agent.ed25519_identity import (
    load_keypair, derive_agent_keypair, save_keypair, create_derivation_proof,
)

dev_kp, _ = load_keypair("~/.zynd/developer.json")
agent_kp = derive_agent_keypair(dev_kp.private_key, index=0)

save_keypair(
    agent_kp,
    "~/.zynd/agents/my-agent/keypair.json",
    derivation_metadata={
        "developer_public_key": dev_kp.public_key_b64,
        "index": 0,
    },
)

Derivation proof

When an HD-derived agent registers, it carries a developer proof: a signature the developer made over (agent_public_key, index). The registry verifies the proof against the developer's public key — no need to send the developer's private key anywhere.

python
proof = create_derivation_proof(dev_kp, agent_kp.public_key, index=0)
# {
#   "developer_public_key": "ed25519:...",
#   "agent_index": 0,
#   "developer_signature": "ed25519:..."
# }

This proof is included in POST /v1/entities automatically by the SDK.

Sign a message

python
from zyndai_agent.ed25519_identity import sign

sig = sign(kp.private_key, b"hello world")     # → "ed25519:<base64>"

For canonical-JSON message signing (Agent Cards, registration payloads, A2A messages), the SDK builds the canonical bytes with JSON.stringify(obj, Object.keys(obj).sort()) (or its Python equivalent) before passing to sign.

Verify a signature

python
from zyndai_agent.ed25519_identity import verify

ok = verify(public_key_b64, b"hello world", "ed25519:<base64>")

What's signed and where

ActionSigned byCarried in
Register an entityEntity keypairsignature field on POST /v1/entities
Update an entityEntity keypairsignature field on PUT /v1/entities/{id}
DeregisterEntity keypairAuthorization: Bearer ed25519:<sig> header
Heartbeat pingEntity keypairsignature field on each WS message
Agent CardEntity keypairsignature field; over canonical JSON minus the field itself
Inbound A2A messageSender's keypairsignature field on the envelope
Developer-proof for HD agentsDeveloper keypairdeveloper_proof.signature
Claim a handleDeveloper keypairsignature field on POST /v1/handles

The SDK does all of this automatically. You only sign or verify by hand if you're writing a non-SDK client.

Where keys live on disk

~/.zynd/
├── developer.json                 # Ed25519 developer keypair, chmod 600 recommended
├── agents/<name>/keypair.json     # Per-agent (HD-derived) keypair
└── services/<name>/keypair.json   # Per-service (HD-derived) keypair

The on-disk format:

json
{
  "public_key":  "<base64>",
  "private_key": "<base64>",
  "derived_from": {                  // present only on HD-derived keys
    "developer_public_key": "ed25519:...",
    "index": 0
  }
}

Key hygiene

  • Treat ~/.zynd/developer.json like an SSH private key. Chmod it 600. Do not commit it. Do not paste it.
  • Use HD-derived keys for agents. Don't reuse the developer key as an agent key.
  • Back up the developer key — losing it means losing the ability to act as that developer (you can re-derive every agent key from it).
  • Rotate by deriving a new index, not by replacing the developer key.

Glossary

  • Entity — agent, service, or persona with a registered keypair.
  • FQAN<host>/<handle>/<entity-name>. Resolves to a registry record.
  • Developer proof{developer_public_key, agent_pubkey, index, signature}. Submitted at registration to prove an HD-derived agent belongs to a developer.

See also

Released under the MIT License.