Proof of Delegation: developer reference

BlindOracle identity · live since 2026-09-27 · spec version 1

This page defines how a BlindOracle or TheBaby agent tells you which agent it is, who runs it, who authorized it, and what it may do, and how you check that without trusting us or calling us. For the why and the live test, read "Which agent are you?".

Status: live since 27 September 2026. The first paid purchase using it settled on Base in tx 0x4492f2b1…7874, and the seller verified both signatures from public documents alone. We don't publish internal delegation records wholesale. They contain canary tokens and task text, so only redacted, individually attested copies leave the fleet. To check an agent's audit record instead, use Verify any agent.

1. Public documents

DocumentURLUsed for
DID document/.well-known/did.json (did:web:craigmbrown.com)Verifying credentials. Key #delegation-1 signs delegation credentials only; #key-1 signs audit credentials. Both are listed under assertionMethod.
Web Bot Auth key directory/.well-known/http-message-signatures-directoryVerifying HTTP request signatures (RFC 9421). JWKS of Ed25519 keys; the keyid is the RFC 7638 thumbprint. The directory response is itself signed and re-signed weekly.

2. What an agent request carries

User-Agent:       TheBabyFleet/1.0 (+https://craigmbrown.com/llms.txt; ...) agent=thebaby-extmkt-buyer
Agent-Delegation: <base64url( JCS( AgentDelegationCredential ) )>
Signature-Agent:  "https://craigmbrown.com"
Signature-Input:  sig1=("@authority" "signature-agent" "agent-delegation");created=...;expires=...;keyid="7EI1Qxm3eIAR-h_jvHpTsalEpsGZS4680Jn1kgBbot4";tag="web-bot-auth"
Signature:        sig1=:<base64 Ed25519 signature>:

3. AgentDelegationCredential (a grant)

A W3C VC 2.0 with type: ["VerifiableCredential", "AgentDelegationCredential"], issuer.id = "did:web:craigmbrown.com", validFrom / validUntil, and an eddsa-jcs-2022 DataIntegrityProof by did:web:craigmbrown.com#delegation-1.

credentialSubject fieldMeaning
id, agentNameWhich agent. id is urn:craigmbrown:agent:<name>; it does not resolve and doesn't claim to.
delegationKind30014, the fleet's ProofOfDelegation kind.
operatedByThe operator: DID, name, URL, contact, plus webBotAuthDirectory and webBotAuthKeyId, so you can tie the grant to the HTTP signature on the same request.
authorizedByWho authorized the agent: id, role, basis (the standing policy), and attestation, which states plainly that this is an issuer assertion, not proof of human presence.
permissions.allow / denyAction verbs, for example discover, purchase.
permissions.requiresHumanConfirmationActions the agent must not take without a human, for example raise_spend_caps, new_payment_rail.
permissions.maxUsdPerCall / maxUsdPerDaySpend caps as decimal strings ("0.03", "1"). Grants issued before 2026-09-27 02:00 UTC used JSON numbers; see section 7.
permissions.venuesWhere the grant applies, for example x402, mcp, agentverse.
auditTrailWhere actions under this grant are recorded, keyed by the credential id.

4. AgentDelegationAttestation

A public copy of one internal ProofOfDelegation record. Internally those records are signed with HMAC-SHA256, which only we can check. An attestation is issued only after the HMAC re-verifies, and it carries delegatedAt, delegatorSessionSha256, delegatorPassportHash, delegateSha256, internalEventId, internalPrevHash (the hash chain) and internalHmacVerified: true. The canary token is never published. The delegated prompt appears only as delegateSha256, so it can be disclosed later and checked against the hash.

5. Verify a credential

  1. Decode the header: base64url, then JSON.
  2. Resolve the issuer did:web to https://<host>/.well-known/did.json.
  3. Require proof.verificationMethod to start with <issuer>#, to appear in verificationMethod, and to be listed in assertionMethod.
  4. Compute sha256(JCS(proofOptions)) || sha256(JCS(credential without proof)), where proofOptions are type, cryptosuite, proofPurpose, verificationMethod, created.
  5. Verify the Ed25519 signature (proofValue is multibase z + base58btc) against the key's publicKeyMultibase (multicodec 0xed01 prefix).
  6. Check validFrom ≤ now ≤ validUntil.
import json, sys, hashlib, urllib.request, base58
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
vc = json.load(open(sys.argv[1])); proof = vc.pop("proof")
issuer = vc["issuer"]["id"]
doc = json.load(urllib.request.urlopen("https://" + issuer[len("did:web:"):] + "/.well-known/did.json"))
vm = next(m for m in doc["verificationMethod"] if m["id"] == proof["verificationMethod"])
assert proof["verificationMethod"] in doc["assertionMethod"]
key = base58.b58decode(vm["publicKeyMultibase"][1:])[2:]
jcs = lambda o: json.dumps(o, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode()
opts = {k: proof[k] for k in ("type","cryptosuite","proofPurpose","verificationMethod","created")}
Ed25519PublicKey.from_public_bytes(key).verify(bytes(base58.b58decode(proof["proofValue"][1:])),
    hashlib.sha256(jcs(opts)).digest() + hashlib.sha256(jcs(vc)).digest())
print("verified")

A full JCS library is safer than json.dumps in general. For credentials issued under this spec the two produce identical bytes, because no field is a JSON number that serializes differently.

6. Verify the HTTP request signature

  1. Parse Signature-Input: label, covered component list, and the created, expires, keyid and tag parameters. Reject if expires has passed.
  2. Fetch <Signature-Agent origin>/.well-known/http-message-signatures-directory. Only fetch https on hostnames that resolve to public IPs; this URL is caller-supplied.
  3. Pick the Ed25519 key whose RFC 7638 thumbprint equals keyid.
  4. Build the signature base, one line per covered component: "<name>": <value>. Header values are trimmed. The derived components are @authority (lowercase host), @method, @scheme, @path, @query, @request-target and @target-uri. Finish with "@signature-params": (<list>)<params>.
  5. Verify the Ed25519 signature over that base.
  6. If agent-delegation is covered and the signature verifies, the grant is bound to this request.

Our gateway runs this check on every inbound call and records the result without enforcing anything. The reference implementation is distribution/inbound_agent_identity.py in the BlindOracle service code.

7. Known limits

7a. Checking whether a grant was stopped

Grants issued since 2026-09-27 carry a credentialStatus entry, inside the signed payload, pointing at /.well-known/delegation-status.json. That file is itself an AgentDelegationStatusList credential signed by the same #delegation-1 key and valid for 48 hours. It lists:

To check: verify the list exactly as you verify a grant, confirm its validUntil has not passed, then look up the grant's id and agentName. A list you cannot fetch, cannot verify, or that has expired means the status is unknown. Treat that as unknown, never as active. Grants issued before 2026-09-27 carry no status entry and can only expire.

8. Want an attestation or a grant checked?

Ask for an attestation of a specific delegation event, or send us a grant you'd like cross-checked, through the agent onboarding page. The verification recipe above needs nothing from us.