FR EN

DID documents

A verifier resolves two DID documents to check a receipt: the company document for companySig, and the citizen document for the WebAuthn assertion. They use different DID methods and expose keys differently.

The company DID document

Resolved from did:web:agreely.ca:c:{slug} at https://agreely.ca/c/{slug}/did.json. It resolves only when the company has a verified domain and an active signing key.

{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/data-integrity/v2"
  ],
  "id": "did:web:agreely.ca:c:acme",
  "alsoKnownAs": ["did:web:acme.com"],
  "verificationMethod": [
    {
      "id": "did:web:agreely.ca:c:acme#kms-1",
      "type": "Multikey",
      "controller": "did:web:agreely.ca:c:acme",
      "publicKeyMultibase": "z6Mk...base58btc..."
    }
  ],
  "assertionMethod": ["did:web:agreely.ca:c:acme#kms-1"],
  "authentication": ["did:web:agreely.ca:c:acme#kms-1"]
}
Field Meaning
id The Agreely-hosted company DID.
alsoKnownAs The company's verified real domain DID, the binding that proves the Agreely-hosted id belongs to the real organization.
verificationMethod[].type Multikey.
verificationMethod[].publicKeyMultibase The Ed25519 public key as z-prefixed base58btc of `(ed25519-pub multicodec 0xed01
assertionMethod / authentication Reference the signing key fragment.

The root passkey and delegation (additive)

Once a company has registered a root passkey and signed a delegation, the same document additively publishes two more elements. A company with no root or delegation produces exactly the document above.

{
  "verificationMethod": [
    { "id": "did:web:agreely.ca:c:acme#kms-1", "type": "Multikey", "controller": "did:web:agreely.ca:c:acme", "publicKeyMultibase": "z6Mk..." },
    {
      "id": "did:web:agreely.ca:c:acme#root-1",
      "type": "WebAuthnAuthenticationKey2024",
      "controller": "did:web:agreely.ca:c:acme",
      "publicKeyCose": "0xa5010203262001...",
      "rpId": "agreely.ca"
    }
  ],
  "capabilityDelegation": ["#root-1"],
  "delegation": {
    "type": "agreely.keyDelegation.v1",
    "body": { "type": "agreely.keyDelegation.v1", "companyDid": "did:web:agreely.ca:c:acme", "delegatee": { "keyId": "0x..." }, "rootKeyId": "0x...", "registry": "0x...", "chainId": "8453", "rpId": "agreely.ca", "nonce": "0x...", "validFrom": "2026-07-20T15:00:00Z", "validUntil": "2027-07-20T15:00:00Z" },
    "proof": {
      "type": "WebAuthnDelegationProof2026",
      "verificationMethod": "did:web:agreely.ca:c:acme#root-1",
      "authenticatorData": "<base64url>",
      "clientDataJSON": "<base64url>",
      "signature": "<base64url>"
    }
  }
}
Field Meaning
#root-1 The company's root passkey, under capabilityDelegation (the W3C-correct relationship: "the root delegates a capability to a subordinate key"). Its publicKeyCose is the raw COSE public key as 0x-hex; its keccak thumbprint must equal the company's DNS root=.
capabilityDelegation References #root-1: the root's only capability is to delegate, never to sign a consent or authenticate.
delegation.body The exact number-free canonical body the WebAuthn challenge was hashed over (challenge = keccak(JCS(body))), so a verifier recomputes the challenge itself.
delegation.proof The documented proprietary proof type WebAuthnDelegationProof2026: the raw WebAuthn assertion artifacts (base64url) verified against #root-1's COSE key. There is no ratified WebAuthn Data Integrity cryptosuite, hence a published verification recipe.

An enrolled recovery key, if any, additionally appears as Ed25519VerificationKey2020 under capabilityInvocation (authorized only to rotate the root).

The citizen DID document

Resolved from did:agreely:citizen:.... It is public, unauthenticated, and tenant-less by design (any verifier may resolve it). It is assembled from the full key history, so every key the DID ever held appears with its authority window.

{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/suites/ed25519-2020/v1"
  ],
  "id": "did:agreely:citizen:9F8K2M4P7Q1R3T5V8W0X2Y4Z6B",
  "verificationMethod": [
    {
      "id": "did:agreely:citizen:9F8K2M4P7Q1R3T5V8W0X2Y4Z6B#passkey-1",
      "controller": "did:agreely:citizen:9F8K2M4P7Q1R3T5V8W0X2Y4Z6B",
      "validFrom": "2026-01-10T14:00:00Z",
      "validUntil": null,
      "type": "WebAuthnAuthenticationKey2024",
      "publicKeyCose": "0xa5010203262001..."
    },
    {
      "id": "did:agreely:citizen:9F8K2M4P7Q1R3T5V8W0X2Y4Z6B#recovery-1",
      "controller": "did:agreely:citizen:9F8K2M4P7Q1R3T5V8W0X2Y4Z6B",
      "validFrom": "2026-01-10T14:00:00Z",
      "validUntil": null,
      "type": "Ed25519VerificationKey2020",
      "publicKeyMultibase": "z6Mk...base58btc..."
    }
  ],
  "authentication": [
    "did:agreely:citizen:9F8K2M4P7Q1R3T5V8W0X2Y4Z6B#passkey-1"
  ],
  "capabilityInvocation": [
    "did:agreely:citizen:9F8K2M4P7Q1R3T5V8W0X2Y4Z6B#recovery-1"
  ]
}
Field Meaning
validFrom / validUntil The key's authority window. validUntil is present even when null so a verifier distinguishes "still valid" from "expired at T". A receipt signed at time T resolves to whichever key was authoritative at T.
WebAuthnAuthenticationKey2024 A passkey. Its publicKeyCose is the COSE-encoded public key as 0x-hex; verify the receipt's WebAuthn assertion against it.
Ed25519VerificationKey2020 A recovery key, exposed as publicKeyMultibase.
authentication Passkey fragments (authorized to sign consent).
capabilityInvocation Recovery-key fragments, authorized solely for DID rotation, never for signing consent.

The window is what keeps old receipts valid

When you verify proof[1] of a receipt, take its verificationMethod fragment, find that method in the citizen document, and confirm its [validFrom, validUntil] window contains the receipt's grantedAt. Then verify the assertion against that method's COSE key. A rotated-out key still verifies its historical receipts.

Next