The protocol
This section is the centerpiece of the documentation. Agreely is a real protocol, not a vendor format: every receipt, request, and DID document is built from published standards (W3C DIDs and Verifiable Credentials, RFC 8785 JSON canonicalization, Ethereum keccak256, OpenZeppelin Merkle proofs, EdDSA, and WebAuthn) wired together so that an auditor with the artifacts and these rules can verify a grant without trusting Agreely.
Each page explains not just what the construction is but why that specific standard was chosen. Start here with identity, then read canonicalization and hashing, signatures and passkeys, the on-chain registry, and IPFS document integrity.
Two kinds of identity
Agreely has two distinct identity subjects, and they use different DID methods on purpose. Do not conflate them:
- Companies are
did:web:agreely.ca:c:{slug}. Agreely hosts the company DID document, bound to the company's verified real domain. - Citizens are
did:agreely:citizen:{opaque}. A purpose-built, opaque, anchored method.
The company method is covered under signatures and passkeys and DID documents. This page is about the citizen method.
did:agreely:citizen
The citizen DID format is:
did:agreely:citizen:<26-char Crockford base32 of 16 random bytes>
The suffix is fully opaque. It encodes no real-world identity, only 128 bits
of randomness rendered in Crockford base32 (digits and uppercase letters,
excluding I, L, O, and U so the handle is unambiguous when read aloud).
Sixteen random bytes encode to exactly 26 characters. An example:
did:agreely:citizen:9F8K2M4P7Q1R3T5V8W0X2Y4Z6B
The DID is the only key the citizen layer is scoped by; it is tenant-less. Every
passkey, recovery key, and key-history row joins through the DID, never through a
company. This is half of what gives Agreely its
unlinkability: the citizen side knows
nothing about a company's customerId.
did:agreely:citizen keeps the identifier stable and opaque while the keys
underneath it rotate freely. The identifier is anchored on-chain as an edgeless
commitment (a creation/rotation event that links to no consent), so its
existence is tamper-evident without exposing anything.
Key history keeps old receipts valid
A DID's keys change over time, but a receipt signed last year must still verify
this year. The citizen DID document is assembled from the full key history,
and every verification method carries a [validFrom, validUntil] authority
window:
{
"id": "did:agreely:citizen:9F8K...#passkey-1",
"controller": "did:agreely:citizen:9F8K...",
"type": "WebAuthnAuthenticationKey2024",
"publicKeyCose": "0x...",
"validFrom": "2026-01-10T14:00:00Z",
"validUntil": null
}
A signature made at time T resolves to whichever key was authoritative at T,
even after that key was rotated out. validUntil is present even when null so
a verifier can distinguish "still valid" (null) from "expired at" (a
timestamp). Recovery keys are exposed as Ed25519VerificationKey2020 and are
authorized solely for DID rotation (capabilityInvocation), never for
signing consent.
See the full shape in DID documents.