ADR 003: DID Resolver
Category | Status |
---|---|
Authors | Alex Tweeddale, Renata Toktar, Ankur Banerjee |
ADR Stage | PROPOSED |
Implementation Status | In Progress |
Start Date | 2022-02-22 |
Last Updated | 2022-08-04 |
The
did:cheqd
method ADR defines how DIDs are created and read from ledger. According to the W3C DID Core specification, DID methods are expected to provide standards-compliant methods of DID and DID Document ("DIDDoc") production.The cheqd DID Resolver is designed to implement the W3C DID Resolution specification for
did:cheqd
method.The DID Resolution specification prescribes a defined algorithm with standardised behaviour for expected and unexpected inputs that a conforming DID method must be able to produce.
All conforming DID resolvers implement
resolve
and resolveRepresentation
abstract functions, as defined in the DID Resolution specification.The
resolve
function is intended to fetch the abstract form of the DID Document, as stored on the ledger. This abstract/raw form may not necessarily be in JSON/JSON-LD format as the underlying data persistence layer where the DIDDoc is stored for any particular method might use different serialisation/storage formats.resolve(did, resolutionOptions) →
« didResolutionMetadata, didDocument, didDocumentMetadata »
Since cheqd uses the Cosmos SDK blockchain framework, the underlying data storage and retrieval ("resolve") mechanisms used rely on those offered by the Cosmos SDK framework. Cosmos SDK uses Protobuf (Protocol Buffers) encoding for its wire protocol.
Cosmos SDK framework typically provides gRPC/gRPC-Web, JSON-RPC, and REST API endpoints for on-ledger modules and functionality.
For example,
did:cheqd:testnet:DAzMQo4MDMxCjgwM
can be fetched using the native Cosmos SDK REST API endpoint (or equivalent endpoints). This provides responses that would meet the abstract definition of a resolve
function as defined in the DID Core specification.In case of the cheqd network testnet, an instance of this
resolve
endpoint through the Cosmos SDK REST API would be api.cheqd.network/cheqd/v1/did/did:cheqd:testnet:97e351e6-2d9d-4314-82ec-e0d12bc5de43 which returns the following response:{
"did": {
"context": [],
"id": "did:cheqd:testnet:97e351e6-2d9d-4314-82ec-e0d12bc5de43",
"controller": [],
"verification_method": [
{
"id": "did:cheqd:testnet:97e351e6-2d9d-4314-82ec-e0d12bc5de43#key1",
"type": "Ed25519VerificationKey2020",
"controller": "did:cheqd:testnet:97e351e6-2d9d-4314-82ec-e0d12bc5de43",
"public_key_jwk": {
"crv": "Ed25519",
"kty": "OKP",
"x": "q8-CHj4_nIYo8tK5RdjYbXlsTUnwW_i4gIEclps2i2o"
}
}
],
"authentication": ["did:cheqd:testnet:97e351e6-2d9d-4314-82ec-e0d12bc5de43#key1"],
"assertion_method": [],
"capability_invocation": [],
"capability_delegation": [],
"key_agreement": [],
"service": [],
"also_known_as": []
},
"metadata": {
"created": "2022-07-19T08:29:07Z",
"updated": "",
"deactivated": false,
"version_id": "cfe2f51f-8ec5-4fd8-8ab9-61859de879f4",
}
}
As you can see in the response body above, this is the raw Protobuf fetched from the cheqd testnet ledger, marshalled into a JSON form. Crucially, this form has certain deviations from the JSON/JSON-LD production expected in DID Core specification:
- 1.JSON key names that correlate to DID Core properties are listed in
snake_case
, rather thancamelCase
as required. This is because Protobuf standard linting rules require these properties to be defined insnake_case
. - 2.DID Core properties with empty values are still shown in this JSON, whereas the requirement is to drop them from standards-compliant DIDDoc representations.
The
resolveRepresentation
abstract function, as defined in DID Core specification, is intended to address concerns similar to the ones highlighted above to product a standards-compliant JSON/JSON-LD representation of a DIDDoc.resolveRepresentation(did, resolutionOptions) →
« didResolutionMetadata, didDocumentStream, didDocumentMetadata »
For example, a
resolveRepresentation
function could derive a valid standards-compliant representation of did:cheqd:testnet:DAzMQo4MDMxCjgwM
from the above resolve
function. The response would be similar to the one below containing Resolution Metadata, DIDDoc, and DIDDoc Metadata:{
"didResolutionMetadata": {
"contentType": "application/did+ld+json",
"retrieved": "2022-08-03T09:52:49Z",
"did": {
"didString": "did:cheqd:testnet:ea2b76cf-a118-403a-8f49-244e56c9dcb8",
"methodSpecificId": "ea2b76cf-a118-403a-8f49-244e56c9dcb8",
"method": "cheqd"
}
},
"didDocument": {
"id": "did:cheqd:testnet:ea2b76cf-a118-403a-8f49-244e56c9dcb8",
"verificationMethod": [
{
"id": "did:cheqd:testnet:ea2b76cf-a118-403a-8f49-244e56c9dcb8#key1",
"type": "Ed25519VerificationKey2020",
"controller": "did:cheqd:testnet:ea2b76cf-a118-403a-8f49-244e56c9dcb8",
"publicKeyMultibase": "z6jVkB274neVf7iJETpMECwznBF8wDe8tpvF4BZLRZgMU"
}
],
"authentication": ["did:cheqd:testnet:ea2b76cf-a118-403a-8f49-244e56c9dcb8#key1"]
},
"didDocumentMetadata": {
"created": "2022-07-19T08:29:07Z",
"versionId": "a0a4f3a3-829d-4188-bc78-8bea6a522037",
"linkedResourceMetadata": [
{
"resourceURI": "did:cheqd:testnet:ea2b76cf-a118-403a-8f49-244e56c9dcb8/resources/44547089-170b-4f5a-bcbc-06e46e0089e4",
"resourceCollectionId": "ea2b76cf-a118-403a-8f49-244e56c9dcb8",
"resourceId": "44547089-170b-4f5a-bcbc-06e46e0089e4",
"resourceName": "DemoResource",
"resourceType": "CL-Schema",
"mediaType": "application/json",
"created": "2022-07-19T08:40:00Z",
"checksum": "7b2022636f6e74656e74223a202274657374206461746122207d0ae3b0c44298",
"previousVersionId": "", // empty string if no previous version, otherwise, resourceId of previous version
"nextVersionId": "" // empty string if no new version, otherwise, resourceId of new version
}
]
}
}
As described above, the abstract
resolve
function is already available for the cheqd ledger via the default Cosmos SDK gRPC/REST API endpoints. Our primary objective with building a DID Resolver for cheqd was to design this resolveRepresentation
piece as a standalone component that was not packaged within the cheqd-node ledger code.This objective has certain advantages:
- Updates to DID Resolver code can be carried out and released independently of cheqd-node releases. As a consequence, there's no need to go through an on-ledger governance vote, and voting period to make a change to
resolveRepresentation
. - A separate web service module would allow for flexibility in how to handle complex scenarios on DID URL Dereferencing, error code handling for DID URL requests, and safely handling content transport for various media types.
- Making the DID Resolver a standalone, non-ledger module allows for an operator of this web service to independently scale their service horizontally and vertically.
We explored two architectural patterns for how a DID Resolver could be implemented for the cheqd ledger. The objective here was to explore and provide DID resolution operators multiple approaches for running resolution service, each with their own pros and cons (which are discussed below).
- 1."Full" cheqd DID Resolver
- 1.Since the cheqd-node ledger / Cosmos SDK is written in Golang, this resolver would consist of Golang libraries imported from the existing ledger code. This promotes code reuse.
- 2.Data would be fetched from the ledger using the gRPC endpoint on a node, which allows it (by default) to take place over an encrypted channel since gRPC uses HTTP/2.
- 3.Data retrieved would be in the native Protobuf representation as stored on ledger, thus allowing data integrity computations to be made.
- 2."Light" cheqd DID Resolver
- 1.Universal Resolver drivers are designed to be run as Docker containers. A limitation of this approach is that the computation footprint of a compute resource can be quite high, e.g., a Docker container may be 100 MB+ in size and suffer from slow startup times in a "cold-start" scenario.
- 2.Thus, our "Light" DID Resolver idea was to explore using Cloudflare Workers, a lightweight serverless compute platform. As a comparison, Cloudflare Workers are limited to 1 MB in size and have extremely low cold-start times. (We use Cloudflare Workers in our Cosmos SDK Custom Data API, for example.)
- 3.Cloudflare Workers can also be deployed outside the Cloudflare service in a Docker container using Miniflare. This could be used to provide a Docker container deployment option for the Universal Resolver
did:cheqd
driver. - 4.However, a limitation of Cloudflare Workers is they do not allow a gRPC request to be made to an external endpoint. This would force the "Light" cheqd Resolver to use the gRPC-Web / REST endpoint
resolve
implementation to fetch data from the ledger. This could be considered a higher risk profile in terms of data integrity by resolver operators / client applications.
Both of the architectural patterns above are designed so that a Universal Resolver driver for
did:cheqd
could be created. The Universal Resolver project aims to provide a common REST API definition for DID Resolution where each DID method can provide a Docker container that with an easy-to-deploy mechanism for the specific DID method.The Full cheqd DID Resolver is able to use
github.com/cheqd/cheqd-node
as a Golang module for send resolve
requests to a cheqd node instance to fetch DIDDoc / Resources from the ledger.Since the Full cheqd DID Resolver is wrapped for usage as a Docker container image using the Universal Resolver specification, the end-to-end sequence diagram for our DID Resolver would look like below:

Full cheqd DID Resolver sequence diagram
The Full cheqd DID Resolver is designed to handle requests concurrently, while reducing the risk of large quantities of threads and requests blocking the efficiency of the on-ledger services.
Since Cosmos SDK SDK encodes data in Protobuf, the DID Resolver "marshalls" them to JSON. The software class diagram below describes how these components/methods are tied together:

Full cheqd DID Resolver class diagram
Figure 2: "Full" cheqd DID Resolver class diagram
Marshalling/unmarshalling requests back-and-forth between Protobuf and JSON is carried out by services in the "Full" DID Resolver

Full cheqd DID Resolver Protobuf <-> JSON marshalling
The cheqd DID Resolver complies with the rules and algorithm defined in Decentralized Identifier Resolution (DID Resolution) v0.2. This section clarifies and expands some descriptions specific to cheqd.
If the output of the DID URL dereferencing function contains thedidDocumentStream
:
If the value of theAccept
HTTP header is absent orapplication/did+ld+json
(or other media type of a conformant representation of a DID document):
The HTTP response status code MUST be200
. The HTTP response MUST contain aContent-Type
header. The value of this header MUST beapplication/did+ld+json
(or other media type of a conformant representation of a DID document). The HTTP response body MUST contain thedidDocumentStream
, in the representation corresponding to theAccept
HTTP header.
Since the cheqd DID Resolver APIs are REST APIs, the default
Content-Type: application/did+ld+json
encoding is used if the Accept
header is not explicitly set since it matches the Accept: */*
header that most client applications send.- Response HTTP headers
- Status code
200 OK
Content-Type: application/did+ld+json
- Response HTTP body
didDocument
/contentStream
contains@context
section;didResolutionMetadata
/dereferencingMetadata
contentType
field isapplication/did+ld+json
- Response HTTP headers
- Status code
200 OK
Content-Type: application/ld+json;profile="https://w3id.org/did-resolution
- Response HTTP body
didDocument
/contentStream
contains@context
section;didResolutionMetadata
/dereferencingMetadata
contentType
field isapplication/ld+json;profile="https://w3id.org/did-resolution
- Response HTTP headers
- Status code
200 OK
Content-Type: application/did+json
- Response HTTP body
didDocument
/contentStream
DOES NOT contain@context
section;didResolutionMetadata
/dereferencingMetadata
ContentType
field isapplication/did+json
Requests to fetch on-ledger cheqd Resources are considered as a DID URL Dereferencing scenario it uses DID URL paths to lead to a Resource object, rather than a DIDDoc.
On the other hand, on-ledger cheqd Resources metadata requests are handled like DID URL Resolution since the result is a subsection of
didDocumentMetadata
specific to that resource.API endpoints related to on-ledger cheqd Resources are described below. All of these request types are
GET
requests, with POST
and PUT
requests disallowed.Returns the Resource data/payload stored on ledger for specified resource.
HEAD
request type is also allowed for this endpoint since it can be used for HTTP content negotiation phase by client applications. In this case, only the HTTP Response headers are returned without the body.Endpoint:
/1.0/identifiers/<did>/resources/<resource_id>
- 1.Request HTTP headers
- 1.
Accept
should allow*/*
or match themediaType
of the Resource - 2.
- 2.Response HTTP headers
- 1.Status code
200 OK
- 2.
Content-Type
should be set tomediaType>
of the Resource - 3.
Content-Encoding
should be set to a valid content compression method inAccept-Encoding
and response compressed accordingly - 4.
- 3.Response HTTP body
- 1.Resource, encoded correctly according to the
mediaType
Return metadata for a specified resource. This is effectively a portion of the DIDDoc Metadata block.
Endpoint:
/1.0/identifiers/<did>/resources/<resource_id>/metadata
Returns metadata for all Resources directly linked to/controlled by a DID. This is effectively the full
linkedResourceMetadata
section in DIDDoc Metadata block.Endpoint:
/1.0/identifiers/<did>/resources/all
Alternative endpoints
- 1.
/1.0/identifiers/<did>/resources/
- 1.Status code
301
- 2.Redirect to
/resources/all
- 2.
/1.0/identifiers/<did>/resources
- 1.Throw an
invalidDidUrl
error
The DID Resolution specification defines an algorithm for how invalid DID URL Resolution/Dereferencing errors should be handled. The cheqd DID Resolver aims to implement all of these scenarios, with the correct HTTP Response status codes based on the specific error encountered.
The DID resolution output should always conform to the following format:
( didResolutionMetadata, didDocument, didDocumentMetadata )
If the resolution is unsuccessful, the DID resolver should return the following result:
didResolutionMetadata
contains"error" : "<Error message>"
didDocument
: nulldidDocumentMetadata
:[]
The DID dereferencing output should always conform to the following format:
( dereferencingMetadata, contentStream, contentMetadata )
dereferencingMetadata
contains"error" : "<Error message>"
contentStream
: nullcontentMetadata
:[]
Given the drawbacks associated with a Light cheqd DID Resolver being unable to send gRPC requests to a cheqd node instance, the decision was taken to (initially) implement the Full cheqd DID Resolver architecture. Future work might separately consider and design a Light DID Resolver profile that can work with Cloudflare Workers while also allowing deployment through Docker.
Compiled packages/binaries for the Full cheqd DID Resolver will be made available as a Docker container image with standardised Docker Compose configuration as defined in the Universal Resolver driver development guide to provide an easy mechanism for DID Resolution operators to incorporate
did:cheqd
as one of the supported DID methods they handle.- 1.Operators should be able to configure gRPC endpoints for mainnet as as testnet (or any other cheqd network namespaces) so that a single DID Resolver instance can resolve requests to any network. (E.g.,
grpc.cheqd.net
for mainnet,grpc.cheqd.network
for testnet) - 2.For each supported cheqd network namespace (e.g., mainnet, testnet), operators should be able to define one or more upstream cheqd node gRPC endpoints. (E.g., additional fallback endpoints besides
grpc.cheqd.net
for mainnet.) In case any one of the upstream endpoints is unavailable, this allows the DID Resolver to try a different upstream gRPC endpoint for resiliency. - 3.Operators should be able to define whether their gRPC pull/request is secure (default, carried out over HTTP.2 with TLS handshake) or insecure (optional, equivalent to a
grpcurl -plaintext
connection downgrade). This accommodates scenarios where gRPC endpoints may not necessarily be equipped to handle TLS handshakes, since the default Cosmos SDK / cheqd node configuration endpoints do not have TLS certificates defined.
- 1.Not applicable, since this would be the first release of cheqd DID Resolver
- 1.Full cheqd DID Resolver reuses existing code to handle some parts of resolve and representing DIDDocs.
- 2.Third-party applications that want to implement their own DID Resolver implementations for cheqd can consume the current implementation as a Golang module (e.g.,
import "github.com/cheqd/cheqd-did-resolver/services"
). - 3.Universal Resolver driver implementation provides a standardised way of incorporating
did:cheqd
into Universal Resolver instances that support multiple DID methods.
- 1.Use of a custom DID URL path (e.g.,
/1.0/identifiers/{did}/resources/{resource-id}
can be non-standard). Efforts should be made to see if this can implemented instead using DID URL queries according to the Trust over IP Foundation "DID URL Resource Parameter" specification.
- 1.Lack of a Light cheqd DID Resolver, at least initially, might not give an easy and computationally-cheap alternative to running Docker containers.
Last modified 20d ago