What is ActivityPub?

This was my first question when I joined to MoodleNet team. Now, after studying it deeply for a few weeks, I feel able to give a proper answer. So I thought of writing a blog post, with my goal being to offer a nice introduction to ActivityPub for technical people. If you’re interested in my more general impressions on the fediverse and our approach to building a federated app, I recommend you to read my previous blog post about our goals.

ActivityPub

ActivityPub is a protocol for decentralized social networks, which can also be extended to create all kinds of federated apps. So if you are thinking of building one it is very interesting to know how this standard works. It is important to notice it has two different parts:

Client to Server API Server to Server API A service can decide if it wants to implement only one or both of them. This decision just depends on the needs of the project. If you’re a frontend developer, you might use the Client to Server API. You will be focused on the Server to Server API when you only care about the federation part though.

ActivityStreams 2.0

The ActivityPub message format is JSON-LD, which can be used just like regular JSON and we’ll do this for this blog post. The vocabulary used in this protocol is defined by ActivityStreams 2.0 and ActivityPub just adds a couple of requirements more on top of it. In this vocabulary only exists two kind of entities: Link and Object. Here we will focus on Object and its subtypes. An Object has several fields and only two of them are required by ActivityPub: id and type. The most important properties are:

type: which defines the type of object. It can be a string or an array if the object implements more than a type. This is mandatory in ActivityPub. id: a unique identifier for the object. For ActivityPub this field should be a URL. The interesting part is that you can retrieve the full object information making a GET request to this URL with the ActivityStreams accept header. This allows us to send just the id instead the full object information in many cases. content: The content or textual representation of the Object, usually an HTML string. name: A simple, human-readable, plain-text name for the object. summary: A natural language summary of the object encoded as HTML. image: A link to an image that represents an object. @context: This is the only necessary property to accomplish with JSON-LD specification. In our examples we just set to “https://www.w3.org/ns/activitystreams". Natural Language Values So any object can have those fields. It is interesting that any field likely to be translated, like content, name or summary, can be renamed to a “map variant” to include different languages, ie:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Note",
  "id": "https://mycoolserver.com/notes/1",
  "content": "This is a translated note"
}

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Note",
  "id": "https://mycoolserver.com/notes/1",
  "contentMap": {
     "es": "Esto es una nota traducida",
     "en": "This is a translated note"
  }
}

“Data Container” Objects You may have noticed that the type of the previous object is “Note” instead of “Object”. A Note is a subtype of object defined by ActivityStreams too, and it is used to represent any short written work. A subtype means that share the same fields, but it may define more if needed.

There are some more defined types like Article, Image, Page, Document, Video, and more. While it may feel strange to not see something like comment or similar, ActivityStreams is very generic and we have to keep this in mind. The recommendation is to use Note because it “Represents a short written work”. Those objects may be considerated “data container” objects. They don’t define more fields and they are used to transmit information.

Activity In addition to those “data container” objects, ActivityStream defines Activity objects. The type of those objects may be more intuitive:

Create Delete Update Add Remove Join Follow etc They are the type of actions that you can expect from a social network. Special attention should be paid to the Announce activity, as it used to call attention to particular objects or notifications. It can be understood like a “retweet” on Twitter, or a simple way to share an already created object in your social network.

Activities may have 6 additional properties compared to a simple object:

object actor target origin result instrument Of course, not all of them make sense in every type of activities, so they are optional. So imagine you want to represent that Luke created the previous note, you can write:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Create",
  "id": "https://mycoolserver.com/activity/1",
  "actor": "https://mycoolserver.com/actor/luke",
  "object": "https://mycoolserver.com/note/1"
}

Here, you only used actor and object. The more common ones. You don’t need to use the rest of them if they do not make sense in your activity.

One important thing to keep in mind is that the ids of the objects will not always appear, and the complete specification of them may appear instead. The previous example could be written in the following way, both being completely valid:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Create",
  "id": "https://mycoolserver.com/activity/1",
  "actor": {
     "type": "Person",
     "id": "https://mycoolserver.com/actor/luke",
     "name": "Luke"
  }
  "object": {
    "type": "Note",
    "id": "https://mycoolserver.com/notes/1",
    "contentMap": {
       "es": "Esto es una nota traducida",
     "en": "This is a translated note"
  }
}

Actor If you take a close look at the previous example you will see that Luke is a Person, this is a type of Object we haven’t yet mentioned. Person is one of the 5 types of actors that ActivityStreams defines:

Person Group Organization Application Service Actors are objects that may execute the different activities, this means they can be used as the actor field on an Activity. An actor has two mandatory properties as well:

inbox outbox The previous two properties are mandatory for any actor. The following properties are not but they are recommended to be implemented:

following follower liked Collection All these five properties are from a core type we haven’t talked about yet: Collections. As you’d expect, a Collection is simply a group of objects. They also have additional properties, ie: totalItems with the number of elements in the Collection or items where you’ll find the objects in the collection.

So this means that the inbox of an actor is just a Collection containing all the activities received by the actor. The outbox contains all the activities sent by the actor. Any time an actor likes an object, this is added to their liked collection.

Obviously, after a while, those collections cannot be managed with just an array. It will be impractical to manage all the followers of a popular person in a simple array. For this reason, we have the last core type of ActivityStreams: the CollectionPage which should be used to paginate a full Collection.

Core types So those are the 5 core object types that ActivityStreams defines:

Object Actor Activity Collection CollectionPage All of them are also an Object, so they share most of the fields. Once you understand this vocabulary it becomes easier to work with the ActivityPub protocol.

Targeting A big difference from working with other private social networks is the “Audience Targeting”. All objects can use 5 optional fields for this:

to bto cc bcc audience