Activity Anchors are based on a federated and replicated Verifiable Data Registry (VDR). The decentralized network consists of Activity Anchor Servers that write, monitor, witness, and propagate batches of document 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 Activity Anchor 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 document operations and by monitoring the applicable witness servers' ledgers. The Activity Anchor servers form a decentralized network without reliance on a common blockchain for coordination.

This document specifies a data model and rules for representing a graph of batches, the anchor server API, an [[[ACTIVITYSTREAMS-CORE]]] vocabulary and an [[[ACTIVITYPUB]]] profile for propagating batches, and a [[[RFC7033]]] profile for discovering endpoints.

Introduction

Our motivation for Activity Anchors is to enable many independent organizations to create Verifiable Data Registries (VDRs) that can become connected over time. In designing Activity Anchors, 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). Activity Anchors enables a federated, replicated and scalable VDR based on a decentralized network for propagating, witnessing and replicating changes to documents.

Server-to-Server APIs

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

Vocabulary

AnchorEvent

An operation batch is represented as an AnchorEvent. Hashlinks [[HASHLINK]] are used to link objects to the AnchorEvent. The AnchorEvent object contains an attachment property that represent objects related to the AnchorEvent.

The AnchorObject represents general content that is related to the AnchorEvent. The url property identifies the content. The contentObject property enables the content to be embedded within the AnchorObject as JSON.

The AnchorIndex object represents content that is intended to be consumed by an appropriate processor. The generator property enables the appropriate processor (and version) to be determined. The url property identifies the content. The metadata property identifies objects that the processor uses to consume the content. The primary purpose of metadata is to map the referenced content into the anchoring system. As an example, a processor can associate identifiers (within the referenced content) to prior anchor events.

Hashlinks may refer to the contentObject within an AnchorObject. Prior to creating the hash in the Hashlink, the contents of the contentObject MUST be canoncialized using the JSON Canonicalization Scheme (JCS) [[RFC8785]]. The Hashlink refers to the canonicalized contents of the contentObject.

            {
              "@context": [
                "https://www.w3.org/ns/activitystreams",
                "https://w3id.org/activityanchors/v1"
              ],
              "type": "AnchorEvent",
              "attributedTo": "ipns://k51qzi5uqu5dl3ua2aal8vdw82j4i8s112p495j1spfkd2blqygghwccsw1z0p",
              "published": "2021-01-27T09:30:00Z",
              "parent": [
                "hl:uEiAsiwjaXOYDmOHxmvDl3Mx0TfJ0uCar5YXqumjFJUNIBg:uoQ-CeEdodHRwczovL2V4YW1wbGUuY29tL2Nhcy91RWlBc2l3amFYT1lEbU9IeG12RGwzTXgwVGZKMHVDYXI1WVhxdW1qRkpVTklCZ3hCaXBmczovL2JhZmtyZWlibXJtZW51eGhnYW9tb2Q0bTI2ZHM1enRkdWp4emhqb2JndnBzeWwydjJuZGNza3EyaWF5",
                "hl:uEiAn3Y7USoP_lNVX-f0EEu1ajLymnqBJItiMARhKBzAKWg:uoQ-CeEdodHRwczovL2V4YW1wbGUuY29tL2Nhcy91RWlBbjNZN1VTb1BfbE5WWC1mMEVFdTFhakx5bW5xQkpJdGlNQVJoS0J6QUtXZ3hCaXBmczovL2JhZmtyZWliaDN3aG5pc3VkNzZrbmt2N3o3dWNiZjNrMnJzNmtuaHZhamVybnJkYWJkYmZhb21ha2xp"
              ],
              "attachment": [
                {
                  "type": "AnchorIndex",
                  "generator": "https://example.com/spec#v1",
                  "url": "hl:uEiD2k2kSGESB9e3UwwTOJ8WhqCeAT8fzKfQ9JzuGIYcHdg:uoQ-CeEdodHRwczovL2V4YW1wbGUuY29tL2Nhcy91RWlEMmsya1NHRVNCOWUzVXd3VE9KOFdocUNlQVQ4ZnpLZlE5Snp1R0lZY0hkZ3hCaXBmczovL2JhZmtyZWlod3NudXJlZ2NlcWgyNjN2Z2RhdGhjcHJuYnZhdHlhdDZoNm11N2lwamhob2RjZGJ5aG95",
                  "metadata": "hl:uEiCrkp_NVZQDeWB5LqC5jK9f2va2BkXk7ySMKNt0Jg85Pg"
                },
                {
                  "type": "AnchorObject",
                  "url": "hl:uEiCrkp_NVZQDeWB5LqC5jK9f2va2BkXk7ySMKNt0Jg85Pg",
                  "contentObject": {
                    "resources": [
                      {
                        "id": "urn:multihash:uEiDahaOGH-liLLdDtTxEAdc8i-cfCz-WUcQdRJheMVNn3A"
                      },
                      {
                        "id": "urn:multihash:uEiA329wd6Aj36YRmp7NGkeB5ADnVt8ARdMZMPzfXsjwTJA",
                        "previousAnchor": "hl:uEiAsiwjaXOYDmOHxmvDl3Mx0TfJ0uCar5YXqumjFJUNIBg"
                      },
                      {
                        "id": "urn:multihash:uEiARIc_M1ZE_CmP-xApv_UTqZPncE1xmY0ugAdELz0MCogo",
                        "previousAnchor": "hl:uEiAn3Y7USoP_lNVX-f0EEu1ajLymnqBJItiMARhKBzAKWg"
                      }
                    ]
                  }
                }
              ]
            }         
            
              hl:uEiAsiwjaXOYDmOHxmvDl3Mx0TfJ0uCar5YXqumjFJUNIBg:uoQ-CeEdodHRwczovL2V4YW1wbGUuY29tL2Nhcy91RWlBc2l3amFYT1lEbU9IeG12RGwzTXgwVGZKMHVDYXI1WVhxdW1qRkpVTklCZ3hCaXBmczovL2JhZmtyZWlibXJtZW51eGhnYW9tb2Q0bTI2ZHM1enRkdWp4emhqb2JndnBzeWwydjJuZGNza3EyaWF5",
            

The example hashlink includes links to `https://example.com/cas/uEiAsiwjaXOYDmOHxmvDl3Mx0TfJ0uCar5YXqumjFJUNIBg` and `ipfs://bafkreibmrmenuxhgaomod4m26ds5ztdujxzhjobgvpsyl2v2ndcskq2iay` to demonstrate that multiple sources and protocols can be used for each object.

TODO: Complete the description of anchor events.

AnchorCredential

A signed operation batch is represented as an AnchorCredential.

              {
                "@context": [
                  "https://www.w3.org/2018/credentials/v1",
                  "https://www.w3.org/ns/activitystreams",
                  "https://w3id.org/activityanchors/v1",
                  "https://w3id.org/jws/v1"
                ],
                "id": "http://sally.example.com/anchors/bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                "type": [
                  "VerifiableCredential",
                  "AnchorCredential"
                ],
                "issuer": "https://sally.example.com/services/anchor",
                "issuanceDate": "2021-01-27T09:30:10Z",
                "credentialSubject": {
                  "type": "AnchorEvent"
                },
                "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 anchor is embedded within an Activity during propagation flows. The Activity includes information about the CAS.
              {
                "@context": [
                  "https://www.w3.org/ns/activitystreams",
                  "https://w3id.org/activityanchors/v1"
                ],
                "id": "https://sally.example.com/services/anchor/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Create",
                "actor": "https://sally.example.com/services/anchor",
                "to": [
                  "https://sally.example.com/services/anchor/followers",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "published": "2021-01-27T09:30:10Z",
                "target": "hl:uEiCsFp-ft8tI1DFGbXs78tw-HS561mMPa3Z6GsGAHElrNQ:uoQ-CeE1odHRwczovL3NhbGx5LmV4YW1wbGUuY29tL2Nhcy91RWlDc0ZwLWZ0OHRJMURGR2JYczc4dHctSFM1NjFtTVBhM1o2R3NHQUhFbHJOUXhCaXBmczovL2JhZmtyZWlmbWMycHo3bjZsamRrZGNydG5wbTU3ZnhiNmR1eGh2dnRkYjV2eG02cTJ5Z2FieXNsbGd1",
                "object": {
                  "@context": [
                    "https://www.w3.org/2018/credentials/v1",
                    "https://www.w3.org/ns/activitystreams",
                    "https://w3id.org/activityanchors/v1"
                  ],
                  "id": "http://sally.example.com/anchors/642bab75-76a4-4b5f-98bb-794467ad5891",
                  "type": [
                    "VerifiableCredential",
                    "AnchorCredential"
                  ],
                  "issuer": "https://sally.example.com/services/anchor",
                  "issuanceDate": "2021-01-27T09:30:10Z",
                  "credentialSubject": {
                    "type": "AnchorEvent"
                  },
                  "proof": [{}]
                }
              }
            

ActivityPub

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

Actor (Discovery)

An Actor exposed by an Activity Anchor 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/anchor 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://w3id.org/activityanchors/v1"
                ],
                "id": "https://alice.example.com/services/anchor",
                "type": "Service",
                "publicKey": {
                  "id": "https://alice.example.com/services/anchor#main-key",
                  "owner": "https://alice.example.com/services/anchor",
                  "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki....."
                },
                "inbox": "https://alice.example.com/services/anchor/inbox",
                "outbox": "https://alice.example.com/services/anchor/outbox",
                "followers": "https://alice.example.com/services/anchor/followers",
                "following": "https://alice.example.com/services/anchor/following",
                "witnesses": "https://alice.example.com/services/anchor/witnesses",
                "witnessing": "https://alice.example.com/services/anchor/witnessing",
                "likes": "https://alice.example.com/services/anchor/likes",
                "liked": "https://alice.example.com/services/anchor/liked",
                "shares": "https://alice.example.com/services/anchor/shares"
              }
            

TODO: Review handling of publicKey.

Delivery

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

Activity Anchor Servers use HTTP Signatures [[HTTP-SIGNATURES]] when calling Server-to-Server APIs to create authenticated messages. The anchor 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 Activity Anchor 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 Activity Anchor Servers following the server.
  • Following: provides a collection (or ordered collection) of the other Activity Anchor Servers the server is following.
  • Witnesses: provides a collection (or ordered collection) of the other Activity Anchor Servers that act as witnesses.
  • Witnessing: provides a collection (or ordered collection) of the other Activity Anchor Servers the server is witnessing.
  • Likes: provides an ordered collection of the processed and replicated anchor activities sent by an Activity Anchor Server.
  • Liked: provides an ordered collection of the anchor activities processed and replicated by the Activity Anchor Server.
  • Shares: provides an ordered collection of the propagation (announce) activities sent by the Activity Anchor Server.

Create Activity

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

              POST /services/anchor/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://w3id.org/activityanchors/v1"
                ],
                "id": "https://sally.example.com/services/anchor/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Create",
                "actor": "https://sally.example.com/services/anchor",
                "to": [
                  "https://sally.example.com/services/anchor/followers",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "published": "2021-01-27T09:30:10Z",
                "object": {
                  "type": "AnchorReference",
                  "url": "hl:uEiCsFp-ft8tI1DFGbXs78tw-HS561mMPa3Z6GsGAHElrNQ:uoQ-CeE1odHRwczovL3NhbGx5LmV4YW1wbGUuY29tL2Nhcy91RWlDc0ZwLWZ0OHRJMURGR2JYczc4dHctSFM1NjFtTVBhM1o2R3NHQUhFbHJOUXhCaXBmczovL2JhZmtyZWlmbWMycHo3bjZsamRrZGNydG5wbTU3ZnhiNmR1eGh2dnRkYjV2eG02cTJ5Z2FieXNsbGd1"
                }
              }
            

On success, HTTP status code 200 is returned.

              POST /services/anchor/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://w3id.org/activityanchors/v1"
                ],
                "id": "https://sally.example.com/services/anchor/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Create",
                "actor": "https://sally.example.com/services/anchor",
                "to": [
                  "https://sally.example.com/services/anchor/followers",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "published": "2021-01-27T09:30:10Z",
                "object": {
                  "type": "AnchorReference",
                  "url": "hl:uEiCsFp-ft8tI1DFGbXs78tw-HS561mMPa3Z6GsGAHElrNQ:uoQ-CeE1odHRwczovL3NhbGx5LmV4YW1wbGUuY29tL2Nhcy91RWlDc0ZwLWZ0OHRJMURGR2JYczc4dHctSFM1NjFtTVBhM1o2R3NHQUhFbHJOUXhCaXBmczovL2JhZmtyZWlmbWMycHo3bjZsamRrZGNydG5wbTU3ZnhiNmR1eGh2dnRkYjV2eG02cTJ5Z2FieXNsbGd1"
                  "attachment": [{
                    "@context": [
                      "https://www.w3.org/2018/credentials/v1",
                      "https://www.w3.org/ns/activitystreams",
                      "https://w3id.org/activityanchors/v1"
                    ],
                    "id": "http://sally.example.com/anchors/bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                    "type": [
                      "VerifiableCredential",
                      "AnchorCredential"
                    ],
                    "issuer": "https://sally.example.com/services/anchor",
                    "issuanceDate": "2021-01-27T09:30:10Z",
                    "credentialSubject": {
                      "type": "AnchorEvent"
                    },
                    "proof": [{}]
                  }
                }
              }
            

On success, HTTP status code 200 is returned.

Announce Activity

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

              POST /services/anchor/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://w3id.org/activityanchors/v1"
                ],
                "id": "https://sally.example.com/services/anchor/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Announce",
                "actor": "https://sally.example.com/services/anchor",
                "to": [
                  "https://sally.example.com/services/anchor/followers",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "published": "2021-01-27T09:30:10Z",
                "object": {
                  "type": "Collection",
                  "totalItems": 1,
                  "items": [
                    {
                      "type": "AnchorReference",
                      "url": "hl:uEiCsFp-ft8tI1DFGbXs78tw-HS561mMPa3Z6GsGAHElrNQ:uoQ-CeE1odHRwczovL3NhbGx5LmV4YW1wbGUuY29tL2Nhcy91RWlDc0ZwLWZ0OHRJMURGR2JYczc4dHctSFM1NjFtTVBhM1o2R3NHQUhFbHJOUXhCaXBmczovL2JhZmtyZWlmbWMycHo3bjZsamRrZGNydG5wbTU3ZnhiNmR1eGh2dnRkYjV2eG02cTJ5Z2FieXNsbGd1"
                    }
                  ]
                }
              }
            

On success, HTTP status code 200 is returned.

              POST /services/anchor/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://w3id.org/activityanchors/v1"
                ],
                "id": "https://sally.example.com/services/anchor/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Announce",
                "actor": "https://sally.example.com/services/anchor",
                "to": [
                  "https://sally.example.com/services/anchor/followers",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "published": "2021-01-27T09:30:10Z",
                "object": {
                  "type": "Collection",
                  "totalItems": 1,
                  "items": [
                    {
                      "type": "AnchorReference",
                      "url": "hl:uEiCsFp-ft8tI1DFGbXs78tw-HS561mMPa3Z6GsGAHElrNQ:uoQ-CeE1odHRwczovL3NhbGx5LmV4YW1wbGUuY29tL2Nhcy91RWlDc0ZwLWZ0OHRJMURGR2JYczc4dHctSFM1NjFtTVBhM1o2R3NHQUhFbHJOUXhCaXBmczovL2JhZmtyZWlmbWMycHo3bjZsamRrZGNydG5wbTU3ZnhiNmR1eGh2dnRkYjV2eG02cTJ5Z2FieXNsbGd1",
                      "attachment": [{
                        "@context": [
                          "https://www.w3.org/2018/credentials/v1",
                          "https://www.w3.org/ns/activitystreams",
                          "https://w3id.org/activityanchors/v1"
                        ],
                        "id": "http://sally.example.com/anchors/bafkreihwsnuregceqh263vgdathcprnbvatyat6h6mu7ipjhhodcdbyhoy",
                        "type": [
                          "VerifiableCredential",
                          "AnchorCredential"
                        ],
                        "issuer": "https://sally.example.com/services/anchor",
                        "issuanceDate": "2021-01-27T09:30:10Z",
                        "credentialSubject": {
                          "type": "AnchorEvent"
                        },
                        "proof": [{}]
                      }]
                    }
                  ]
                }
              }
            

On success, HTTP status code 200 is returned.

Follow Activity

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

                POST /services/anchor/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/anchor",
                  "actor": "https://sally.example.com/services/anchor",
                  "object": "https://alice.example.com/services/anchor"
                }
              

On success, HTTP status code 200 is returned.

Accept Follow Activity

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

                POST /services/anchor/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/anchor",
                  "type": "Accept",
                  "actor": "https://alice.example.com/services/anchor",
                  "object": {
                      "id": "https://sally.example.com/activities/9052018f-9530-4aef-8a46-40bde9445a25",
                      "to": "https://alice.example.com/services/anchor",
                      "type": "Follow",
                      "actor": "https://sally.example.com/services/anchor",
                      "object": "https://alice.example.com/services/anchor"
                    }
                }
              

On success, HTTP status code 200 is returned.

Reject Follow Activity

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

                POST /services/anchor/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/anchor",
                  "type": "Reject",
                  "actor": "https://alice.example.com/services/anchor",
                  "object": {
                      "id": "https://sally.example.com/activities/9052018f-9530-4aef-8a46-40bde9445a25",
                      "to": "https://alice.example.com/services/anchor",
                      "type": "Follow",
                      "actor": "https://sally.example.com/services/anchor",
                      "object": "https://alice.example.com/services/anchor"
                    }
                }
              

On success, HTTP status code 200 is returned.

Undo Follow Activity

An Activity Anchor Server requests to stop following by sending an HTTP POST of a Undo activity to an Activity Anchor Server's ActivityPub inbox endpoint. Refer to ActivityPub for guidance.

                POST /services/anchor/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://sally.example.com/activities/e017e49a-6256-44a0-855d-5dc304ae67f7",
                  "to": "https://alice.example.com/services/anchor",
                  "type": "Undo",
                  "actor": "https://sally.example.com/services/anchor",
                  "object": {
                      "id": "https://sally.example.com/activities/9052018f-9530-4aef-8a46-40bde9445a25",
                      "to": "https://alice.example.com/services/anchor",
                      "type": "Follow",
                      "actor": "https://sally.example.com/services/anchor",
                      "object": "https://alice.example.com/services/anchor"
                    }
                }
              

On success, HTTP status code 200 is returned.

Like Activity

An Activity Anchor Server MAY notify other actors that it has successfully processed an anchor event using the Like activity. In particular, it is useful for the originator of the anchor to discover other actors that are processing the events it is producing. Processing an anchor event MAY also result in the creation of an additional reference to the anchor event due to replication. The result property MAY be included to allow other actors to discover the additional anchor reference. Note: the new anchor reference MAY contain multiple links to the anchor event (representing multiple replication sources).

              POST /services/anchor/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://w3id.org/activityanchors/v1"
                ],
                "id": "https://sally.example.com/services/anchor/activities/d2f4b508-021c-48a2-ad08-8949970366b2",
                "type": "Like",
                "actor": "https://sally.example.com/services/anchor",
                "to": [
                  "https://alice.example.com/services/anchor",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "published": "2021-01-27T09:30:10Z",
                "object": {
                  "type": "AnchorReference",
                  "url": "hl:uEiCsFp-ft8tI1DFGbXs78tw-HS561mMPa3Z6GsGAHElrNQ:uoQ-CeE1odHRwczovL3NhbGx5LmV4YW1wbGUuY29tL2Nhcy91RWlDc0ZwLWZ0OHRJMURGR2JYczc4dHctSFM1NjFtTVBhM1o2R3NHQUhFbHJOUXhCaXBmczovL2JhZmtyZWlmbWMycHo3bjZsamRrZGNydG5wbTU3ZnhiNmR1eGh2dnRkYjV2eG02cTJ5Z2FieXNsbGd1"
                },
                "result": {
                  "type": "AnchorReference",
                  "url": "hl:uEiCsFp-ft8tI1DFGbXs78tw-HS561mMPa3Z6GsGAHElrNQ:uoQ-BeDhodHRwczovL2V4YW1wbGUuY29tL2NmMTQ5YTY4LTA4NTYtNDMwNC1hOWVjLTM0NzU2NzU1NDE2Yw"
                }
              }
            

On success, HTTP status code 200 is returned.

Undo Like Activity

An Activity Anchor Server MAY notify other actors that it no longer uses an anchor (or stops replicating the anchor) that it previously liked. Refer to ActivityPub for guidance.

              POST /services/anchor/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://w3id.org/activityanchors/v1"
                ],
                "id": "https://sally.example.com/activities/e017e49a-6256-44a0-855d-5dc304ae67f7",
                "to": [
                  "https://alice.example.com/services/anchor",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "type": "Undo",
                "actor": "https://sally.example.com/services/anchor",
                "object": {
                  "id": "https://sally.example.com/services/anchor/activities/d2f4b508-021c-48a2-ad08-8949970366b2",
                  "type": "Like",
                  "actor": "https://sally.example.com/services/anchor",
                  "to": [
                    "https://alice.example.com/services/anchor",
                    "https://www.w3.org/ns/activitystreams#Public"
                  ],
                  "published": "2021-01-27T09:30:10Z",
                  "object": {
                    "type": "AnchorReference",
                    "url": "hl:uEiCsFp-ft8tI1DFGbXs78tw-HS561mMPa3Z6GsGAHElrNQ:uoQ-CeE1odHRwczovL3NhbGx5LmV4YW1wbGUuY29tL2Nhcy91RWlDc0ZwLWZ0OHRJMURGR2JYczc4dHctSFM1NjFtTVBhM1o2R3NHQUhFbHJOUXhCaXBmczovL2JhZmtyZWlmbWMycHo3bjZsamRrZGNydG5wbTU3ZnhiNmR1eGh2dnRkYjV2eG02cTJ5Z2FieXNsbGd1"
                  },
                  "result": {
                    "type": "AnchorReference",
                    "url": "hl:uEiCsFp-ft8tI1DFGbXs78tw-HS561mMPa3Z6GsGAHElrNQ:uoQ-BeDhodHRwczovL2V4YW1wbGUuY29tL2NmMTQ5YTY4LTA4NTYtNDMwNC1hOWVjLTM0NzU2NzU1NDE2Yw"
                  }
                }
              }
            

On success, HTTP status code 200 is returned.

Witness ActivityPub

Invite Witness Activity

An Activity Anchor Server invites another Activity Anchor Server to be a witness by performing an HTTP POST of an Invite activity containing a witnesses object to its ActivityPub inbox endpoint.

                POST /services/anchor/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://w3id.org/activityanchors/v1"
                  ],
                  "id": "https://sally.example.com/activities/7052018f-9530-3aef-8a46-20bde9445a21",
                  "to": "https://alice.example.com/services/anchor",
                  "type": "Invite",
                  "actor": "https://sally.example.com/services/anchor",
                  "object": "https://w3id.org/activityanchors#AnchorWitness",
                  "target": "https://alice.example.com/services/anchor"
                }
              

On success, HTTP status code 200 is returned.

Accept Invite Witness Activity

An Activity Anchor Server accepts an Invite Witness request by sending an HTTP POST of a Accept activity to the requesting Activity Anchor Server's ActivityPub inbox endpoint. Upon receiving this activity, the requesting Activity Anchor Server adds the actor to its collection of 'witnesses'. Refer to ActivityPub for guidance.

                POST /services/anchor/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",
                    "https://w3id.org/activityanchors/v1"
                  ],
                  "id": "https://alice.example.com/activities/f017e49a-7256-44a0-255d-3dc304ae67f7",
                  "to": "https://sally.example.com/services/anchor",
                  "type": "Accept",
                  "actor": "https://alice.example.com/services/anchor",
                  "object": {
                    "type": "Invite",
                    "id": "https://sally.example.com/activities/7052018f-9530-3aef-8a46-20bde9445a21",
                    "actor": "https://sally.example.com/services/anchor",
                    "object": "https://w3id.org/activityanchors#AnchorWitness",
                    "target": "https://alice.example.com/services/anchor"
                  }
                }
              

On success, HTTP status code 200 is returned.

Reject Invite Witness Activity

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

                POST /services/anchor/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",
                    "https://w3id.org/activityanchors/v1"
                  ],
                  "id": "https://alice.example.com/activities/b013e49a-6256-24a0-855d-1dc304ar67f7",
                  "to": "https://sally.example.com/services/anchor",
                  "type": "Reject",
                  "actor": "https://alice.example.com/services/anchor",
                  "object": {
                    "type": "Invite",
                    "id": "https://sally.example.com/activities/7052018f-9530-3aef-8a46-20bde9445a21",
                    "actor": "https://sally.example.com/services/anchor",
                    "object": "https://w3id.org/activityanchors#AnchorWitness",
                    "target": "https://alice.example.com/services/anchor"
                  }
                }
              

On success, HTTP status code 200 is returned.

Undo Invite Witness Activity

An Activity Anchor Server notifies a Witness that they have been removed by sending an HTTP POST of a Undo activity to an Activity Anchor Server's ActivityPub inbox endpoint. Refer to ActivityPub for guidance.

                POST /services/anchor/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",
                    "https://w3id.org/activityanchors/v1"
                  ],
                  "id": "https://sally.example.com/activities/b013e49a-6256-24a0-855d-1dc304ar67f7",
                  "to": "https://alice.example.com/services/anchor",
                  "type": "Undo",
                  "actor": "https://sally.example.com/services/anchor",
                  "object": {
                    "type": "Invite",
                    "id": "https://sally.example.com/activities/7052018f-9530-3aef-8a46-20bde9445a21",
                    "actor": "https://sally.example.com/services/anchor",
                    "object": "https://w3id.org/activityanchors#AnchorWitness",
                    "target": "https://alice.example.com/services/anchor"
                  }
                }
              

On success, HTTP status code 200 is returned.

Offer Activity

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

              POST /services/anchor/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/anchor/activities/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
                "type": "Offer",
                "actor": "https://sally.example.com/services/anchor",
                "to": [
                  "https://sally.example.com/services/anchor/witnesses",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "target": "https://w3id.org/activityanchors#AnchorWitness",
                "startTime": "2021-01-27T09:30:10Z",
                "endTime": "2021-01-27T09:31:10Z",
                "object": {
                  "@context": [
                    "https://www.w3.org/2018/credentials/v1",
                    "https://www.w3.org/ns/activitystreams",
                    "https://w3id.org/activityanchors/v1"
                  ],
                  "id": "http://sally.example.com/anchors/ed7c2d19-be27-44d4-b393-d3ec9775a1d1",
                  "type": [
                    "VerifiableCredential",
                    "AnchorCredential"
                  ],
                  "issuer": "https://sally.example.com/services/anchor",
                  "issuanceDate": "2021-01-27T09:30:10Z",
                  "credentialSubject": {
                    "type": "AnchorEvent"
                  },
                  "proof": {}
                }
              }
            

Accept Anchor Activity

A Witness promises to include an anchor event into their ledger using a Accept 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 anchor activity will become included into their ledger using startTime and endTime fields.

              POST /services/anchor/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",
                  "https://w3id.org/activityanchors/v1"
                ],
                "id": "https://witness1.example.com/services/anchor/activities/87bcd005-abb6-433d-a889-18bc1ce84988",
                "type": "Accept",
                "actor": "https://witness1.example.com/services/anchor",
                "to": [
                  "https://sally.example.com/services/anchor",
                  "https://www.w3.org/ns/activitystreams#Public"
                ],
                "object": {
                  "id": "https://sally.example.com/services/anchor/activities/9052018f-9530-4aef-8a46-40bde9445a25",
                  "to": "https://witness1.example.com/services/anchor",
                  "type": "Offer",
                  "actor": "https://sally.example.com/services/anchor",
                  "target": "https://w3id.org/activityanchors#AnchorWitness"
                },
                "result": {
                    "type": "AnchorReceipt",
                    "inReplyTo": "http://sally.example.com/anchors/ed7c2d19-be27-44d4-b393-d3ec9775a1d1",
                    "startTime": "2021-01-27T09:30:15Z",
                    "endTime": "2021-01-27T09:40:15Z",
                    "attachment": [{
                      "@context": [
                          "https://w3id.org/activityanchors/v1",
                          "https://w3id.org/jws/v1"
                      ],    
                      "type": "AnchorProof",
                      "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..."  
                        }
                    }]
                }
              }
            

The Writer Activity Anchor 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 .

Determine if it makes sense to also include log-specific attachments such as signed certificate timestamps.

Reject Anchor Activity

TODO: Provide description and example for Reject.

WebFinger

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

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

            GET /.well-known/webfinger?resource=https%3A%2F%2Falice.example.com%2Fservices%2Fanchor 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/anchor",
              "aliases": [
                "https://alice.example.com/@anchor",
                "https://alice.example.com/services/anchor"
              ],
              "links": [
                {
                  "rel": "self",
                  "type": "application/activity+json",
                  "href": "https://alice.example.com/services/anchor"
                }
              ]
            }
          

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

            GET /.well-known/webfinger?resource=acct%anchor%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%2Fanchor activitys%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/anchors/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y",
              "links": [
                {
                  "rel": "self",
                  "type": "application/ld+json",
                  "href": "https://sally.example.com/anchors/bafkreiatkubvbkdidscmqynkyls3iqawdqvthi7e6mbky2amuw3inxsi3y"
                },
                {
                  "rel": "working-copy",
                  "type": "application/ld+json",
                  "href": "https://sally.example.com/cas/uEiCsFp-ft8tI1DFGbXs78tw-HS561mMPa3Z6GsGAHElrNQ"
                },
                {
                  "rel": "working-copy",
                  "type": "application/ld+json",
                  "href": "https://replica.example.com/cas/uEiCsFp-ft8tI1DFGbXs78tw-HS561mMPa3Z6GsGAHElrNQ"
                }
              ]
            }
          

Review the appropriate link relations in the above example.