🟢ADR 002: DID-Linked Resources
Last updated
Last updated
Category | Status |
---|---|
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.
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