The did:agent: Method
did:agent: is a self-issued Decentralized Identifier method designed for AI agents. It produces a short, deterministic identifier from an Ed25519 public key with no registry, no blockchain, and no network calls.
Format
did:agent:{hex(sha256(public_key_bytes)[0..16])}The DID is constructed in three steps:
- Take the 32-byte Ed25519 public key
- Hash it with SHA-256 (32 bytes)
- Take the first 16 bytes of the hash and hex-encode them (32 hex characters)
The result is a 42-character string:
did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
|--- 32 lowercase hex chars ---|Why truncate the hash?
The full SHA-256 hash is 32 bytes (64 hex chars). Truncating to 16 bytes (32 hex chars) keeps DIDs short while still providing 128 bits of collision resistance - far more than needed for agent identity. For comparison, UUIDs provide 122 bits.
Why hash at all?
The public key is already 32 bytes. Hashing serves two purposes:
- Privacy - the DID does not directly reveal the public key. The key is only shared when verification is needed.
- Format consistency - the DID is always the same length regardless of key type. If a future version supports different curves, the format stays stable.
DID Documents
Every did:agent: identity can produce a W3C-compliant DID Document. This document contains the public key material needed to verify signatures, following the W3C DID Core specification.
Minimal document
A DID Document with no service endpoints:
{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"verificationMethod": [
{
"id": "did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6#key-1",
"type": "Ed25519VerificationKey2020",
"controller": "did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"publicKeyMultibase": "z6MkhaXgBZDvotDkL5LQ3inerD..."
}
],
"authentication": [
"did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6#key-1"
],
"assertionMethod": [
"did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6#key-1"
]
}Key fields:
| Field | Purpose |
|---|---|
@context | W3C DID v1 + Ed25519-2020 suite |
id | The DID itself |
verificationMethod | Array of public keys (currently always one) |
authentication | Key references authorized for authentication |
assertionMethod | Key references authorized for signing assertions |
Verification method
The verification method uses the Ed25519VerificationKey2020 type from the Ed25519 Signature Suite. The public key is encoded as publicKeyMultibase - a compact format that includes the key type.
If the keypair was generated with a timestamp, the verification method includes a created field:
{
"id": "did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6#key-1",
"type": "Ed25519VerificationKey2020",
"controller": "did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"publicKeyMultibase": "z6MkhaXgBZDvotDkL5LQ3inerD...",
"created": "2026-03-15T10:30:00.000Z"
}Service endpoints
DID Documents can include service endpoints that describe how to reach the agent. This is useful for agent-to-agent communication and service discovery.
use kanoniv_agent_auth::{AgentKeyPair, ServiceEndpoint};
let keypair = AgentKeyPair::generate();
let identity = keypair.identity();
let services = vec![
ServiceEndpoint::new(
"#messaging",
"AgentMessaging",
"https://example.com/agent/msg",
),
ServiceEndpoint::new(
"#resolve",
"KanonivResolve",
"https://api.kanoniv.com/v1/resolve",
),
];
let doc = identity.did_document_with_services(&services);import { generateKeyPair, didDocumentWithServices } from "@kanoniv/agent-auth";
const keypair = generateKeyPair();
const doc = didDocumentWithServices(keypair.identity, [
{
id: "#messaging",
serviceType: "AgentMessaging",
endpoint: "https://example.com/agent/msg",
},
{
id: "#resolve",
serviceType: "KanonivResolve",
endpoint: "https://api.kanoniv.com/v1/resolve",
},
]);from kanoniv_agent_auth import AgentKeyPair, ServiceEndpoint
keypair = AgentKeyPair.generate()
identity = keypair.identity()
services = [
ServiceEndpoint("#messaging", "AgentMessaging", "https://example.com/agent/msg"),
ServiceEndpoint("#resolve", "KanonivResolve", "https://api.kanoniv.com/v1/resolve"),
]
doc_json = identity.did_document_with_services(services)The resulting document includes a service array:
{
"@context": ["..."],
"id": "did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"verificationMethod": ["..."],
"authentication": ["..."],
"assertionMethod": ["..."],
"service": [
{
"id": "did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6#messaging",
"type": "AgentMessaging",
"serviceEndpoint": "https://example.com/agent/msg"
},
{
"id": "did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6#resolve",
"type": "KanonivResolve",
"serviceEndpoint": "https://api.kanoniv.com/v1/resolve"
}
]
}Fragment IDs (strings starting with #) are automatically prefixed with the DID. Full URIs are used as-is.
Multibase encoding
Public keys in DID Documents use multibase encoding - a self-describing format that includes the key type.
The encoding is: z + base58btc(0xed 0x01 + 32 public key bytes)
| Component | Value | Purpose |
|---|---|---|
z prefix | z | Multibase identifier for base58btc |
| Multicodec | 0xed 0x01 | Ed25519 public key type identifier |
| Key bytes | 32 bytes | The raw Ed25519 public key |
To decode a publicKeyMultibase value back to an identity:
use kanoniv_agent_auth::AgentIdentity;
let multibase = "z6MkhaXgBZDvotDkL5LQ3inerD...";
let identity = AgentIdentity::from_multibase(multibase).unwrap();
println!("DID: {}", identity.did);import { identityFromMultibase } from "@kanoniv/agent-auth";
const multibase = "z6MkhaXgBZDvotDkL5LQ3inerD...";
const identity = identityFromMultibase(multibase);
console.log("DID:", identity.did);TIP
Multibase encoding is used in DID Documents to comply with the W3C Ed25519-2020 suite. For MCP proofs and wire-format transport, public keys use plain hex encoding (64 characters for 32 bytes) because hex is simpler to handle across Rust, TypeScript, and Python without base58 dependencies.
Resolution
did:agent: is a "self-resolving" method. There is no external resolver or registry. To resolve a DID:
- Obtain the agent's public key (from a signed message, MCP proof, or prior exchange)
- Compute
hex(sha256(public_key)[0..16]) - Compare with the claimed DID
If they match, the DID is authentic. If they don't, the message is from a different agent than claimed. This makes did:agent: zero-dependency - verification never requires a network call.
