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.
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/did/v2/did:cheqd:testnet:97e351e6-2d9d-4314-82ec-e0d12bc5de43 which returns the following response:
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:
JSON key names that correlate to DID Core properties are listed in snake_case
, rather than camelCase
as required. This is because Protobuf standard linting rules require these properties to be defined in snake_case
.
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.
For example, a resolveRepresentation
function could derive a valid standards-compliant representation of did:cheqd:testnet:ea2b76cf-a118-403a-8f49-244e56c9dcb8
from the above resolve
function. The response would be similar to the one below containing Resolution Metadata, DIDDoc, and DIDDoc Metadata:
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).
"Full" cheqd DID Resolver
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.
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.
Data retrieved would be in the native Protobuf representation as stored on ledger, thus allowing data integrity computations to be made.
"Light" cheqd DID Resolver
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.
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.)
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.
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:
Figure 1: "Full" cheqd DID Resolver sequence diagram (editable version)
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:
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
Figure 3: "Full" cheqd DID Resolver Protobuf <-> JSON marshalling (editable version)
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.
did:cheqd
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.
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)
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.
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.
Not applicable, since this would be the first release of cheqd DID Resolver
Full cheqd DID Resolver reuses existing code to handle some parts of resolve and representing DIDDocs.
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"
).
Universal Resolver driver implementation provides a standardised way of incorporating did:cheqd
into Universal Resolver instances that support multiple DID methods.
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.
Lack of a Light cheqd DID Resolver, at least initially, might not give an easy and computationally-cheap alternative to running Docker containers.
W3C Decentralized Identifiers (DIDs) recommendation
W3C Decentralized Identifier Resolution (DID Resolution) specification
This ADR defines how resources (e.g., text, JSON, images, etc) can be created and referenced using a persistent and unique did:cheqd
DID URL.
Each resource will be linked with a DID Document, with create/update operations controlled using the specified verification methods in the associated DID Document.
Trust over IP Foundation (ToIP) describes how "resources" could be generically defined and accessed using DID URLs. In a self-sovereign identity (SSI) ecosystem, such resources are often required in tandem with W3C Verifiable Credentials, which is a standard way of representing portable digital credentials that represent claims about its subjects and can be verified via digital proofs.
Common types of resources that might be required to issue and validate Verifiable Credentials are:
Schemas: Describe the fields and content types in a credential in a machine-readable format. Prominent examples of this include Schema.org, Hyperledger Indy SCHEMA
objects, etc.
Status lists: Allow recipients of a Verifiable Credential exchange to check the revocation status of a credential for validity. Prominent examples of this include the W3C Status List 2021
specification, W3C Revocation List 2020
, Hyperledger Indy revocation registries, etc.
Visual representations for Verifiable Credentials: Although Verifiable Credentials can be exchanged digitally, in practice most identity wallets want to present "human-friendly" representations. This allows the credential representation to be shown according to the brand guidelines of the issuer, internationalisation ("i18n") translations, etc. Examples of this include the Overlays Capture Architecture (OCA) specification, Apple Wallet PassKit (".pkpass
"), Google Wallet Pass, etc.
Figure 1: Mobile boarding passes in Apple Wallet showing different visual styles (source British Airways Media centre
Such visual representations can also be used to quickly communicate information visually during identity exchanges, such as airline mobile boarding passes. In the example above from British Airways, the pass at the front is for a "Gold" loyalty status member, whereas the pass at the back is for a "standard" loyalty status member. This information can be represented in a Verifiable Credential, of course, but the example here uses the Apple Wallet / Google Wallet formats to overlay a richer, "human-friendly" display.
More broadly, there are other resources that might be relevant for issuers and verifiers in a self-sovereign identity exchange:
Documents related to SSI ecosystems: ToIP recommends making Governance Frameworks available through DID URLs, which would typically be a text file, a Markdown file, PDF etc. This, for example, can enable parties building self-sovereign identity ecosystems to use DIDs to reference Governance Frameworks they conform to, at different levels of the technical stack.
Logos: Issuers may want to provide authorised image logos to display in relation to their DID or Verifiable Credentials. Examples of this include key-publishing sites like Keybase.io (which is used by Cosmos SDK block explorers such as our own to show logos for validators) and "favicons" (commonly used to set the logo for websites in browser tabs).
Decentralized Identifiers (DIDs) are often stored on ledgers (e.g., cheqd, Hyperledger Indy), distributed storage (e.g., IPFS in Sidetree), or non-ledger distributed systems (e.g., KERI).
DIDs can be stored on traditional centralised-storage endpoints (e.g., did:web
, did:git
) but this comes with certain drawbacks:
DIDs could be tampered by compromising the hosting provider: DIDs and DID Documents ("DIDDocs") stored at a centralised web endpoint can be compromised and replaced by malicious actors.
Hosting providers could unilaterally cease to host particular clients: Hosting providers could terminate accounts due to factors such as non-payment of fees, violation of Terms of Service, etc.
Single point-of-failure in resiliency: Even for highly-trusted and sophisticated hosting providers who may not present a risk of infrastructure being compromised, a service outage at the hosting provider can make a DID anchored on their systems inaccessible.
See notable examples of service outages from major cloud providers: Amazon Web Services (AWS), Microsoft Azure, Google Cloud, Facebook / Meta, GitHub, Cloudflare...\
Source: Why Facebook, Instagram, and WhatsApp All Went Down Today. Figure 2: Graph showing drop in Facebook traffic from their global service outage in 2021 (source: Kentik).
In particular, the 2021 global Facebook outage also took down apps that used "Login with Facebook" functionality. This highlights the risks of "contagion impact" (e.g., a different Facebook outage took down Spotify, TikTok, Pinterest) of centralised digital systems - even ones run by extremely-capable tech providers.
Link rot: "Link rot" happens when over time, URLs become inaccessible, either because the endpoint where the content was stored is no longer active, or the URL format itself changes. The graph below from an analysis by The New York Times of linkrot shows degradation over time of URLs.
Figure 3: Linkrot analysis over 1996-2019 by New York Times (source: Columbia Journalism Review / New York Times)_
The issues highlighted above a material difference to the longevity of Verifiable Credentials.
For example, a passport (which typically have a 5-10 year validity issued as a Verifiable Credential anchored to a DID (regardless of whether the DID was on-ledger or not) might stop working if the credential schema, visual presentation format, or other necessary resources were stored off-ledger on traditional centralised storage.
Despite these issues, many self-sovereign identity (SSI) implementations - even ones that use ledgers / distributed systems for DIDs - often utilise centralised storage. From the W3C Verifiable Credential Implementation Guide:
Example schema.org address with full URLs
Using traditional web endpoints to store resources (such as schemas) that are critical for a Verifiable Credential to function undermines the benefits that persistently-accessible Decentralized Identifiers offer.
We took the following design principles into consideration, along with an explanation of how we addressed them:
Built using existing, familiar DID Core Spec patterns: Wherever possible, our design attempts to utilise existing patterns and behaviours within the W3C DID Core specification (such as the use of DID URLs to identify resources), instead of trying to implement proprietary/custom approaches. We believe that similar designs could be adopted by other DID methods if they choose.
Protection against linkrot for long-term retrieval: Any Resource stored on-ledger is replicated across multiple nodes.
If any individual node or endpoint is down, lookup requests can be sent to any other node on the network.
In a catastrophic scenario where the network itself stops to exist, e.g., companies shutting down, getting acquired etc the on-ledger data can still be restored by digital archivists using ledger snapshots. A practical example of this is how Cosmos Hub makes historical chain archives available which can be restored. While this can be cumbersome, we wanted to design for this as a fail-safe.
Extensible by default: Our objective was to build a flexible design pattern that allowed developers to define and extend their own resource types. Trying to control what kinds of resources could be written to ledger would make the ledger-side logic complex. Instead, we opted for a design where the cheqd ledger acts agnostically to store resources, as long as correctly authorised, as a permanently-accessible endpoint.
Design for DID-spec "dumb" as well as DID-spec "smart" client applications: Many approaches in this space assume that client applications must be adept at parsing DIDDocs and resolving complex inter-DIDDoc relationships. We saw describing resources using DIDDocs as metadata about the resource which could be independently-parsed by "smart" client applications; while also providing a fallback approach for "dumb" client applications. We internally considered this as "What if an identity wallet understood how to parse JSON, but didn't understand the DID Core spec?"
Version controlled: The ability to evolve a resource over time is critical for identity use cases. As described above, examples of this include when identity document schemas change, logos evolve, etc. Current approaches (such as Hyperledger Indy CredDefs) deal with this by creating entirely new, unlinked resources. We designed to make it easy, using existing DID Core specification techniques, so that client applications could query "What was the version of a resource with this name on this date/time?"
Make re-use of resources simple: We liked the concept of Schema.org in the sense that it promotes a common, machine-readable specification. Therefore, our design allows for patterns where the controllers of a DID can reference resources created by other DID owners/controllers, e.g., referencing a pre-existing schema. Taking this line of thought further, it allows for an arbitrary depth of how resources can be nested, as long as they are discoverable/resolvable.
Notalltypes of resources should be stored on a ledger...but can be made discoverable through similar design patterns: Distributed ledgers are great for redundancy, but the cost of this duplication (in terms of storage required by node, block size, transaction finality speeds, etc) can be quite significant. For instance, a distributed ledger is probably not the best storage and retrieval mechanism for a video file (which can run into many GBs/TBs); or even a PDF larger than a few MBs. cheqd network restricts the block size for an individual block to ~200 KB. This can be updated via an on-ledger vote, but the trade-off of asking node operators to provision ever-expanding storage would be not ideal. Our design therefore restricts the file/payload size of on-ledger resources (in our case, ~190 KB - giving enough room for transaction data besides the resource itself), while allowing the same techniques below to be used for describing off-ledger resources. E.g., our first DID on cheqd network references a 7+ MB image accessible via IPFS. We recognise and accept that DID owners/creators may choose to use their own centralised/decentralised storage, and the design patterns described below accommodate that.
Resources on cheqd ledger are collated under Resource Collections, which are defined as a list of resources linked to and controlled using a DID Document ("DIDDoc").
Figure 4: Overview of Resource and Resource Collection creation editable version)
To create a new Resource, a client application first needs to create a DID (or use an existing not deactivated DID along with its associated DIDDoc.) This DID-Linked Resource is the lowest, direct level of create/update/deactivate operation control that exists.
Individual Resources are uniquely identified by a common Resource Name and common Resource Type that MUST remain consistent across versions. The specific version number of a Resource is described using the Resource ID, which is a Universally-Unique Identifier (UUID). Since UUIDs can be generated by any compatible software library, client applications are able to define this version number independent of the cheqd ledger. This same technique and rationale is described in ADR-001: cheqd DID method.
This allows a specific Resource version to be referenced in a Verifiable Credential, as well as allowing client applications to query historical/updated Resource versions along with metadata that describes how the Resource evolved within a Resource Collection.
Once a Resource has been created under a Resource Collection, the parent DIDDoc will automatically have an updated didDocumentMetadata section, including linkedResourceMetadata.
The syntax of the linked Resource metadata is as follows:
Importantly, we decided not to populate the actual resource data into the didDocumentMetadata, but instead, what we refer to as a Resource Preview which contains all the metadata about the associated resources.
Resource previews will aopear within DIDDocMetadata. These do not include the actual core data of the resource and only reference the metadata:
Resource Collection ID: (did:cheqd:...:) (supplied client-side)**
Resource ID: UUID ➝ specific to resource, also effectively a version number (supplied client-side)
Resource Name: String (e.g., CL-Schema1
(supplied client-side))
Resource Type (supplied client-side)
Resource Version (supplied client-side)
MediaType: (e.g. application/json
/image
/application/octet-stream
/text/plain
) (computed ledger-side)
Created: XMLDatetime (computed ledger-side)
Checksum: SHA-256 (computed ledger-side)
previousVersionId: empty string if first, otherwise ID as long as Name, ResourceType, and MimeType match previous version (computed ledger-side)
nextVersionId: empty string if first/latest, otherwise ID as long as Name, ResourceType, and MimeType match previous version (computed ledger-side)
Also known as: a list of alternative URIs that can be used to get the resource.
Example:
Once a Resource has been created under a Resource Collection, the linked DIDDoc can be updated to provide a link to access it in the service section.
The rationale for linking to Resources in this manner, instead of creating a new top-level section, are as follows:
Client applications capable of doing DID Resolution may have strong architectural assumptions to only expect the default DID Core specification sections in a response. We considered the possibility that such applications might (incorrectly) reject the entire DIDDoc as malformed, or crash in the process of trying to parse the DIDDoc.
On the other hand, the Service section in a DIDDoc is designed to be flexible and extensible by design. New DID Service types can be registered through DID Specification Registries by anyone. We suggest a new service type called LinkedResource should be used to reference any resource on cheqd within the service section. This is conceptually similar to the existing LinkedDomains.
In practice, we noted that client applications capable of DID Resolution will gracefully fail/ignore unknown Service types. Client applications that do understand a particular Service type can continue parsing/resolving content they are designed to handle.
DIDDocs can reference other DIDDocs, such as when the DID Controller in one DIDDoc is specified as a Verification Method in another DIDDoc. These links can be traversed using DID URL dereferencing.
Historical versions of Resources can always be accessed by traversing forwards/backwards in the Resource Collection by checking if a particular Service ID has old/new versions.
Multi-party control on Resource Collection updates is possible, since DIDs with multiple controllers specified in them already handle this scenario. In the normal process of updating a DIDDoc with multiple controllers, rules can be defined by client applications and/or the ledger on whether all controllers sign an update, or whether an m-of-n threshold needs to be applied. (Currently, the cheqd ledger requires all controllers to sign off on updates.)
Since the cheqd ledger does not co-relate the on-ledger cheqd/Cosmos accounts to keys that control DIDDocs, this provides another layer of access control by allowing DIDDoc controllers to rotate keys, if required.
Example of referencing a resource using the service section:
To create a new DID-Linked Resource, a client application first needs to create a DID (or use an existing DID along with its associated DIDDoc). This DID-Linked Resource is the lowest, direct level of create/update/deactivate operation control that exits.
Figure 5: Detailed sequence diagram of Resource creation on cheqd editable version.
Resources must be under the maximum block size restrictions to be able to fit into a transaction. Currently this is estimated to be ~190 KB on cheqd mainnet, based on the ~200 KB block size limit plus additional headroom for metadata that needs to be described in the ResourceHeader
.
Each request to create a Resource must provide the following parameters, supplied by the client application:
Resource Collection ID: (did:cheqd:...:) (supplied client-side)
Resource ID: UUID ➝ specific to resource, also effectively a version number (supplied client-side)
Resource Name: String (e.g., CL-Schema1
(supplied client-side))
Resource Type: (e.g JSONSchema 2020, supplied client-side. It is recommended that new Resource Types are included in the DID Spec Registries)
Resource Version: String (OPTIONAL). It is a human-readable semantic version for the Resource (e.g., 1.0.0
(supplied client-side))
alsoKnownAs: (OPTIONAL) a list of alternative URIs that can be used to get the resource.
In addition to the above client-provided parameters, the ledger-side code will populate the following additional header fields (for properly-authenticated requests):
MediaType: (e.g. application/json
/image
/application/octet-stream
/text/plain
) (computed ledger-side) This is based on the file extension of the associated resource file.
Created: XMLDatetime (computed ledger-side)
Checksum: SHA-256 (computed ledger-side)
previousVersionId: an empty string if first, otherwise ID as long as Name, ResourceType, and MimeType match previous version (computed ledger-side)
nextVersionId: an empty string if first/latest, otherwise ID as long as Name, ResourceType, and MimeType match previous version (computed ledger-side)
Example using the Veramo CLI:
This section will delineate between expected inputs in JSON and how the cheqd ledger stores the resource input in protobuf
collectionId
: (did:cheqd:...:)<identifier>
(supplied client-side) ➝ unique identifier from DIDDoc.
id
: UUID representing resource ID ➝ specific to resource, also effectively a version number (supplied client-side)
name
: String (e.g., CL-Schema1
(supplied client-side))
version
: String (OPTIONAL) ➝ a human-readable semantic version for the Resource (e.g., 1.0.0
(supplied client-side))
resourceType
: a string representing type of resource (supplied client-side). It is recommended that new Resource Types are included in the DID Spec Registries.
data
: Bytes representing a user data (supplied client-side)
alsoKnownAs
(supplied client-side) a list of alternative URIs for the SAME Resource
signInputs
: Signatures of the corresponding DID Document's controller(s) (supplied client-side).
Resource
is stored on-ledger in protobuf. This is converted back to JSON client side.
Collection ID
: String - an identifier of linked DIDDoc.
Returns collection of resources
created by the specific collection ID
. This is converted to JSON on the client side.
Collection ID
: String - an identifier of linked DIDDoc
ID
: String - unique resource id
Returns Resource
with a given collection ID
and ID
. This is converted to JSON client side.
Collection ID
: String - an identifier of linked DIDDoc
ID
: String - unique resource id
Returns resource's metadata
with a given collection ID
and ID
.
resources-metadata:<collection-id>:<resource-id>
➝ Metadata
<collection-id>
is the last part of DID. It can be UUID, Indy-style or whatever is allowed by ledger. It allows us to evolve over time more easily.
<resource-id>
is a unique resource identifier on UUID format
resources-data:<collection-id>:<resource-id>
➝ Data
<collection-id>
is the last part of DID. It can be UUID, Indy-style or whatever is allowed by ledger. It allows us to evolve over time more easily.
<resource-id>
is a unique resource identifier on UUID format
Input:
Output:
Processing logic:
Check that associated DIDDoc exists;
Authenticate request the same way as DIDDoc creation and updating;
Validate properties;
Validate that ID is unique;
Set created date time;
Set previousVersion
and nextVersion
if this is a new version (a resource with the same collection-id, resource-name and resource-type exists);
Compute checksum;
Persist the resource in state;
cheqd Cosmos CLI Example:
payload-file
: path to the payload file:
resource-data-file
: path to the resource file (e.g.: /path/to/resource.jpeg
)
Processing logic:
Retrieves the whole resource collection for the specified DID;
Returns only resource headers (without data
field);
cheqd Cosmos CLI Example:
Input:
Output:
Processing logic:
Retrieves a specific resource by Collection-ID and resource ID;
cheqd Cosmos CLI Example:
Processing logic:
Retrieves all resource versions by collection id, resource id
Returns a resource's metadata from a collection with a given collection_id and id
cheqd Cosmos CLI Example:
Resource are immutable, but it is possible to create new versions of it under a new identifier(id
field). When creating a resource whose fields collection_id
, name
and resource_type
match an existing resource:
The latest version of the current resource will be added with a link to the new one. That is, field next_version_id
will contain the new resource identifier.
A new resource with data from the transaction will be created with the previous version resource id in field previousVersionId
.
Example:
Step 1. Resource exists in the ledger:
Step 2. Client send request for creating a new resource with a transaction MsgCreateResource
Step 3. After the transaction is applied
Immutability:
Resources on-ledger are immutable, so can't be destroyed;
Limitations
Resource size is now limited by maximum tx/block size;
Limitations
Introduce module level resource size limit that can be changed by voting
A new module will be created: resource
.
It will have cheqd
module as a dependency.
Will be used for DIDs existence checks.
Will be used for authentication
W3C Verifiable Credentials official specification
Cosmos blockchain framework official project website
cosmos-sdk
GitHub repository (documentation)
Hyperledger Indy official project background on Hyperledger Foundation wiki
indy-node
GitHub repository: Server-side blockchain node for Indy (documentation)
indy-plenum
GitHub repository: Plenum Byzantine Fault Tolerant consensus protocol; used by indy-node
(documentation)
Indy DID method (did:indy
)
Hyperledger Aries official project background on Hyperledger Foundation wiki
aries
GitHub repository: Provides links to implementations in various programming languages
aries-rfcs
GitHub repository: Contains Requests for Comment (RFCs) that define the Aries protocol behaviour.
This ADR defines the cheqd DID method and describes the identity entities, queries, and transaction types for the cheqd network: a purpose-built self-sovereign identity (SSI) network based on the Cosmos blockchain framework.
Decentralized identifiers (DIDs) are a type of identifier that enables verifiable, decentralized digital identity. A DID refers to any subject (for example, a person, organization, thing, data model, abstract entity, and so on) as determined by the controller of the DID.
Hyperledger Indy is a verifiable data registry (VDR) built for DIDs with a strong focus on privacy-preserving techniques. It is one of the most widely-adopted SSI blockchain ledgers. Most notably, Indy is used by the Sovrin Network.
The Sovrin Foundation initiated a project called libsovtoken
in 2018 to create a native token for Hyperledger Indy. libsovtoken
was intended to be a payment handler library that could work with libindy
and be merged upstream. This native token would allow interactions on Hyperledger Indy networks (such as Sovrin) to be paid for using tokens.
Due to challenges the project ran into, the libsovtoken
codebase saw its last official release in August 2019.
The cheqd network aims to support similar use cases for SSI as seen on Hyperledger Indy networks, with a similar focus on privacy-resspecting techniques.
Since the core of Hyperledger Indy's architecture was designed before the W3C DID specification started to be defined, the Indy DID Method (did:indy
) has aspects that are not fully-compliant with latest specifications.
However, the rationale for why the cheqd team chose the Cosmos blockchain framework instead of Hyperledger Indy were primarily down to the following reasons:
Hyperledger Indy is a permissioned ledger: Indy networks are permissioned networks where the ability to have write capability is restricted to a limited number of nodes. Governance of such a permissioned network is therefore also not decentralised.
Limitations of Hyperledger Indy's consensus mechanism: Linked to the permissioned nature of Indy are the drawbacks of its Plenum Byzantine Fault Tolerant (BFT) consensus mechanism, which effectively limits the number of nodes with write capability to approximately 25 nodes. This limit is due to limited transactions per second (TPS) for an Indy network with a large number of nodes, rather than a hard cap implemented in the consensus protocol.
Wider ecosystem for token functionality outside of Hyperledger Indy: Due to its origins as an identity-specific ledger, Indy does not have a fully-featured token implementation with sophisticated capabilities. Moreover, this also impacts end-user options for ecosystem services such as token wallets, cryptocurrency exchanges, custodianship services etc that would be necessary to make a viable, enterprise-ready SSI ledger with token functionality.
By selecting the Cosmos blockchain framework, the maintainers of the cheqd project aim to address the limitations of Hyperledger Indy outlined above. However, with an eye towards interoperability, the cheqd project aims to use Hyperledger Aries for ledger-related peer-to-peer interactions.
Our aim is to support the functionality enabled by identity-domain transactions in by Hyperledger Indy into cheqd-node
. This will partly enable the goal of allowing use cases of existing SSI networks on Hyperledger Indy to be supported by the cheqd network.
The following identity-domain transactions from Indy were considered:
NYM
: Equivalent to "DIDs" on other networks
ATTRIB
: Payload for DID Document generation
SCHEMA
: Schema used by a credential
CRED_DEF
: Credential definition by an issuer for a particular schema
REVOC_REG_DEF
: Credential revocation registry definition
REVOC_REG_ENTRY
: Credential revocation registry entry
Revocation registries for credentials are not covered under the scope of this ADR. This topic is discussed separately in a future ADR as there is ongoing research by the cheqd project on how to improve the privacy and scalability of credential revocations.
Schemas and Credential Definitions are also not covered under the scope of this ADR. Their implementation is covered in ADR 002: DID-Linked Resources.
did:cheqd
)The method-name
for the cheqd DID Method will be identified by the string cheqd
.
A DID that uses the cheqd DID method MUST begin with the prefix did:cheqd
. This prefix string MUST be in lowercase. The remainder of the DID, after the prefix, is as follows:
The cheqd DID method's method-specific identifier (method-specific-id
) is made up of the namespace
component. The namespace
is defined as a string that identifies the cheqd network (e.g., "mainnet", "testnet") where the DID reference is stored. Different cheqd networks may be differentiated based on whether they are production vs non-production, governance frameworks in use, participants involved in running nodes, etc.
The namespace
associated with a certain network/ledger is stored in the genesis file on the node and cannot be changed by validators vote. A namespace is optional and can be omitted.
A did:cheqd
DID must be unique. The syntax of the unique-id
component may be defined as a Universally Unique Identifier (UUID) generated by the creator(s) of the DID. UUIDs are the preferred unique identifier format for cheqd network.
Usage of UUID-style identifiers significantly simplifies the generation and implementation of unique identifiers, with extremely-low probability of a collission where the same UUID is generated independently.
Any client application can generate these UUIDs using their own preferred implementation in any programming language, as opposed to the method-specific logic required for Indy-style DID identifiers.
Alternatively, the unique-id
may also be generated similar to the did:indy method
from the initial public key of the DID (e.g., base58 encoding of the first 16 bytes of the SHA256 of the first Verification Method Ed25519
public key). This unique-id
format is referred to as the "Indy-style" unique identifier in our documentation.
Support for Indy-style unique identifiers makes compatibility with Indy-based client SDKs, such as those based on Hyperledger Aries.
If no namespace
is specified, it assumed to be default namespace
for the network/ledger the request is targetted at. This will generally be mainnet
for the primary production cheqd network.
did:cheqd
methodThe cheqd DID method ABNF to conform with DID syntax guidelines is as follows:
Note: The
*id-char unique-id
must be 16 bytes of Indy-style base58 encoded identifier.
Where the formal definition of UUIDs is represented using this ABNF:
did:cheqd
identifiersA DID written to the cheqd "mainnet" ledger namespace
with an Indy-style identifier:
A DID written to the cheqd "testnet" ledger namespace
with an Indy-style identifier:
An Indy-style DID where no namespace is defined, where the namespace
would default to the one defined on ledger where it's published (typically, mainnet
):
A UUID-style DID on cheqd "mainnet" namespace
:
A UUID-style DID where no namespace is defined, where the namespace
would default to the one defined on ledger where it's published (typically, mainnet
):
A DID Document ("DIDDoc") associated with a cheqd DID is a set of data describing a DID subject. The representation of a DIDDoc when requested for production from a DID on cheqd networks MUST meet the DID Core specifications.
The following elements are needed for a W3C specification compliant DIDDoc representation:
@context
(optional): A list of strings with links or JSONs for describing specifications that this DID Document is following to.
id
: Target DID with cheqd DID Method prefix did:cheqd:<namespace>:
and a unique-id
identifier.
controller
(optional): A list of fully qualified DID strings or one string. Contains one or more DIDs who can update this DIDdoc. All DIDs must exist.
verificationMethod
(optional): A list of Verification Methods
authentication
(optional): A list of strings with key aliases or IDs
assertionMethod
(optional): A list of strings with key aliases or IDs
capabilityInvocation
(optional): A list of strings with key aliases or IDs
capabilityDelegation
(optional): A list of strings with key aliases or IDs
keyAgreement
(optional): A list of strings with key aliases or IDs
service
(optional): A set of Service Endpoint maps
alsoKnownAs
(optional): A list of strings. A DID subject can have multiple identifiers for different purposes, or at different times. The assertion that two or more DIDs refer to the same DID subject can be made using the alsoKnownAs
property.
"diddoc:<id>" -> {DIDDoc, DidDocumentMetadata, txHash, txTimestamp }
didDocumentMetadata
is created by the node after transaction ordering and before adding it to a state.
Each DID Document MUST have a metadata section when a representation is produced. It can have the following properties:
created
(string): Formatted as an XML Datetime normalized to UTC 00:00:00 and without sub-second decimal precision, e.g., 2020-12-20T19:17:47Z
.
updated
(string): The value of the property MUST follow the same formatting rules as the created property. The updated
field is null
if an Update operation has never been performed on the DID document. If an updated property exists, it can be the same value as the created property when the difference between the two timestamps is less than one second.
deactivated
(string): If DID has been deactivated, DID document metadata MUST include this property with the boolean value true
. By default this is set to false
.
versionId
(string): A UUID string that represents the version identifier of the DID Document.
resources
(list of resources metadata referred to as Resource previews)| optional. Cannot be changed by CreateDID or UpdateDID transactions. cheqd ledger stores only the resource identifiers in the DID Doc metadata. The remainder of the resources' metadata is added when a DID is resolved.
previousVersionId
(string): A UUID string that represents the version identifier of the previous version of the DID Document. The previousVersionId
field is an empty string if an Update operation has never been performed on the DID document
nextVersionId
(string): A UUID string that represents the version identifier of the next version of the DID Document. The nextVersionId
field is an empty string if an Update operation has never been performed on the DID document
Verification methods are used to define how to authenticate / authorise interactions with a DID subject or delegates. Verification method is an OPTIONAL property.
id
(string): A string with format did:cheqd:<namespace>#<key-alias>
controller
: A string with fully qualified DID. DID must exist.
type
(string)
publicKeyJwk
(map[string,string]
, optional): A map representing a JSON Web Key that conforms to RFC7517. See definition of publicKeyJwk
for additional constraints.
publicKeyBase58
(optional): A base58-encoded string.
publicKeyMultibase
(optional): A base58-encoded string that conforms to a MULTIBASE encoded public key.
Note: A single verification method entry cannot contain more than one of publicKeyJwk
, publicKeyBase58
and publicKeyMultibase
, but must contain at least one of them.
Services can be defined in a DIDDoc to express means of communicating with the DID subject or associated entities.
id
(string): The value of the id
property for a Service MUST be a URI conforming to RFC3986. A conforming producer MUST NOT produce multiple service entries with the same ID. A conforming consumer MUST produce an error if it detects multiple service entries with the same ID. It has a follow formats: <DIDDoc-id>#<service-alias>
or #<service-alias>
.
type
(string): The service type and its associated properties SHOULD be registered in the DID Specification Registries
Since cheqd-node is built using the Cosmos SDK, the data stored directly on ledger is formatted in Protobuf and is transformed back into compliant JSON client-side on request.
Example of how cheqd-node stores verification_method
id
(string): A string with format did:cheqd:<namespace>#<key-alias>
controller
: A string with fully qualified DID. DID must exist.
verification_method_type
(string): A string that represents type of verification method. Supported: Ed25519VerificationKey2018
, Ed25519VerificationKey2020
, JsonWebKey2020
.
verification_material
(string): It represents the exact decoded string value of public key for the verification method. Supported types of public key representations are: publicKeyBase58
, publicKeyMultibase
, publicJwk
.
This operation updates the DID Document associated with an existing DID of type did:cheqd:<namespace>
.
signatures
: UpdateDidRequest
should be signed by all controller
private keys. This field contains a dict
structure with the key URI from DIDDoc.authentication
, as well as signature values.
id
: Fully qualified DID of type did:cheqd:<namespace>
.
versionId
: Transaction hash of the previous DIDDoc version. This is necessary to provide replay protection. The previous DIDDoc versionId
can fetched using a get DID query.
controller, verificationMethod, authentication, assertionMethod, capabilityInvocation, capabilityDelegation, keyAgreement, service, alsoKnownAs, context
: Optional parameters in accordance with DID Core specification properties.
This operation deactivates the DID for a given did:cheqd:<namespace>
. Once deactivated, a DID cannot be re-activated or any DIDDoc update operations carried out.
id
: Fully qualified DID of type did:cheqd:<namespace>
.
signatures
: DeactivateDidDocRequest
should be signed by all controller
private keys. This field contains controller key URIs and signature values.
DIDDocs associated with a DID of type did:cheqd:<namespace>
can be resolved using the GetDidDoc
query to fetch a response from the ledger. The response contains:
did_doc
: DIDDoc associated with the specified DID in a W3C specification compliant DIDDoc structure.
metadata
: Contains the MUST have DIDDoc metadata associated with a DIDDOc.
DID resolution requests can be sent to the Tendermint RPC interface for a node by passing the fully-qualified DID.
The response is returned as a Protobuf, which can be converted to JSON client-side.
For creating a new DID or update the DIDDoc associated with an existing DID, the requested should be signed by all controller
signatures.
To update a DIDDoc fragment without a controller
(any field except VerificationMethods
), the request MUST be signed by the DID's controller
(s).
To update a DIDDoc fragment that has its own controller
(s), the request MUST be signed by the DID's controller
(s) and the DIDDoc fragment's controller
(s).
Changing the controller
(s) associated with a DID requires a list of signatures as before for changing any field.
One of the key design decisions in the cheqd DID method is to use separate sets of key pairs for Cosmos / node layer transactions and identity payloads.
Keypairs and accounts on the Cosmos node layer are public, and can be crawled/explored by inspecting transactions on a node or through a block explorer. This therefore poses a privacy risk through correlation, if the identity payloads were signed using the same keys.
By splitting the keys/accounts for the two layers, we account for identity payloads being signed using keys that can be kept off-ledger.
This also allows for uses cases where the key owners/controllers at the identity layer are different than the key/account owners at the Cosmos node layer. In essence, that this allows is it removes the need for a DID's controllers to also have an account on the cheqd network ledger.
Further discussion on how these boundaries are separated in implementation, with one specific implementation library, is described in cheqd node ADR 003: Command Line Interface (CLI) tools
NYM
transactions to DID
transactionsNYM is the term used by Hyperledger Indy for DIDs. cheqd uses the term DID
instead of NYM
in transactions, which should make it easier to understand the context of a transaction easier by bringing it closer to W3C DID terminology used by the rest of the SSI ecosystem.
role
field from DID transactionsHyperledger Indy is a public-permissioned distributed ledger and therefore use the role
field to distinguish transactions from different types of nodes. As cheqd networks are public-permissionless, the role
scope has been removed.
ATTRIB
transactions droppedATTRIB
was originally used in Hyperledger Indy to add document content similar to DID Documents (DIDDocs). The cheqd DID method replaces this by implementing DIDDocs for most transaction types.
To support the cheqd Resource Module, cheqd ledger includes a reference to resource previews within the DIDDoc metadata.
Identity entities and transactions for the cheqd network may differ in name from those in Hyperledger Indy, but aim enable equivalent support for privacy-respecting SSI use cases.
The differences stem primarily from aiming to achieve better compliance with the W3C DID Core specification and architectural differences between Hyperledger Indy and Cosmos SDK (used to build cheqd-node
).
With better compliance against the DID Core specification, the goal of the cheqd DID method is to maximise interoperability with compatible third-party software librarires, tools and projects in the SSI ecosystem.
The cheqd DID method does not aim to be 1:1 compatible in API methods with did:indy
. It makes opinionated choices to not implement certain transaction types, which in our analysis have been superseded by new developments in the W3C DID Core specification.
Support for Indy-style DID unique identifiers is intended to provide a compatibility mode with existing Indy-based client applications.
cheqd-node
release v0.1.19 and earlier had a transaction type called NYM
which would allow writing/reading a unique identifier on ledger. However, this NYM
state was not fully defined as a DID method and did not store DID Documents associated with a DID. This NYM
transaction type is deprecated and the data written to cheqd testnet with legacy states will not be retained.
Design decisions defined in this ADR aim to make the cheqd DID method compliant with the W3C DID Core specification.
Usage of UUID-style identifiers significantly simplifies the generation and implementation of unique identifiers, since any client application can generate these UUIDs using their own preferred implementation in any programming language, as opposed to the method-specific logic required for Indy-style DID identifiers.
As the client/peer-to-peer exchange layer (at least in the implementation provided by VDR Tools SDK) is built on a library that supports Hyperledger Aries, extending Aries implementations to other W3C compliant DID methods should become simpler for the SSI ecosystem.
Trying to maintain backwards-compatibility with Hyperledger Indy APIs means some functionality will not be available for legacy client applications which cannot support non-Indy DID Document elements, e.g., multiple verification methods in the same DIDDoc.
DID transaction operations at the moment must be assembled using a client-side library with DID specification identity standards support, and then wrapped up inside a Cosmos transaction that is sent to the Tendermint RPC or Cosmos SDK gRPC interface. We aim to build client apps/SDKs that can be used by developers to make the process of interacting with the ledger simpler.
Hyperledger Indy official project background on Hyperledger Foundation wiki
indy-node
GitHub repository: Server-side blockchain node for Indy (documentation)
indy-plenum
GitHub repository: Plenum Byzantine Fault Tolerant consensus protocol; used by indy-node
(documentation)
Indy DID method (did:indy
)
Hyperledger Aries official project background on Hyperledger Foundation wiki
aries
GitHub repository: Provides links to implementations in various programming languages
aries-rfcs
GitHub repository: Contains Requests for Comment (RFCs) that define the Aries protocol behaviour
Cosmos blockchain framework official project website
cosmos-sdk
GitHub repository (documentation)
libsovtoken
: Sovrin Network token library
Rules and architecture of DID resolver to handle DID URL queries, paths and fragments
When you resolve a DID you get a DID Document.
When you dereference a DID, you may get the DID Document, a portion of a DID document, or the resource at the end of a service endpoint (if the DID contains a service component).
You can only resolve a DID to return a DID document, and you can only dereference a DID reference to return whatever resource is referenced.
Normal dereferencing can be conceived in two steps:
Endpoint: /1.0/identifiers/<did>
Endpoint: /1.0/identifiers/<did>#example
If a DID URL contains a fragment #
. the logic is similar to the general web browser logic where #
symbols can be used for linking to the particular document section.
For the DIDDoc, you can equate a specific section within a webpage to a specific section inside the DIDDoc, for example verificationMethod
. To resolve to a particular section, we need to use a request which specifies the section itself.
Take the example excerpt from a DIDDoc below:
Here, to dereference to this verificationMethod, we need to pass key-1
as a fragment parameter:
Example:
All the queries can be divided into 2 main groups:
Queries for fetching a resource referenced within the DIDDoc body.
Queries for fetching an external resource associated with the DIDDoc, referenced within the DIDDoc metadata.
Note: in instances where there is no versionId
or versionTime
parameters specfied, we have set the default dereferencing rules to fetch the latest version of a resource, if it is uniquely identified by the parameters in the query.
Common parameter validation
parameters metadata
and resourceMetadata
are designed to be a bool variables, like true
or false
.
parameters versionTime
and resourceVersionTime
are designed as string representation of time and can be in 2 formats, RFC3339 and RFC3339Nano. Examples are ("2006-01-02T15:04:05Z07:00" and "2006-01-02T15:04:05.999999999Z07:00")
Here we have an ability to specify different parameters to filter to particular parts of a DIDDoc.
All queries can be chained together to make very specific requests using &
within the request format. This makes query parameters particularly powerful because they can be used to fetch specific parts of a DID Document or specific resources associated with a DID Document.
For example, the example below queries a particular service endpoint associated with a particular DIDDoc version.
VersionId
If the user knows the particular versionId
he could ask about it by adding the query versionId=<uuid of DIDDoc version>
It can be combined with any other queries for building more complex requests. linkedResourceMetadata
will contain only resources which were active for this version. In other words, resource which were created before the next version in terms of timeline.
Example:
VersionTime
Example:
TransformKeys
The transformKeys parameter changes the representation of verificationMethod
section in DIDDoc. It allows you to change publicKey
representation into the one of next variants. To do so, just pass the parameter transfromKeys
with one of the following values:
Ed25519VerificationKey2020
Ed25519VerificationKey2018
JsonWebKey2020
Example:
Service
Service - is a DIDDoc section which includes serviceEndpoint
field with URL inside. As a result, pair service=<service-id>
will redirect to the serviceEndpoint
value.
Example:
RelativeRef
Example:
Metadata
By default, without metadata=true
pair DID-resolver
returns only particular DIDDoc. If you want to specifically fetch only the metadata for a DIDDoc, you can pass an additional query. It can be helpful in the instance, for example, where you want to get all the resources which were created for the exact versionId
or before versionTime
.
Example:
NotFoundError
raises if there is now resource/metadata with requested set of parameters or the result is ambiguous
RepresentationNotSupported
raises if there are:
unsupported query parameters
value for parameters are empty
unsupported transformKeys
value
metadata
or resourceMetadata
parameter has value not only true
or false
relativeRef
parameter is used without service
one. They must be used only together
InvalidDidUrl
raises if there are:
versionId
or resourceId
are not valid UUID strings
versionTime
or resourceVersionTime
are not in RFC3339 or RFC3339Nano formats.
resourceVersionTime
is used without any other resource parameters.
The diagram below shows how a DID-Linked Resource sits beneath a DID and can be fetched using a DID URL.
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.
Endpoint: /1.0/identifiers/<did>/resources/<resource_id>
Request HTTP headers
Accept
should allow */*
or match the mediaType
of the Resource
Response HTTP headers
Status code 200 OK
Content-Type
should be set to mediaType>
of the Resource
Content-Encoding
should be set to a valid content compression method in Accept-Encoding
and response compressed accordingly
Response HTTP body
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.0/identifiers/<did>/resources/
Status code 301
Redirect to /resources/all
/1.0/identifiers/<did>/resources
Throw an invalidDidUrl
error
Here we have an ability to specify different parameters to filter to particular DID-Linked Resources.
Like with DIDDoc query parameters, these can all be chained together to create complex requests for specific DID-Linked Resources at particular points in time or associated with particular DIDDoc versions.
It is important to understand how our resolver logically handles more complex dereferencing requests. We have set some baseline defaults and rules to ensure a logical and consistent experience for clients who use our resolver.
Ambiguity generally throws an error
If the request specifies a parameter where there are multiple potential results, such as where the DID has two resources of the same resourceType
('String') but resourceName
is not the same, an error will be thrown because there is not enough information to discern which resource is being requested.
Multiple versions of the same resource
If there are multiple resources with the same resourceType
and resourceName
but with different versionIds,
and there is no parameter specified to fetch a particular version, the resolver will fetch the latest resource by default.
This is because the query is not ambiguous in terms of discerning which set of resources to dereference to, but is only ambiguous in terms of which version of that resource to fetch.
Ambiguity + resourceMetadata=true
If there is an ambiguous query, such as where there are two resources with the same name but different types, AND there is a resourceMetadata=true parameter, resource data pertaining to all the resources which could potentially be seen as being ambiguous will be returned.
For example, in the below example, there are multiple resources with the resourceType= string
, but with different resourceName
parameters:
ResourceId
ResourceId
parameter can be used for filtering a particular resource version by specifically identifying its unique ID.
Example:
ResourceCollectionId
resourceCollectionId
parameter filters all the resource by collectionId
field. By default cause we are asking for resources for a particular DID it already includes all the resource with the same collectionId
and this parameter can used mostly as sanity check. Without resourceMetadata=true
parameter will return the latest created resource if there is only one resource or an unambiguous resource.
Example:
ResourceType
This parameter is also just a filter by Type
field through resources. But there is a corner case if the user asks about exact resource (exact data). If after applying all the parameters in request several resources are left with the same Name
- the latest one will be responded. Otherwise - error NotFoundError
will be raised.
Example:
ResourceName
Example:
ResourceVersion
This parameter filters by Version
field. We introduced it with latest network upgrade and can be optionally set to identify a version of a resource with a particular string.
Example:
ResourceVersionTime
Important: This parameter must always be accompanied by another resource query qualifier.
The main goal here is to get the nearest resource for resourceVersionTime
value. "Nearest" means that if we are asking for time between resource1
and resource2
were created - resource1
will be returned. In case if requested resourceVersionTime
is before the first resource created - NotFoundError
will be returned. The most useful use-case here is checking that some "Credential" (driver's license) was active at resourceVersionTime
(was not revoked from Revocation Registry for example).
Example:
Checksum
It just checks that checksum
is the same as resource's metadata and also can used as a sanity check. For example, if the user knows what is exact checksum then it may be checked before actual downloading.
Example:
ResourceMetadata
resourceMetadata=true
resourceMetadata=false
Unused
Example:
If the output of the DID URL dereferencing function contains the
didDocumentStream
:
If the value of the
Accept
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 be
200
.The HTTP response MUST contain a
Content-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 the
didDocumentStream
, 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.
Accept
header is application/did+ld+json
OR blank OR */*
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 is application/did+ld+json
Accept
request HTTP header contains application/ld+json;profile="https://w3id.org/did-resolution"
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 is application/ld+json;profile="https://w3id.org/did-resolution
Accept
request HTTP header contains application/did+json
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 is application/did+json
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
: null
didDocumentMetadata
: []
The DID dereferencing output should always conform to the following format: ( dereferencingMetadata, contentStream, contentMetadata )
dereferencingMetadata
contains "error" : "<Error message>"
contentStream
: null
contentMetadata
: []
The DID create/update/deactivate functions raise architectural questions around key management, since they typically involve the generation and use of private keys and other secrets.
The DID registrar can operate in the following modes:
Internal Secret Mode
External Secret Mode
In this mode, the DID Registrar is responsible for generating the DID controller cryptogprahic keys used in DID operations. Therefore, a DID Registrar used in this mode is considered a highly trusted component which should be fully under the control of a DID controller. If it is operated as a remotely hosted service, secure connection protocols such as TLS, DIDComm, etc. MUST be used.
In this mode, the DID Registrar does not itself have access to the cryptographic keys used in DID operations, but it has a way of accessing an external wallet in order to perform cryptographic operations such as generating signatures.
In this mode, the DID Registrar does not itself have access to the cryptographic keys used in DID operations, but it will ask the client to perform operations such as generating keys and signatures in a separate action from using the Registrar.
In the above diagram you can see the following steps
Request Operation
The client requests a DID operation providing the required fields
Return JobId and Serialized Payload
The registrar responds with a JobId and a base64 encoded serialized payload
The serialized payload should be signed by all the verificationMethods belonging to the controllers of the DID Document
Submit JobId and SigningResponse
Submit the JobId and the SigningResponse's to the same api
Validate signature and Complete Operation
The registrar validates the signature for the provided DID Document
Submits the DID operation request to the network
The payload can also be created using our helper endpoint /1.0/did-document
, which requires the following options to construct the DID Document payload
Verification Method Type
Method Specific Algorithm
Network
PublicKey Hex encoded string
Endpoint: /1.0/update
Only setDidDocument operation is supported in the cheqd-did-registrar. To update a DID Document, fetch the body of the DID Document you want to change from the DID Resolver, make the relevant updates and pass it to the request operation.
Endpoint: /1.0/deactivate
Endpoint: /1.0/{:did}/create-resource
Provide an existing DID as the path parameter, and the request body with resource name, type and base64 encoded data
Category | Status |
---|---|
Category | Status |
---|---|
Category | Status |
---|---|
Category | Status |
---|
The defines how DIDs are created and read from ledger. According to the , DID methods are expected to provide .
The is designed to implement the for method.
Part of the is the notion of DID URL Dereferencing whereby a DID URL identifies either a primary or secondary resource which is returned directly.
A is resolved to a ;
A resource within / associated with the is identified, based on the portion of the that follows the (path, query, fragment as defined by the ABNF in .).
Current implementation returns here the full DIDDoc as in . By default, in didDocumentMetadata
section will be the metadata for the latest version of DIDDoc and the list of all resources.
Parameter | Type | Description |
---|
The user could ask resolver for the nearest DIDDoc which was created/updated before versionTime
value. The logic about linkedResourceMetadata
is the same. Here, resolver figure out what the versionId
is the nearest one for versionTime
and shows all the active resources. If you want to file resources also, you can use parameter and combine the query with versionTime
.
relativeRef
is an additional part for serviceEndpoint
and can be used only with parameter. It combines serviceEndpoint
URL with relativeRef
value and does the redirect to the URL.
cheqd's introduces a new concept to the SSI ecosystem - namely, resources that can be associated with a DID and fetched using existing DID URL dereferencing patterns.
Requests to fetch on-ledger cheqd Resources are considered as a DID URL Dereferencing scenario it uses to lead to a Resource object, rather than a DIDDoc.
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 phase by client applications. In this case, only the HTTP Response headers are returned without the body.
may be allowed compression methods (e.g., gzip
, compress
, etc.)
should be set to the Resource size, in decimal bytes
Parameter | Type | Description |
---|
Behavior of this parameter is similar with one. If there is no ambiguous resource, it will be fetched. Otherwise greater specifity is required.
This parameter a kind of modifier which works in the same manner as but applies to resources. It allows to get only Metadata information about resource(s) without downloading. Also it changes the flow for resourceType
and resourceName
parameters and general meaning of this parameter - just filter. So, here it allows to create a chain of parameters and apply all of them to the resourceCollection and get only interested resource metadata. Possible variants:
The cheqd DID Resolver complies with the rules and algorithm defined in . This section clarifies and expands some descriptions specific to cheqd.
The section states that:
The DID Resolution specification should be handled. The cheqd DID Resolver aims to implement all of these scenarios, with the encountered.
Category | Status |
---|
The defines how DIDs are created and read from ledger. According to the , DID methods are expected to provide .
The is designed to implement the for method to enable create/update/deactivate DID operations easily.
The also supports the creation of for example, schemas, credential definitions, status lists, trust registries and logos.
Client Managed Secret Mode
The cheqd DID Registrar only supports the , considering the security and scalability of the registrar. The workflow for all the operations follows the protocol below:
Endpoint: /1.0/create
Provide a DID Document payload according to the in the request body.
recommendation
specification
guide
Authors
Alex Tweeddale, Renata Toktar, Ankur Banerjee, Andrew Nikitin
ADR Stage
PROPOSED
Implementation Status
In Progress
Start Date
2022-02-22
Last Updated
2023-04-25
Authors
Ankur Banerjee, Alexandr Kolesov, Alex Tweeddale, Renata Toktar
ADR Stage
ACCEPTED
Implementation Status
Implemented
Start Date
2021-09-23
Last Updated
2023-02-06
Authors
Ankur Banerjee, Alexandr Kolesov, Alex Tweeddale, Brent Zundel, Renata Toktar, Richard Esplin
ADR Stage
ACCEPTED
Implementation Status
Implemented
Start Date
2021-09-23
Last Updated
2023-02-06
Authors | Alex Tweeddale, Abdulla Ashurov, Andrew Nikitin |
ADR Stage | PROPOSED |
Implementation Status | DRAFT |
Start Date | 2023-06-05 |
Last Updated | 2023-06-06 |
Authors | Daev Mithran |
ADR Stage | ACCEPTED |
Implementation Status | In Progress |
Start Date | 2023-01-30 |
Last Updated | 2023-01-30 |
| Used to filter to a specific version id of the DIDDoc |
| Used to filter to a specific version of the DIDDoc at a point in time |
| Used to navigate to a serviceEndpoint of a DIDDoc, using the serviceId as a query parameter |
| Used to transform the verificationMethod key material into a different key type, based on the same cryptographic curve |
| Used to fetch a specific part of a serviceEndpoint, such as a particular heading on a website |
| Used to fetch the metadata associated with a particular DIDDoc |
| The unique identifier of a particular DID-Linked Resource |
| Can be used to query all resources associated with a DID if combined with resourceMetadata=true |
| The specific name of a DID-Linked Resource |
| The specific type of a DID-Linked Resource |
| Used to fetch a version of a resource at a specific point in time |
| Used to specify a particular resource checksum to demonstrate it is untampered |
| Used to fetch metadata related to a specific resource or group of resources |
A that conforms to a method specific unique identifier format.
A serialized as an normalized to UTC 00:00:00 and without sub-second decimal precision.
Using a from the serviceid
property within the DID document, this will dereference to the associated serviceEndpoint
A that identifies the intended key type
A that identifies a particular "secondary" resource appended to the serviceEndpoint
A that conforms to a method specific unique identifier format.
A that conforms to a method specific unique identifier format.
A
A
A serialized as an normalized to UTC 00:00:00 and without sub-second decimal precision.
A