Orb is a decentralized identifier (DID) method based on a federated and replicated Verifiable Data Registry (VDR). The decentralized network consists of Orb servers that write, monitor, witness, and propagate batches of DID operations. The batches form a graph that is propagated and replicated between the servers as content-addressable objects. These content-addressable objects can be located via both domain and distributed hash table (DHT) mechanisms. Each Orb witness server observes a subset of batches in the graph and includes them in their ledgers (as append-only Merkle Tree logs). The servers coordinate by propagating batches of DID operations and by monitoring the applicable witness servers' ledgers. The Orb servers form a decentralized network without reliance on a common blockchain for coordination.

This document specifies the Orb DID Method along with a data model and rules for representing a graph of batches, the Orb server API, a registry of protocol versions and parameters, an [[[ACTIVITYSTREAMS-CORE]]] vocabulary and an [[[ACTIVITYPUB]]] profile for propagating batches, a [[[RFC7033]]] profile for discovering endpoints, a [[[SIDETREE]]] protocol profile for encoding DID operations, and a [[[RFC6962]]] profile for witness ledgers.

Introduction

The Orb DID method enables users or entities to create self certifying decentralized identifiers (DIDs) that are propagated across a decentralized network without reliance on a common blockchain for coordination. Decentralized identifiers [[DID-CORE]] are a fundamental building block to enable persons and entities to prove that they control an identifier that is attached to digital objects such as Verifiable Credentials [[VC-DATA-MODEL]].

Systems that rely on self certifying DIDs need a mechanism to propagate the ordered updates to DID documents from the server writing operations to their DID resolver. As an example, operations can be distributed via a content-addressable network (such as IPFS) and announced on a public blockchain (such as Bitcoin) or a permissioned blockchain (such as Hyperledger Fabric). This DID method, instead, makes use of decentralized federation protocols to propagate announcements and replicate content in a gossip manner. By using a decentralized federation mechanism, we have no need to choose a common blockchain nor rely on a consortium's distributed ledger (DLT) for coordination. Orb has the ability to enable use cases where a public blockchain is not acceptable to stakeholders and also avoids lock-in to a single DLT.

As operations on DIDs can be originated by different writers in a decentralized network, it is also beneficial to enable a mechanism for determining the latest operations for a particular suffix. For example, when all DID operations are announced by the same blockchain, the other systems can monitor that blockchain for the latest operations. These blockchains can also provide relative timing in cases of late publishing. This DID method, instead, makes use of independent witness ledgers that the DID controller can associate with their DID. These witness ledgers can be monitored as additional propagation sources and proofs from these ledgers can also provide relative timing confidence in cases of late publishing. Rather than being a global chain of all operation announcements, these ledgers contain a subset of the announcements that propagate through the decentralized federation protocols.

The property of self certifying DIDs is obtained due to Orb being an extension of the [[[SIDETREE]]] protocol. By using the Sidetree protocol, the DID controller forms their own verifiable chain of patches from inception to the current state of the DID document. These verifiable chains are included into larger batches by one of the independent Orb servers and encoded as content-addressable objects. We also inherit the scalability properties of the Sidetree protocol batch strategy.

Our motivation for the Orb DID method is to enable many independent organizations to create Verifiable Data Registries (VDRs) that can become connected over time. In designing Orb, we have the goal to enable these VDRs to interconnect into a decentralized network without the need to choose a common public blockchain or to rely on special-purpose consortiums to form (and remain in operation). By using decentralized federation protocols and witness ledgers, we enable self-certifying DIDs even in use cases where leveraging a public blockchain or a consortium DLT is not acceptable to stakeholders. Orb enables a federated, replicated and scalable VDR based on a decentralized network for propagating, witnessing and replicating changes to DID documents.

Concepts

Orb uses a propagation model where servers announce transactions to other servers that are following them. In general, Orb Servers have the capability to propagate transactions and replicate Orb Verifiable Data Registries (VDRs). Beyond propagation and replication, there are three primary Orb Server roles in the transaction flow:

Orb transaction propagation model

Orb servers apply both the DID controller's intention and also observed timings to resolve scenarios where a DID update is applied at the same sequence position in the DID's operation chain. As the DID controller is fully responsible for the changes to their DIDs, they can create a new branch in their operation chain even after time has passed. Orb Servers use rules to determine the first published branch and use observations to assist in that decision as follows:

The following diagram provides an example of applying the stale rule to DID B and the usage of a Witness associated to DID A as a tie-breaker to resolve a branch.

Orb branch rules example

Transactions are structured as a content-addressable graph. An Orb server accepts operations from DID controllers and periodically creates a Sidetree batch consisting of operations from multiple DIDs. From the batch, a pretransaction is created that includes CIDs of the most recent transaction for each DID from the Writer server's point of view. The Writer preannounces the transaction to a set of Witnesses. Each Witness promises to include the transaction in their ledger by returning a timestamped and signed pretransaction. The Orb server then propagates the combination of the pretransaction with the timestamped witness signatures as a new transaction.

Orb enables the VDR to be replicated between independent systems to increase the overall resilience of the ecosystem. The transaction model includes Witness signatures as a common Orb format regardless of the Witness ledger implementation. To achieve our system monitoring properties, we assume a Witness holds a monitorable ledger that stores timestamped transactions with an append-only model. Witness ledgers can be replicated and monitored for consistent behavior. In particular, monitoring systems validate:

Orb transaction data model
We enable both a Web API for content-addressable storage (including endpoint discovery) and also Distributed Hash Tables (DHTs) such as IPFS. This specification also includes a Witness ledger profile based on certificate transparency [[RFC6962]].

Self Certifying Decentralized Identifiers

Orb DIDs are self certifying. The data structure that encodes the ordered updates to a DID document form their own verifiable chain from inception to the current state of the DID document. We inherit this property due to our usage of the [[[SIDETREE]]] protocol for encoding the DID document updates.

Propagation via Decentralized Federation

Orb makes use of decentralized federation protocols to propagate announcements and replicate content in a gossip manner. We use [[[ACTIVITYSTREAMS-CORE]]] and [[[ACTIVITYPUB]]]. In later sections, we describe the vocabulary and profile for using these protocols in Orb.

Orb does not rely on public blockchains nor DLTs to coordinate - there is no need for network consensus. Instead, we rely on self certifying DIDs being propagated and the ability to replicate VDRs in a gossip manner.

DID Updates Are Batched

Modifications to DID documents that are sent to an Orb servers are aggregated into batches prior to being announced on the decentralized network. By supporting batching, we enhance the scalability properties due to a reduction in messages and objects being stored. We inherit this property due to our usage of the [[[SIDETREE]]] protocol for encoding the DID document updates.

DID Updates Batches Form a Graph

When writing a batch of DID document updates, an Orb server also includes immutable references to the prior batches. These references form a graph (in the form of a Merkle Tree) such that the ancestor operations can be processed prior to a newly observed batch. This property is needed in the Orb data structures since we do not have a common blockchain to provide this history.

When a DID controller supplies their DID, they also include a reference to a batch in their DID string. This reference allows an Orb server to discover the graph where a DID was created (or more generally where a core Sidetree update occurred). This reference also enables the DID controller to specify a minimum version of the DID document that must be discovered in order to resolve.

CAS-based Verifiable Data Registry

The Orb VDR leverages Content-Addressable Storage (CAS) to hold the DID document batch files. The processed batch files result in hosting resolvable DID documents. By supporting a CAS, we enhance the ability to replicate immutable content across the decentralized network. We inherit this property due to our usage of the [[[SIDETREE]]] protocol for encoding the DID document updates and batches.

We also leverage the CAS to hold the graph of batches. The immutable references to prior batches are in the form of content identifiers (CIDs).

Witnesses Resolve Late Publishing

The Orb method relies on decentralized federation, gossip and CAS replication mechanisms to propagate the VDR among servers. The self certifying nature of Orb DIDs enables confidence in the DID validity without the need for network consensus. As the DID controller is fully responsible for the changes to their DIDs, they can create a new branch in their operation chain even after time has passed. This situation is called late publishing. The resolver systems require a mechanism to decide which branch should be used to represent the current DID state. It is also beneficial to create rules that enable a consistent viewpoint of the active branch among interconnected Orb servers. Resolver systems use rules to determine the first published branch and use observations from Witness systems to assist in that decision.

In the absence of Witnesses, we start with a simple rule: the first branch of a DID observed by an Orb server is the first published branch for that DID from that server's point of view. As Orb servers become increasingly interconnected, batches are gossiped between the Orb servers. Each server that forwards a batch includes a signed timestamp of when they observed the batch. The Orb Server MAY designate some of the other Orb servers as trusted. These trusted servers are then used to determine relative timing between batches. In the case that a majority viewpoint of itself and the other trusted Orb servers exists, that majority viewpoint will be used to determine the relative timing of the batches. From the relative timing of the batches, we also resolve the relative timing of the branch in the DID. An Orb server does not wait for consensus - its viewpoint may eventually converge to a majority view, over time, in these situations.

Rules exist to resolve the active branch so that Orb servers can consistently resolve the DID controller's intended and current DID document. To mitigate against initial divergences, we also introduce the Witness role to observe a transaction prior to propagation. The Orb transaction writer preannounces the existence of an Orb transaction by submitting it to a Witness. A Witness creates a proof that they observed an Orb transaction at a certain time. Witnesses have the capability to provide timestamping and also host a Witness Ledger to provide relative ordering between merged sets of Orb Transactions.

Although each Orb Server decides who to designate as trusted, it is also important that an Orb Server's behavior can be monitored. We use a Witness ledger as the mechanism for monitoring behavior - if inconsistencies are detected, other systems can adjust their viewpoint for trustworthiness. A Witness ledger is responsible for recording individual Orb transactions, providing signed transaction timestamps that can be embedded within a propagated Orb transaction, providing ledger consistency proofs, and providing an API that exposes their ledger. A Witness ledger is not responsible for maintaining the Orb transaction graph structure nor is it responsible for maintaining a complete history of Orb transactions. An Orb server is monitorable by exposing a Witness ledger API.

We structure witnesses ledgers in a similar manner to certificate transparency [[RFC6962]], as append-only Merkle Trees with proof capabilities. A Witness ledger promises to include a submitted Orb transaction within a time period known as the Maximum Merge Delay (MMD). Once the ledger merges a set of Orb transactions into the Merkle Tree, a Signed Tree Head (STH) is produced. The STH (and associated Merkle Proofs) is used to validate consistency between older and newer ledger revisions.

When an Orb server creates a transaction, the server requests other Orb servers (as Witnesses) to include the transaction into their Witness ledger. The Orb server preannounces a transaction to Witnesses after preparing and writing the Sidetree CAS objects. The transaction announcement is then sent to Witnesses for validation and inclusion by the Witnesses. Each witnesses validates the Orb transaction structure and that it was issued within an acceptable delta of their current time. Upon successful validation, each witness returns a signed transaction timestamp and a promise to include the Orb transaction into their Ledger. The Orb transaction (combined with the signed transaction timestamps) is then written into the CAS and propagated. When an Orb Server receives a propagated transaction, they invalidate (as stale) each Sidetree operation that has a timestamp that is not within an acceptable delta of the Orb transaction's timestamp. When these proofs originate from trusted servers, their timing information is immediately applied after receiving an Orb transaction propagation. An Orb server that receives witnessed transactions from trusted servers likely holds the majority timing view immediately in these situations.

Witnesses Propagate for a DID

As Orb relies on gossip replication, it is possible for a resolver system to miss transactions originating from other servers that they do not follow. To mitigate this issue for a particular DID, we allow the DID controller to specify a witness policy. This policy contains a set of witnesses that MUST be used when an Orb transaction includes changes to the associated DID. The Writer server ensures that, according to the DID's policy, a sufficient number of these server(s) are also acceptable as Witnesses to the Writer server. If the DID's policy is unacceptable to the Writer server, the operation MUST be rejected and not included into the Orb transaction. The DID policy ensures that the Resolver Servers can determine if they have the latest propagations and that the DID controller can use any Writer server that has mutually acceptable policies.

As a DID can be associated to a particular Witness, that Witness provides observations of the DID controller's behavior. In addition to propagating for a DID, the associated Witness is also given extra weight as a tie-breaker for resolving the late publishing scenario described in the previous section.

The ability to set (or change) the policy is an operation that is included into a propagated Sidetree core index.

Web Discovery APIs are Supported

The Orb DID method enables CAS discovery and usage via Web APIs. The WebFinger protocol [[RFC7033]] allows systems to query for both [[ACTIVITYPUB]] endpoints and also for CAS endpoints for a given resource. By enabling a fully Web-enabled model, we do not introduce a requirement on Distributed Hash Table (DHT) usage into the method.

Distributed Hash Tables are Supported

Although Orb DIDs can be created that do not have a dependency on DHTs, we also enable optional support for registering CAS resources on a DHT. Orb servers MAY choose to expose their CAS-based VDRs on a Distributed Hash Table (DHT) network such as IPFS. When using a DHT, we gain the advantage of not needing to specify any Web domain to be queried for discovery.

Graphs Enable Propagation Discovery

The Orb transaction graph that is stored into the content-addressable storage (CAS) also enables discovery of propagation properties. Each node in the transaction graph includes known CAS discovery information and witness endpoints. The CAS discovery information enable Orb servers to include additional endpoints when responding to WebFinger [[RFC7033]] queries. The witness endpoints enable Orb servers to follow additional systems for propagation.

The did:orb Format

The format for the did:orb method conforms to the [[[DID-CORE]]] specification. The DID scheme consists of the did:orb prefix, the mechanism for discovering content-addressable objects, a content identifier (CID) for the minimum node in the DID operation batch graph, and the unique suffix of the DID.

The method uses the following ABNF [[RFC5234]] format:

          did-orb-format        = "did:orb:" cas-discovery-scheme [":" min-graph-cid]
                                  ":" did-suffix [":" long-form-suffix-data]
          cas-discovery-scheme  = dht-scheme / webcas-scheme / local-scheme
          dht-scheme            = ( "ipfs" )
          webcas-scheme         = "webcas:" reg-name
          local-scheme          = "local"
          reg-name              = 1*idchar                ; more constrained than [RFC3986]
          min-graph-cid         = 1*idchar
          did-suffix            = 1*idchar
          long-form-suffix-data = 1*idchar                ; only applicable in the local-scheme
        

See [[?RFC3986]] for the original definition of reg-name and [[DID-CORE]] for the definition of idchar. [[SIDETREE]] provides additional explanation for the did-suffix and long-form-suffix-data elements.

          did:orb:webcas:example.com:bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y:EiDyOQbbZAa3aiRzeCkV7LOx3SERjjH93EXoIM3UoN4oWg
        
          did:orb:ipfs:bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y:EiDyOQbbZAa3aiRzeCkV7LOx3SERjjH93EXoIM3UoN4oWg
        

TODO: discussion about not including a CAS discovery scheme and the possibility of a DHT profile for Orb.

Client-to-Server Operations

The Orb DID method extends the operations specified in the [[SIDETREE]] specification.

Create

An Orb DID is created by submitting a create to the operations endpoint of an Orb Server, as specified in Sidetree.

Detailed steps are specified in the Sidetree API.

Read

An Orb Server exposes a DID resolution API as defined in [[DID-CORE]] using the HTTP(S) Binding specified by [[DIDRESOLUTION]].

Detailed steps and additional method metadata properties are specified in the Sidetree API.

Update

An Orb DID is created by submitting an update to the operations endpoint of an Orb Server, as specified in Sidetree.

Detailed steps are specified in the Sidetree API.

Recover

An Orb DID is recovered by submitting a recover to the operations endpoint of an Orb Server, as specified in Sidetree.

Detailed steps are specified in the Sidetree API.

Deactivate

An Orb DID is deactivated by submitting a deactivate to the operations endpoint of an Orb Server, as specified in Sidetree.

Detailed steps are specified in the Sidetree API.

Propagation Delay and Canonical IDs

Orb DIDs support Sidetree DID URI Composition for the did-suffix and long-form-suffix-data elements. An Orb resolver with previous knowledge of a particular suffix (or with the inclusion of long-form-suffix-data) is able to return a resolution result without additional prefix information. This situation typically occurs when there is a need to immediately use a freshly created DID suffix that hasn't yet been propagated.
            did:orb:local:EiA329wd6Aj36YRmp7NGkeB5ADnVt8ARdMZMPzfXsjwTJA
          
            did:orb:local:EiA329wd6Aj36YRmp7NGkeB5ADnVt8ARdMZMPzfXsjwTJA:ey...
          
            did:orb:webcas:example.com:EiA329wd6Aj36YRmp7NGkeB5ADnVt8ARdMZMPzfXsjwTJA
          
When an Orb Server has propagation information for a resolved DID, the server includes the min-graph-cid segment within the canonicalId property of the returned DID document metadata. The min-graph-cid is set to the CID of the latest known AnchorCredential that contains a Create or Recover operation for the DID. After a DID controller sends a Create or Recover operation to an Orb Server, the resolution result may have no (or an outdated) min-graph-cid segment. The DID controller may need to retry resolution until the operation has been propagated and the CID becomes available. Once the CID is available, resolution responses contain the updated canonicalId property.

Discovery

Domain Endpoint Discovery

A client discovers a domain's endpoints for DID resolution and DID operations using a .well-known scheme [[RFC8615]]. The domain declares its own endpoints for resolution and operations.

              GET /.well-known/did-orb HTTP/1.1
              Host: alice.example.com
              Accept-Encoding: gzip, deflate
            

When the discovery profile exists, an HTTP 200 status code is returned.

              HTTP/1.1 200 OK
              Date: Sat, 30 Jan 2021 18:31:58 GMT
              Content-Type: application/json; charset=utf-8
              Connection: keep-alive
              Access-Control-Allow-Origin: *

              {
                "resolutionEndpoint": "https://alice.example.com/sidetree/0.1/identifiers",
                "operationEndpoint": "https://alice.example.com/sidetree/0.1/operations"
              }
            

Shared Resolution Domain

A shared domain may be used to enable a group of organizations to share resolution responsibilities. This model enables a resolution client to have increased confidence by validating a resolution result against multiple entities. A domain is a shared domain model when it declares its linked domains and an n-of-m policy in its well-known configuration. Linked domains indicate to clients that these domains have an synchronized (eventually consistent) relationship and can be used n-of-m for resolution. These domains and policy are fetched using a WebFinger [[RFC7033]] query. When using linked domain resolution, the client performs the following steps to obtain higher confidence in the resolution results:

  1. Lookup n from the https://trustbloc.dev/ns/min-resolvers property.
  2. Choose n of the links from the WebFinger result.
  3. Fetches the configurations at each chosen link using WebFinger.
  4. Validates that each well-known configuration has the same policy for n and that all of the chosen links are listed in the n fetched configurations.
  5. Resolve the DID at each of the n chosen links.
  6. Ensure that the DID resolution result matches (other than resolver-specific metadata such as timestamps).
  7. In case of a mismatch, additional links may need to be chosen until the client has n matches.
              GET /.well-known/did-orb HTTP/1.1
              Host: shared.example.com
              Accept-Encoding: gzip, deflate
            

When the discovery profile for the shared domain exists, an HTTP 200 status code is returned.

              HTTP/1.1 200 OK
              Date: Sat, 30 Jan 2021 18:31:58 GMT
              Content-Type: application/json; charset=utf-8
              Connection: keep-alive
              Access-Control-Allow-Origin: *
  
              {
                "resolutionEndpoint": "https://shared.example.com/sidetree/0.1/identifiers",
                "operationEndpoint": "https://shared.example.com/sidetree/0.1/operations"
              }
            
              GET /.well-known/webfinger?resource=https%3A%2F%2Fshared.example.com%2Fsidetree%2F0.1%2Fidentifiers HTTP/1.1
              Host: shared.example.com
              Accept-Encoding: gzip, deflate
            

When the DID resolution capability exists, an HTTP 200 status code is returned.

              HTTP/1.1 200 OK
              Date: Sat, 30 Jan 2021 18:31:58 GMT
              Content-Type: application/jrd+json
              Connection: keep-alive
              Access-Control-Allow-Origin: *
  
              {
                "subject": "https://shared.example.com/sidetree/0.1/identifiers",
                "properties": {
                  "https://trustbloc.dev/ns/min-resolvers": 2
                },
                "links": [
                  {
                    "rel": "self",
                    "href": "https://shared.example.com/sidetree/0.1/identifiers"
                  },
                  {
                    "rel": "alternate",
                    "href": "https://charlie.example.com/sidetree/0.1/identifiers"
                  },
                  {
                    "rel": "alternate",
                    "href": "https://oscar.example.com/sidetree/0.1/identifiers"
                  },
                  {
                    "rel": "alternate",
                    "href": "https://mike.example.com/sidetree/0.1/identifiers"
                  }
                ]
              }
            

Operation Origin and Policy

The DID controller declares their origin policy by setting the URI of their desired witness into the anchorOrigin property in the Create or Replace operation.

TODO: In a followup revision, we might specify additional origin policy information.

Operation Anchoring Validity

The DID Controller includes their skew-adjusted current timestamp in the anchorFrom property of an operation's data object payload. To allow for clock skew, the DID Controller subtracts five minutes from their current timestamp. The timestamp is formatted as a [[RFC7519]] NumericDate.

The DID Controller includes their skew-adjusted current timestamp in the anchorUntil property of an operation's data object payload. To allow for batching and clock skew, the DID Controller adds 25 minutes to their current timestamp. The timestamp is formatted as a [[RFC7519]] NumericDate.

The delta range between anchorFrom and anchorUntil is 30 minutes.

TODO: In a followup revision, we will specify protocol parameters to constrain the delta range.

Server-to-Server APIs

An Orb Server exposes APIs for propagation, content-addressable storage, discovery, and witnessing.

Vocabulary

AnchorCredential

An Orb Transaction is represented as an AnchorCredential.

              {
                "@context": [
                  "https://www.w3.org/2018/credentials/v1",
                  "https://trustbloc.github.io/did-method-orb/contexts/anchor/v1",
                  "https://w3id.org/jws/v1"
                ],
                "id": "http://sally.example.com/transactions/bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                "type": [
                  "VerifiableCredential",
                  "AnchorCredential"
                ],
                "issuer": "https://sally.example.com/services/orb",
                "issuanceDate": "2021-01-27T09:30:10Z",
                "credentialSubject": {
                  "operationCount": 1,
                  "coreIndex": "bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                  "namespace": "did:orb",
                  "version": "1",
                  "previousAnchors": {
                    "EiA329wd6Aj36YRmp7NGkeB5ADnVt8ARdMZMPzfXsjwTJA": "bafkreibmrmenuxhgaomod4m26ds5ztdujxzhjobgvpsyl2v2ndcskq2iay",
                    "EiABk7KK58BVLHMataxgYZjTNbsHgtD8BtjF0tOWFV29rw": "bafkreibh3whnisud76knkv7z7ucbf3k2rs6knhvajernrdabdbfaomakli"
                  },
                  "type": "Anchor"
                },
                "proof": [{
                  "type": "JsonWebSignature2020",
                  "proofPurpose": "assertionMethod",
                  "created": "2021-01-27T09:30:00Z",
                  "verificationMethod": "did:example:abcd#key",
                  "domain": "sally.example.com",
                  "jws": "eyJ..."
                },
                {
                  "type": "JsonWebSignature2020",
                  "proofPurpose": "assertionMethod",
                  "created": "2021-01-27T09:30:05Z",
                  "verificationMethod": "did:example:abcd#key",
                  "domain": "https://witness1.example.com/ledgers/maple2021",
                  "jws": "eyJ..."
                },
                {
                  "type": "JsonWebSignature2020",
                  "proofPurpose": "assertionMethod",
                  "created": "2021-01-27T09:30:06Z",
                  "verificationMethod": "did:example:efgh#key",
                  "domain": "https://witness2.example.com/ledgers/spruce2021",
                  "jws": "eyJ..."
                }]                  
              }
            
The Orb transaction is embedded within an Activity during propagation flows. The Activity includes information about the CAS.
              {
                "@context": [
                  "https://www.w3.org/ns/activitystreams",
                  "https://trustbloc.github.io/did-method-orb/contexts/anchor/v1"
                ],
                "id": "https://sally.example.com/services/orb/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Create",
                "actor": "https://sally.example.com/services/orb",
                "to": [
                  "https://sally.example.com/services/orb/followers",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "published": "2021-01-27T09:30:10Z",
                "target": {
                  "id":  "https://sally.example.com/cas/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                  "type": "ContentAddressedStorage",
                  "cid":  "bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y"
                },
                "object": {
                  "@context": [
                    "https://www.w3.org/2018/credentials/v1",
                    "https://trustbloc.github.io/did-method-orb/contexts/anchor/v1"
                  ],
                  "id": "http://sally.example.com/transactions/642bab75-76a4-4b5f-98bb-794467ad5891",
                  "type": [
                    "VerifiableCredential",
                    "AnchorCredential"
                  ],
                  "issuer": "https://sally.example.com/services/orb",
                  "issuanceDate": "2021-01-27T09:30:10Z",
                  "credentialSubject": {
                    "operationCount": 1,
                    "coreIndex": "bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                    "namespace": "did:orb",
                    "version": "1",
                    "previousAnchors": {
                      "EiA329wd6Aj36YRmp7NGkeB5ADnVt8ARdMZMPzfXsjwTJA": "bafkreibmrmenuxhgaomod4m26ds5ztdujxzhjobgvpsyl2v2ndcskq2iay",
                      "EiABk7KK58BVLHMataxgYZjTNbsHgtD8BtjF0tOWFV29rw": "bafkreibh3whnisud76knkv7z7ucbf3k2rs6knhvajernrdabdbfaomakli"
                    },
                    "type": "Anchor"
                  },
                  "proof": [{}]
                }
              }
            

Service Discovery

An Orb Server discovers a domain's endpoints for the Orb actor using a .well-known scheme [[RFC8615]].

            GET /.well-known/did-orb HTTP/1.1
            Host: alice.example.com
            Accept-Encoding: gzip, deflate
          

When the discovery profile exists, an HTTP 200 status code is returned.

            HTTP/1.1 200 OK
            Date: Sat, 30 Jan 2021 18:31:58 GMT
            Content-Type: application/json; charset=utf-8
            Connection: keep-alive
            Access-Control-Allow-Origin: *

            {
              "resolutionEndpoint": "https://alice.example.com/sidetree/0.1/identifiers",
              "operationEndpoint": "https://alice.example.com/sidetree/0.1/operations",
              "serviceEndpoint": "https://alice.example.com/services/orb"
            }
          

Add description for .well-known/nodeinfo.

ActivityPub

This section defines a minimal profile of [[ACTIVITYPUB]] that Orb Servers implement to participate in propagation flows.

Actor (Discovery)

An Actor exposed by an Orb Server is capable of participating in propagation flows. ActivityPub endpoints are retrieved by performing an HTTP GET to the Actor endpoint. The Actor endpoint returns an Actor object.

              GET /services/orb HTTP/1.1
              Host: alice.example.com
              Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
              Accept-Encoding: gzip, deflate
            

When the service profile exists, an HTTP 200 status code is returned.

              HTTP/1.1 200 OK
              Date: Wed, 27 Jan 2021 21:57:25 GMT
              Content-Type: application/jrd+json; charset=utf-8
              Connection: keep-alive
              Access-Control-Allow-Origin: *

              {
                "@context": [
                "https://www.w3.org/ns/activitystreams",
                "https://w3id.org/security/v1",
                "https://trustbloc.github.io/did-method-orb/contexts/anchor/v1"
                ],
                "id": "https://alice.example.com/services/orb",
                "type": "Service",
                "publicKey": {
                  "id": "https://alice.example.com/services/orb#main-key",
                  "owner": "https://alice.example.com/services/orb",
                  "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
                },
                "inbox": "https://alice.example.com/services/orb/inbox",
                "outbox": "https://alice.example.com/services/orb/outbox",
                "followers": "https://alice.example.com/services/orb/followers",
                "following": "https://alice.example.com/services/orb/following",
                "witnesses": "https://alice.example.com/services/orb/witnesses",
                "witnessing": "https://alice.example.com/services/orb/witnessing",
                "likes": "https://alice.example.com/services/orb/likes",
                "liked": "https://alice.example.com/services/orb/liked",
                "shares": "https://alice.example.com/services/orb/shares"
              }
            

TODO: Review handling of publicKey.

Delivery

Orb Servers use ActivityPub Delivery mechanisms to propagate transactions and to request to follow other servers. A propagation or follow request is sent by performing an HTTP POST of an activity to an Orb Server's ActivityPub inbox endpoint.

Orb Servers use HTTP Signatures [[HTTP-SIGNATURES]] when calling Server-to-Server APIs to create authenticated messages. The Orb Transaction objects are additionally signed using JSON-LD proofs [[LDPROOF]] from both the Writer server as well as Witness servers.

Collections

Please refer to [[ACTIVITYPUB]] section 5 for general collections guidance. An Orb Server exposes:

  • Outbox: provides an ordered collection of the activities published by the server.
  • Inbox: provides an ordered collection of the activities received by the server.
  • Followers: provides a collection (or ordered collection) of the other Orb servers following the server.
  • Following: provides a collection (or ordered collection) of the other Orb servers the server is following.
  • Witnesses: provides a collection (or ordered collection) of the other Orb servers that act as witnesses.
  • Witnessing: provides a collection (or ordered collection) of the other Orb servers the server is witnessing.
  • Likes: provides an ordered collection of the witness activities sent by the Orb server.
  • Liked: provides an ordered collection of the witness activities received by the Orb server.
  • Shares: provides an ordered collection of the propagation (announce) activities sent by the Orb server.

Create Activity

An Orb Server that creates a Transaction uses an Create activity to propagate that knowledge to its own followers. A transaction is propagated to another Orb Server by performing an HTTP POST of an Create activity containing an AnchorCredential to its ActivityPub inbox endpoint.

              POST /services/orb/inbox HTTP/1.1
              Host: alice.example.com
              Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
              Accept-Encoding: gzip, deflate

              {
                "@context": [
                  "https://www.w3.org/ns/activitystreams",
                  "https://trustbloc.github.io/context/orb"
                ],
                "id": "https://sally.example.com/services/orb/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Create",
                "actor": "https://sally.example.com/services/orb",
                "to": [
                  "https://sally.example.com/services/orb/followers",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "published": "2021-01-27T09:30:10Z",
                "object": {
                  "id": "http://sally.example.com/transactions/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                  "type": "AnchorCredentialReference",
                  "target": {
                    "id":  "https://sally.example.com/cas/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                    "type": "ContentAddressedStorage",
                    "cid":  "bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y"
                  }
                }
              }
            

On success, HTTP status code 200 is returned.

              POST /services/orb/inbox HTTP/1.1
              Host: alice.example.com
              Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
              Accept-Encoding: gzip, deflate

              {
                "@context": [
                  "https://www.w3.org/ns/activitystreams",
                  "https://trustbloc.github.io/did-method-orb/contexts/anchor/v1"
                ],
                "id": "https://sally.example.com/services/orb/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Create",
                "actor": "https://sally.example.com/services/orb",
                "to": [
                  "https://sally.example.com/services/orb/followers",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "published": "2021-01-27T09:30:10Z",
                "target": {
                  "id":  "https://sally.example.com/cas/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                  "type": "ContentAddressedStorage",
                  "cid":  "bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y"
                },
                "object": {
                  "@context": [
                    "https://www.w3.org/2018/credentials/v1",
                    "https://trustbloc.github.io/did-method-orb/contexts/anchor/v1"
                  ],
                  "id": "http://sally.example.com/transactions/bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                  "type": [
                    "VerifiableCredential",
                    "AnchorCredential"
                  ],
                  "issuer": "https://sally.example.com/services/orb",
                  "issuanceDate": "2021-01-27T09:30:10Z",
                  "credentialSubject": {
                    "operationCount": 1,
                    "coreIndex": "bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                    "namespace": "did:orb",
                    "version": "1",
                    "previousAnchors": {
                      "EiA329wd6Aj36YRmp7NGkeB5ADnVt8ARdMZMPzfXsjwTJA": "bafkreibmrmenuxhgaomod4m26ds5ztdujxzhjobgvpsyl2v2ndcskq2iay",
                      "EiABk7KK58BVLHMataxgYZjTNbsHgtD8BtjF0tOWFV29rw": "bafkreibh3whnisud76knkv7z7ucbf3k2rs6knhvajernrdabdbfaomakli"
                    },
                    "type": "Anchor"
                  },
                  "proof": [{}]
                }
              }
            

On success, HTTP status code 200 is returned.

Announce Activity

An Orb Server that learns about a new Transaction that was created by another Orb server uses an Announce activity to gossip that knowledge to its own followers. A transaction is forwarded to another Orb Server by performing an HTTP POST of an Announce activity containing an AnchorCredential to its ActivityPub inbox endpoint.

              POST /services/orb/inbox HTTP/1.1
              Host: alice.example.com
              Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
              Accept-Encoding: gzip, deflate

              {
                "@context": [
                  "https://www.w3.org/ns/activitystreams"
                ],
                "id": "https://sally.example.com/services/orb/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Announce",
                "actor": "https://sally.example.com/services/orb",
                "to": [
                  "https://sally.example.com/services/orb/followers",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "published": "2021-01-27T09:30:10Z",
                "object": {
                  "type": "Collection",
                  "totalItems": 1,
                  "items": [
                    {
                      "@context": [
                        "https://trustbloc.github.io/did-method-orb/contexts/anchor/v1"
                      ],
                      "id": "http://sally.example.com/transactions/bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                      "type": "AnchorCredentialReference",
                      "target": {
                        "id":  "https://sally.example.com/cas/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                        "type": "ContentAddressedStorage",
                        "cid":  "bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y"
                      }
                    }
                  ]
                }
              }
            

On success, HTTP status code 200 is returned.

              POST /services/orb/inbox HTTP/1.1
              Host: alice.example.com
              Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
              Accept-Encoding: gzip, deflate

              {
                "@context": [
                  "https://www.w3.org/ns/activitystreams",
                  "https://trustbloc.github.io/did-method-orb/contexts/anchor/v1"
                ],
                "id": "https://sally.example.com/services/orb/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Announce",
                "actor": "https://sally.example.com/services/orb",
                "to": [
                  "https://sally.example.com/services/orb/followers",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "published": "2021-01-27T09:30:10Z",
                "object": {
                  "type": "Collection",
                  "totalItems": 1,
                  "items": [
                    {
                      "id": "http://sally.example.com/transactions/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                      "type": "AnchorCredentialReference",
                      "target": {
                        "id":  "https://sally.example.com/cas/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                        "type": "ContentAddressedStorage",
                        "cid":  "bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y"
                      },
                      "object": {
                        "@context": [
                          "https://www.w3.org/2018/credentials/v1",
                          "https://trustbloc.github.io/did-method-orb/contexts/anchor/v1"
                        ],
                        "id": "http://sally.example.com/transactions/bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                        "type": [
                          "VerifiableCredential",
                          "AnchorCredential"
                        ],
                        "issuer": "https://sally.example.com/services/orb",
                        "issuanceDate": "2021-01-27T09:30:10Z",
                        "credentialSubject": {
                          "operationCount": 1,
                          "coreIndex": "bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                          "namespace": "did:orb",
                          "version": "1",
                          "previousAnchors": {
                            "EiA329wd6Aj36YRmp7NGkeB5ADnVt8ARdMZMPzfXsjwTJA": "bafkreibmrmenuxhgaomod4m26ds5ztdujxzhjobgvpsyl2v2ndcskq2iay",
                            "EiABk7KK58BVLHMataxgYZjTNbsHgtD8BtjF0tOWFV29rw": "bafkreibh3whnisud76knkv7z7ucbf3k2rs6knhvajernrdabdbfaomakli"
                          },
                          "type": "Anchor"
                        },
                        "proof": [{}]
                      }
                    }
                  ]
                }
              }
            

On success, HTTP status code 200 is returned.

Follow Activity

An Orb Server requests to follow another Orb Server by performing an HTTP POST of a Follow activity to its ActivityPub inbox endpoint. Refer to ActivityPub for guidance.

                POST /services/orb/inbox HTTP/1.1
                Host: alice.example.com
                Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
                Accept-Encoding: gzip, deflate
  
                {
                  "@context": "https://www.w3.org/ns/activitystreams",
                  "id": "https://sally.example.com/activities/9052018f-9530-4aef-8a46-40bde9445a25",
                  "type": "Follow",
                  "to": "https://alice.example.com/services/orb",
                  "actor": "https://sally.example.com/services/orb"
                }
              

On success, HTTP status code 200 is returned.

Accept Activity

An Orb Server accepts a Follow request by sending an HTTP POST of a Accept activity to the requesting Orb Server's ActivityPub inbox endpoint. Refer to ActivityPub for guidance.

                POST /services/orb/inbox HTTP/1.1
                Host: sally.example.com
                Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
                Accept-Encoding: gzip, deflate
  
                {
                  "@context": "https://www.w3.org/ns/activitystreams",
                  "id": "https://alice.example.com/activities/e017e49a-6256-44a0-855d-5dc304ae67f7",
                  "to": "https://sally.example.com/services/orb",
                  "type": "Accept",
                  "actor": "https://alice.example.com/services/orb",
                  "object": {
                      "id": "https://sally.example.com/activities/9052018f-9530-4aef-8a46-40bde9445a25",
                      "to": "https://alice.example.com/services/orb",
                      "type": "Follow",
                      "actor": "https://sally.example.com/services/orb",
                  }                  
                }
              

On success, HTTP status code 200 is returned.

Reject Activity

An Orb Server rejects a Follow request by sending an HTTP POST of a Reject activity to the requesting Orb Server's ActivityPub inbox endpoint. Refer to ActivityPub for guidance.

                POST /services/orb/inbox HTTP/1.1
                Host: sally.example.com
                Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
                Accept-Encoding: gzip, deflate
  
                {
                  "@context": "https://www.w3.org/ns/activitystreams",
                  "id": "https://alice.example.com/activities/e017e49a-6256-44a0-855d-5dc304ae67f7",
                  "to": "https://sally.example.com/services/orb",
                  "type": "Reject",
                  "actor": "https://alice.example.com/services/orb",
                  "object": {
                      "id": "https://sally.example.com/activities/9052018f-9530-4aef-8a46-40bde9445a25",
                      "to": "https://alice.example.com/services/orb",
                      "type": "Follow",
                      "actor": "https://sally.example.com/services/orb",
                  }                  
                }
              

On success, HTTP status code 200 is returned.

Witness ActivityPub

Offer Activity

An Orb Server sends a preannouncement of an Orb transaction using an Offer activity populated with an AnchorCredential. The AnchorCredential contains a Linked Data Proof from the Orb Writer server. The Offer activity also includes a startTime and endTime indicating the time period where the Orb Server will accept linked data proofs for inclusion into the Orb transaction. The startTime is the current timestamp for the Orb Server. The endTime is the cut-off timestamp for inclusion into the transaction.

              POST /services/orb/inbox HTTP/1.1
              Host: witness1.example.com
              Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
              Accept-Encoding: gzip, deflate

              {
                "@context": [
                  "https://www.w3.org/ns/activitystreams"
                ],
                "id": "https://sally.example.com/services/orb/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Offer",
                "actor": "https://sally.example.com/services/orb",
                "to": [
                  "https://sally.example.com/services/orb/witnesses",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "startTime": "2021-01-27T09:30:10Z",
                "endTime": "2021-01-27T09:31:10Z",
                "object": {
                  "@context": [
                    "https://www.w3.org/2018/credentials/v1",
                    "https://trustbloc.github.io/did-method-orb/contexts/anchor/v1"
                  ],
                  "id": "http://sally.example.com/transactions/bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                  "type": [
                    "VerifiableCredential",
                    "AnchorCredential"
                  ],
                  "issuer": "https://sally.example.com/services/orb",
                  "issuanceDate": "2021-01-27T09:30:10Z",
                  "credentialSubject": {
                    "anchorString": "bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                    "namespace": "did:orb",
                    "version": "1",
                    "previousTransactions": {
                      "EiA329wd6Aj36YRmp7NGkeB5ADnVt8ARdMZMPzfXsjwTJA": "bafkreibmrmenuxhgaomod4m26ds5ztdujxzhjobgvpsyl2v2ndcskq2iay",
                      "EiABk7KK58BVLHMataxgYZjTNbsHgtD8BtjF0tOWFV29rw": "bafkreibh3whnisud76knkv7z7ucbf3k2rs6knhvajernrdabdbfaomakli"
                    }
                  },
                  "proof": {}
                }
              }
            

Like Activity

A Witness promises to include an Orb transaction into their ledger using a Like activity populated with its linked data proof of an AnchorCredential received in a previous Offer. A reference to the previously offered object is populated into the object field. The Witness includes the time range when the Orb transaction will become included into their ledger using startTime and endTime fields.

              POST /services/orb/inbox HTTP/1.1
              Host: witness1.example.com
              Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
              Accept-Encoding: gzip, deflate

              {
                "@context": [
                  "https://www.w3.org/ns/activitystreams"
                ],
                "id": "https://witness1.example.com/services/orb/likes/87bcd005-abb6-433d-a889-18bc1ce84988",
                "type": "Like",
                "actor": "https://witness1.example.com/services/orb",
                "to": [
                  "https://sally.example.com/services/orb",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "startTime": "2021-01-27T09:30:15Z",
                "endTime": "2021-01-27T09:40:15Z",
                "object": "http://sally.example.com/transactions/bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                "result": {
                  "@context": [
                    "https://w3id.org/security/v1",
                    "https://w3id.org/jws/v1"
                  ],
                  "proof": {
                    "type": "JsonWebSignature2020",
                    "proofPurpose": "assertionMethod",
                    "created": "2021-01-27T09:30:15Z",
                    "verificationMethod": "did:example:abcd#key",
                    "domain": "https://witness1.example.com/ledgers/maple2021",
                    "jws": "eyJ..."  
                  }
                }
              }
            

Review the appropriate Activity type in the above example.

Review the whether or not we should embed the rest of the VC in the response.

The Writer Orb Server assembles the linked data proofs into a proof chain to create the final AnchorCredential. The proof chain starts with the Writer's linked data proof followed by each witness's linked data proof. See .

WebFinger

WebFinger [[RFC7033]] is used to discover endpoints for Orb service and resource URIs.

The following example retrieves endpoints for a service named orb at alice.example.com.

            GET /.well-known/webfinger?resource=https%3A%2F%2Falice.example.com%2Fservices%2Forb HTTP/1.1
            Host: alice.example.com
            Accept-Encoding: gzip, deflate
          

When the resource exists, an HTTP 200 status code is returned.

            HTTP/1.1 200 OK
            Date: Sat, 30 Jan 2021 13:20:11 GMT
            Content-Type: application/jrd+json
            Connection: keep-alive
            Access-Control-Allow-Origin: *

            {
              "subject": "https://alice.example.com/services/orb",
              "aliases": [
                "https://alice.example.com/@orb",
                "https://alice.example.com/services/orb"
              ],
              "links": [
                {
                  "rel": "self",
                  "type": "application/activity+json",
                  "href": "https://alice.example.com/services/orb"
                }
              ]
            }
          

Aliases for a resource can also be queried. For example, the following is a request for orb@alice.example.com. This request has the same response as the prior example.

            GET /.well-known/webfinger?resource=acct%orb%40alice.example.com HTTP/1.1
            Host: alice.example.com
            Accept-Encoding: gzip, deflate
          

WebFinger is also used to query for resource endpoint discovery.

            GET /.well-known/webfinger?resource=https%3A%2F%2Fsally.example.com%2Ftransactions%2Fbafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y HTTP/1.1
            Host: sally.example.com
            Accept-Encoding: gzip, deflate
          

When the resource exists, an HTTP 200 status code is returned.

            HTTP/1.1 200 OK
            Date: Sat, 30 Jan 2021 13:20:11 GMT
            Content-Type: application/jrd+json
            Connection: keep-alive
            Access-Control-Allow-Origin: *

            {
              "subject": "https://sally.example.com/transactions/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
              "links": [
                {
                  "rel": "self",
                  "type": "application/ld+json",
                  "href": "https://sally.example.com/transactions/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y"
                },
                {
                  "rel": "working-copy",
                  "type": "application/ld+json",
                  "href": "https://sally.example.com/cas/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y"
                },
                {
                  "rel": "working-copy",
                  "type": "application/ld+json",
                  "href": "https://replica.example.com/cas/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y"
                }
              ]
            }
          

Review the appropriate link relations in the above example.

Content Addressable Storage

Orb Servers expose Orb and Sidetree content as content-addressable objects. These objects can be retrieved through Web endpoints or discovered using a Distributed Hash Table (DHT).

WebCAS

Orb Servers expose a CAS endpoint to provide access to Orb and Sidetree content. Resources are retrieved by sending a HTTP GET to the Server's CAS path with the content identifier [[CID]] of the resource.

              GET /cas/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y HTTP/1.1
              Host: sally.example.com
              Accept-Encoding: gzip, deflate
            

When the content exists, an HTTP 200 status code is returned.

              HTTP/1.1 200 OK
              Date: Sat, 30 Jan 2021 13:20:11 GMT
              Content-Type: text/plain; charset=utf-8
              Connection: keep-alive
              Access-Control-Allow-Origin: *

              {
                "@context": [
                  "https://www.w3.org/2018/credentials/v1",
                  "https://trustbloc.github.io/did-method-orb/contexts/anchor/v1",
                  "https://w3id.org/jws/v1"
                ],
                "id": "http://sally.example.com/transactions/bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                "type": [
                  "VerifiableCredential",
                  "AnchorCredential"
                ],
                "issuer": "https://sally.example.com/services/orb",
                "issuanceDate": "2021-01-27T09:30:10Z",
                "credentialSubject": {
                  "operationCount": 1,
                  "coreIndex": "bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                  "namespace": "did:orb",
                  "version": "1",
                  "previousAnchors": {
                    "EiA329wd6Aj36YRmp7NGkeB5ADnVt8ARdMZMPzfXsjwTJA": "bafkreibmrmenuxhgaomod4m26ds5ztdujxzhjobgvpsyl2v2ndcskq2iay",
                    "EiABk7KK58BVLHMataxgYZjTNbsHgtD8BtjF0tOWFV29rw": "bafkreibh3whnisud76knkv7z7ucbf3k2rs6knhvajernrdabdbfaomakli"
                  },
                  "type": "Anchor"
                },
                "proof": [{}]                  
              }
            

Distributed Hash Table

We currently enable usage of the IPFS DHT. In the future, we may want to additionally consider an Orb-specific profile to limit usage to VDRs.

Witness Ledger

Ledger Types

Name Type Code Specification
Verifiable Credential Transparency vct-v1

Discovery

The linked data proof includes a domain property that indicates the ledger that promises to include an Orb transaction. The type of the ledger can be determined using the WebFinger protocol [[RFC7033]] along with known replicas, if any. The replica links have the relation field set to alternate.

          GET /.well-known/webfinger?resource=https%3A%2F%2Fwitness1.example.com%2Fledgers%2Fmaple2021 HTTP/1.1
          Host: witness1.example.com
          Accept-Encoding: gzip, deflate
        

When the ledger exists, an HTTP 200 status code is returned.

          HTTP/1.1 200 OK
          Date: Sat, 30 Jan 2021 18:31:58 GMT
          Content-Type: application/jrd+json
          Connection: keep-alive
          Access-Control-Allow-Origin: *

          {
            "subject": "https://witness1.example.com/ledgers/maple2021",
            "properties": {
              "https://trustbloc.dev/ns/ledger-type": "vct-v1"
            },
            "links": [
              {
                "rel": "self",
                "href": "https://witness1.example.com/ledgers/maple2021"
              },
              {
                "rel": "alternate",
                "href": "https://replica.example.com/ledgers/maple2021"
              }
            ]
          }
        

Verifiable Credential Transparency (VCT)

The Verifiable Credential Transparency (VCT) Witness ledger is based on on certificate transparency [[RFC6962]]. The Anchor Credentials are included into VCT ledgers (as append-only Merkle Tree logs). VCT ledgers may be named to enable rollover.

We extend [[RFC6962]] to include Verifiable Credential (VC) [[VC-DATA-MODEL]] objects. The following subsections describe the endpoints and the additional VC type for the Merkle Tree.

Add Verifiable Credential

Orb AnchorCredentials are added using the process described in , as required by Orb.

Latest Signed Tree Head

The Retrieve Latest Signed Tree Head endpoint is described in RFC6962. In this example, a signed tree head is requested from a ledger named maple2021.

            GET /ledgers/maple2021/ct/v1/get-sth HTTP/1.1
            Host: witness1.example.com
            Accept-Encoding: gzip, deflate
          
            HTTP/1.1 200 OK
            Date: Sat, 30 Jan 2021 13:20:11 GMT
            Content-Type: application/json
            Connection: keep-alive
            Access-Control-Allow-Origin: *

            {
              "tree_size": 100,
              "timestamp": 1612097791,
              "sha256_root_hash": "qOK55eWhQ96DmRbAsHTridM1BrxZtqJAW2uF0ceQcTo=",
              "tree_head_signature": "6UIXmSf8Pg9jY6e8DUPguEW3sl0HxX0fckMnP5ckUchcnsjlXee0722AV4RL1+xNEGVX/poCKu/wPH9teprMyw",
            }
          

Merkle Consistency Proof

The Retrieve Merkle Consistency Proof between Two Signed Tree Heads endpoint is described in RFC6962.

Merkle Audit Proof

The Retrieve Merkle Audit Proof from Log by Leaf Hash endpoint is described in RFC6962.

Retrieve Entries

The Retrieve Entries from Log endpoint is described in RFC6962.

extra_data: We extend the entry types to include verifiable credentials. In the case of a VC type, extra_data contains the verifiable credential.

Acceptable Issuers

TODO: In a followup revision, we may specify an equivalent endpoint for [[RFC6962]] section 4.7.

Entry and Audit Proof

The Retrieve Entry+Merkle Audit Proof endpoint is described in RFC6962.

extra_data: We extend the entry types to include verifiable credentials. In the case of a VC type, extra_data contains the verifiable credential.

Merkle Tree and Entries

We extend the LogEntryType enumeration described in RFC6962 to include vc_entry. The MerkleTree structure described in RFC6962 is extended to include a vc_entry case. In the case of vc_entry, the signed_entry field of the Merkle Tree contains a Verifiable Credential.

Monitoring and Auditing

Please refer to [[RFC6962]] section 5.3 and section 5.4.

Security Considerations

Security considerations are provided in conformance with [[RFC3552]] section 5 and [[DID-CORE]].

As Orb is an extension of [[SIDETREE]], we inherit the security properties of self certifying DIDs and the Sidetree operation chains:

As Orb is a DID method based on gossip propagation, each Orb Server holds a subset of the overall transaction graph.

The following table summarizes the security considerations required by [[RFC3552]].

Attack In-Scope Susceptible Mitigations and Notes
Eavesdropping No No
  • Eavesdropping attacks are not applicable since the VDR is open by design.
Replay Yes No
  • Each Orb transaction represents a unique node in the transaction graph. As transactions are propagated in a gossip manner, servers should de-duplicate and not announce transactions multiple times to its followers.
  • If a transaction is received multiple times by an Orb server, the Orb server is not affected as transactions are simply announcements of immutable operations. Duplicated transactions have no effect and are ignored.
  • The Orb specification contains rules for choosing one active branch in the DID operation chain. Duplicated operations are detected by their position in the Sidetree commit/reveal chain. As Orb Servers only choose one active branch for each DID, duplicated operations have no effect and are ignored.
Message Insertion Yes No
  • Orb transactions contain immutable references to their parent transactions. Inserted messages have no effect and are ignored.
  • DID operation are chained using the Sidetree commit/reveal scheme. Inserted messages have no effect and are ignored.
Deletion Yes Mitigated
  • The Orb VDR is replicable across Orb Servers and CAS networks. However, if all copies of a VDR object are deleted, the associated DIDs become unresolvable.
  • Orb servers should use an appropriate backup strategy and replicate VDR objects from the rest of the ecosystem.
Modification Yes No
  • VDR objects are stored in content-addressable storage and are immutable by design.
  • The DID operation chain is tamper-resistant as it leverages the Sidetree commit/reveal scheme and signed operations.
  • As we anticipate that Orb Servers leverage caches to reduce processing burdens, those servers and caches should be appropriately secured.
Man-in-the-Middle Yes Mitigated
  • It is recommended to use TLS when calling the HTTP(S) Resolution API.
Denial of Service Yes Mitigated
  • An attacker may cause DoS issues by sending spam transaction announcements, in an effort to overwhelm storage and processing. Propagated transaction announcements are signed so that attacking systems can become ignored.

Privacy Considerations

Please refer to [[DID-CORE]] Privacy Considerations. The following table summarizes privacy considerations from [[RFC6973]] section 5:

Threat Comments
Surveillance Orb creates a decentralized and replicated VDR. Interconnected systems should be expected to observe DID update operations. For DID resolution, the opportunities for surveillance can be mitigated where an RP is running their own highly interconnected server. The usage of batches also brings advantages as operations from many DIDs become intermingled into files that are being requested. A Witness Server may also have surveillance opportunities when other systems inquire for the latest transaction for a particular DID. These opportunities are mitigated for highly interconnected servers that already have confidence in having the latest transactions, either from propagations or from monitoring and replicating Witness ledgers.
Stored data compromise Orb creates a decentralized and replicated VDR. Interconnected systems should be expected to observe DID update operations.
Unsolicited traffic The DID controller is responsible for choosing which endpoints to include within their DIDs.
Misattribution Orb uses self certifying DIDs - the DID controller is fully responsible for the changes to their DIDs.
Correlation Orb DID Suffixes are not inherently correlatable since the Unique Suffix is bound to the DID's initial state including inception keys. Please refer to [[DID-CORE]] for additional guidance.
Identification Orb DID Suffixes are not inherently identifiable since the Unique Suffix is bound to the DID's initial state including inception keys. Please refer to [[DID-CORE]] for additional guidance.
Secondary use The DID operations applied by a DID controller map to their usage of constructing a DID document.
Disclosure The DID controller is responsible for the operations applied to their DID documents.
Exclusion The DID method specifies a decentralized and replicable VDR. Increasing the connections between systems and using a replicable VDR strategy mitigates against exclusion.

As Orb enables Servers to interconnect as much as they choose and is agnostic to underlying ledgers, the availability of data can vary based on those choices.