AgentFieldbuild

Decentralized identity

W3C DIDs with Ed25519 cryptography, automatic registration, and AES-256-GCM keystore

Cryptographic identity — DID and verifiable credentials

Every agent gets a cryptographic identity rooted in W3C DIDs -- no central certificate authority required.

AgentField assigns each agent and its functions a did:key identifier backed by Ed25519 key pairs. DIDs are generated deterministically on registration, organized in a 3-tier hierarchy (platform, agent, function), and stored in an AES-256-GCM encrypted keystore. Every execution can be cryptographically tied to the agent that performed it.

When you publish selected capabilities through external agent discovery, the public entry can include trust metadata derived from the same identity system. External clients can discover a reasoner, see the publisher identity, and decide whether to import it before any runtime call is allowed.

from agentfield import Agent

# Enable DID — agent + each function gets a did:key identity
app = Agent(
    node_id="treasury",
    version="1.0.0",
    enable_did=True,  # Ed25519 key pair, AES-256-GCM encrypted keystore
)

@app.reasoner()
async def secure_transfer(amount: float, execution_context=None) -> dict:
    # Every request carries the caller's cryptographic identity
    caller = execution_context.caller_did  # did:key:z6MkpTHR...
    my_did = app.did_manager.get_agent_did()  # did:key:z6MkhaXg...

    app.note(f"Transfer ${amount} authorized by {caller}", ["audit", "transfers"])

    # Function-level DIDs let you trace exactly which reasoner ran
    fn_did = app.did_manager.get_function_did("secure_transfer")

    return await app.ai(system="Process this transfer.", user=str(amount))

What just happened

The page example registered an agent, exposed its DID package, and showed that both the agent and its functions can be resolved through the control plane. The important takeaway is not just that identities exist, but that identity becomes inspectable infrastructure instead of hidden SDK state.

{
  "agent_did": "did:key:z6Mk...",
  "function_dids": [
    "did:key:z6Mk...#audit"
  ],
  "resolution_endpoints": [
    "/api/ui/v1/nodes/secure-agent/did",
    "/api/v1/registered-dids"
  ]
}
What you get
  • W3C DID identifiers -- agents and their functions each receive a did:key identifier backed by Ed25519 key pairs
  • Web-resolvable DIDs -- did:web documents served at standard paths for cross-network resolution
  • Automatic registration -- DIDs are generated deterministically when agents register with the control plane
  • 3-tier hierarchy -- platform root, agent node, and individual function DIDs. Agents that host sessions register and sign the same way agents that host reasoners and skills do.
  • Encrypted keystore -- private keys stored with AES-256-GCM encryption
  • Non-repudiation -- every execution can be cryptographically tied to the agent that performed it
Enabling DIDs
from agentfield import Agent

app = Agent(
    node_id="auditor",
    version="1.0.0",
    enable_did=True,  # Enabled by default
)

@app.reasoner()
async def audit(report: str) -> dict:
    # This execution will be signed with the agent's DID
    return await app.ai(system="Audit this report.", user=report)

app.run()
How it works

DID Hierarchy

AgentField creates a 3-tier DID hierarchy using deterministic key derivation:

LevelDerivation PathPurpose
Platform rootm/44'/0'Signs agent registration VCs; root of trust
Agent nodem/44'/1'/{node_index}Signs execution VCs; identifies the agent
Functionm/44'/1'/{node_index}/{function_index}Identifies specific reasoners and skills

Key Generation

Keys are derived deterministically from a master seed using HKDF (HMAC-based Key Derivation Function) with SHA-256:

  1. The control plane generates a 256-bit master seed on first initialization
  2. The root DID is derived from the master seed using path m/44'/0'
  3. Agent DIDs are derived using HKDF with the agent's node ID as context
  4. Function DIDs are derived from the agent DID with the function name as context

This means the same master seed always produces the same DID hierarchy -- deterministic and reproducible.

DID Formats

AgentField supports two DID methods:

did:key -- self-certifying identifier encoding the Ed25519 public key directly:

did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK

Resolvable by anyone who can decode the identifier -- no network call required.

did:web -- web-resolvable identifier tied to the control plane's domain:

did:web:agentfield.example.com:agents:auditor

Resolved by fetching the DID document from the agent's well-known path.

did:web Resolution

The control plane serves DID documents at standard paths:

GET /.well-known/did.json                # Platform DID document
GET /agents/{agentID}/did.json           # Agent-specific DID document

These documents follow the W3C DID Document specification and include the agent's public key, verification methods, and service endpoints.

Keystore

Private keys are encrypted at rest using AES-256-GCM:

PropertyValue
AlgorithmAES-256-GCM
Key size256 bits
NonceGenerated fresh per encryption operation
StorageLocal filesystem at ./data/keys/ (relative to working directory)
Directory permissions0700 (owner only)

The keystore currently supports local filesystem storage. The service interface is designed for future HSM and cloud KMS integration.

Configuration

Enable or disable DIDs in agentfield.yaml:

features:
  did:
    enabled: true
    keystore:
      type: local
      path: ./data/keys
    authorization:
      domain: agentfield.example.com
DID Authentication

Use DIDs for authenticating outbound HTTP requests between agents. The SDK signs requests with the agent's Ed25519 private key and the receiving agent verifies the signature using the sender's public DID.

Python -- DIDAuthenticator

from agentfield.did_auth import DIDAuthenticator
import json

# Create an authenticator with DID and private key
authenticator = DIDAuthenticator(
    did=app.did_manager.get_agent_did(),
    private_key_jwk=private_key_jwk,  # JWK-formatted Ed25519 private key
)

# Sign an outbound HTTP request body
body = json.dumps({"query": "portfolio risk"}).encode()
headers = authenticator.sign_headers(body)
# headers includes:
#   X-Caller-DID: did:key:z6Mk...
#   X-DID-Signature: <base64-encoded Ed25519 signature>
#   X-DID-Timestamp: <unix timestamp>
#   X-DID-Nonce: <hex-encoded random nonce>

import httpx
resp = await httpx.AsyncClient().post(
    "https://partner-api.example.com/data",
    content=body,
    headers={**headers, "Content-Type": "application/json"},
)

DIDAuthenticator methods:

MethodDescription
sign_headers(body)Generate signed DID auth headers for a request body
set_credentials(did, private_key_jwk)Set or update DID credentials
is_configuredProperty: whether DID and private key are loaded
didProperty: the agent's DID identifier

TypeScript

import { DIDAuthenticator } from '@agentfield/sdk';

const auth = new DIDAuthenticator(agentDid, privateKeyJwk);

// Sign outbound request body
const body = Buffer.from(JSON.stringify({ query: 'portfolio risk' }));
const headers = auth.signRequest(body);
// headers includes: X-Caller-DID, X-DID-Signature, X-DID-Timestamp, X-DID-Nonce

Go -- client.SignHTTPRequest()

import "github.com/Agent-Field/agentfield/sdk/go/client"

c, _ := client.New("http://localhost:8080", client.WithDIDAuth(agentDID, privateKeyJWK))

// SignHTTPRequest signs the request in-place with DID auth headers
body := []byte(`{"query":"portfolio risk"}`)
req, _ := http.NewRequest("POST", "https://partner-api.example.com/data", bytes.NewReader(body))
c.SignHTTPRequest(req, body)
// req now has X-Caller-DID, X-DID-Signature, X-DID-Timestamp, X-DID-Nonce headers

Enabling DID Auth on All Outbound Calls

Set RequireOriginAuth in Go agent config to validate that incoming execution requests include proper authorization. When EnableDID is set, outbound calls via Call() are automatically signed with DID auth headers.

app = Agent(
    node_id="secure-agent",
    enable_did=True,  # outbound calls are DID-signed automatically
)
Patterns

Verifying Agent Identity

Use the DID to verify that an execution was performed by a specific agent:

# Fetch the agent's DID
import requests

resp = requests.get("http://localhost:8080/api/v1/registered-dids")
dids = resp.json()

# Find the agent's public key
for did_info in dids:
    if did_info["agent_node_id"] == "auditor":
        public_key = did_info["public_key_jwk"]
        # Use this to verify VC signatures

Cross-Network Identity

Use did:web when agents need to verify each other across networks:

# Agent A resolves Agent B's identity
GET https://other-platform.com/agents/data-provider/did.json

# The DID document contains Agent B's public key
# Agent A can now verify VCs signed by Agent B

See Credentials for how DIDs are used to sign execution verifiable credentials.

ARD Trust Metadata

Public discovery entries can include a trust manifest with the publisher identity. In AgentField, that means an Agentic Resource Discovery entry can advertise the DID-backed identity of the control plane or agent function while still requiring a separate import and callable-binding decision on the consuming side.

{
  "displayName": "Contract Review Reasoner",
  "type": "application/openapi+json",
  "trustManifest": {
    "identity": "did:web:example.com",
    "identityType": "did"
  }
}

Use this as discovery-time trust context, not as a replacement for access policies or credential management.

API reference

Platform DID

GET /api/v1/did/agentfield-server

Returns the control plane's root DID and public key.

Issuer Public Key

GET /api/v1/did/issuer-public-key

Returns the Ed25519 public key used to sign verifiable credentials, in JWK format.

Registered DIDs

GET /api/v1/registered-dids

Lists all registered DIDs across the platform -- agents, reasoners, and skills.

Agent DID Resolution

Through the UI API:

GET /api/ui/v1/nodes/{nodeId}/did

Returns the DID identity package for a specific agent node, including the agent DID and all function DIDs.

DID Resolution Bundle

GET /api/ui/v1/did/{did}/resolution-bundle
GET /api/ui/v1/did/{did}/resolution-bundle/download

Returns a complete resolution bundle for offline verification: the DID document, the public key, and any associated verifiable credentials.