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.