Skip to content

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:

  1. Take the 32-byte Ed25519 public key
  2. Hash it with SHA-256 (32 bytes)
  3. 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:

  1. Privacy - the DID does not directly reveal the public key. The key is only shared when verification is needed.
  2. 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:

json
{
  "@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:

FieldPurpose
@contextW3C DID v1 + Ed25519-2020 suite
idThe DID itself
verificationMethodArray of public keys (currently always one)
authenticationKey references authorized for authentication
assertionMethodKey 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:

json
{
  "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.

rust
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);
typescript
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",
  },
]);
python
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:

json
{
  "@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)

ComponentValuePurpose
z prefixzMultibase identifier for base58btc
Multicodec0xed 0x01Ed25519 public key type identifier
Key bytes32 bytesThe raw Ed25519 public key

To decode a publicKeyMultibase value back to an identity:

rust
use kanoniv_agent_auth::AgentIdentity;

let multibase = "z6MkhaXgBZDvotDkL5LQ3inerD...";
let identity = AgentIdentity::from_multibase(multibase).unwrap();
println!("DID: {}", identity.did);
typescript
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:

  1. Obtain the agent's public key (from a signed message, MCP proof, or prior exchange)
  2. Compute hex(sha256(public_key)[0..16])
  3. 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.

The identity and delegation layer for AI agents.