Talking ActivityPub#
by xokomola
How can we make digital and human agents talk with each other? How can we build digital communities that can be linked together into a larger landscape? It is time to get familiar with the Fediverse and brush up on the ActivityPub protocols. All messages is described using the JSON-LD format for Linked Data.
In this article I will sketch out some scenarios translated into these protocols. Concepts referenced: Fediverse, ActivityPub, JSON-LD, Linked Data.
Introducing ActivityPub#
ActivityPub consists of two layers: a federation protocol to share information between information servers and a client-server protocol. The latter is discussed here.
The basic concepts use a familiar metaphor: email. A user is called an "actor". Actors communicate by sending messages to their outbox and receiving messages through their inbox. In this project we deal with both human and digital actors. The latter are, infamously, called bots.
The ActivityPub protocol provides a standardised wrapper for transporting messages (Activity Stream messages) between actors. These messages are not limited to just content but can also express certain activities. We first need to discuss Activity Streams when we want to start exchanging meaningful messages.
%% 2024-03-12-activitypub-talk 2024-03-12 15.21.59.excalidraw %%
Introducing the vocabulary#
An activity is a semantic description of an past, present or future action.
Here is a simple note object.
{
"@context": "https://www.w3.org/ns/activitystreams",
"summary": "A note",
"type": "Note",
"content": "My dog has fleas."
}
The @context
tells a processor of this JSON that this is actually a JSON-LD document that uses the Activity Streams vocabulary.
In order to send the note we have to add sender (attributedTo
) and recipient (to
).
{
"@context": "https://www.w3.org/ns/activitystreams",
"to": ["https://dverse.fontys.nl/ben"],
"attributedTo": "https://dverse.fontys.nl/marc",
"summary": "A note",
"type": "Note",
"content": "My dog has fleas."
}
YAML syntax
For the remainder of this article I will write examples as YAML and leave out the @context
line. This makes these JSON messages easier to read.
to:
- https://dverse.fontys.nl/ben
attributedTo: https://dverse.fontys.nl/marc
summary: A note
type: Note
content: My dog has fleas.
Describing objects is fine, but in order to share it with others we need to add some information and wrap it in a Create
activity.
type: Create
object:
type: Note
attributedTo: https://dverse.fontys.nl/marc
to:
- https://dverse.fontys.nl/ben
content: My dog has fleas.
Given the note above I now want to actually send it. For this I will POST it to some ActivityPub endpoint. This endpoint will notice the sender and recipient fields and create an ActivityPub wrapper around this message. It will also add IDs for traceability. This will then be send off to my outbox. When using an ActivityPub server you won't have to create this wrapper yourself.
type: Create
id: https://dverse.fontys.nl/marc/activity/12345
to: https://dverse.fontys.nl/ben
actor: https://dverse.fontys.nl/marc
object:
type: Note
id: ttps://dverse.fontys.nl/marc/note/12346
attributedTo: https://dverse.fontys.nl/marc
to:
- https://dverse.fontys.nl/ben
content: My dog has fleas.
Back to ActivityPub#
The server will take messages from every outbox on the server and deliver it to the recipients inboxes.
Chatty protocol
One of the issues that servers face is that when they get more users the amount of work it has to do to keep up with all the activities results in a lot of messages being sent around. Suppose that I have 100 followers. This means that a message will go to 100 people's inbox. When 100 users each with 100 followers send out a single message the server will have to deal with 100 x 100 actions. Of course this can be alleviated by smart programming. In fact, an ex-Twitter engineer, Nathan Marz, has recently created a proof of concept Mastodon clone using a data processing framework he developed during the last 10 years and that demonstrates that it could, in principle, scale to Twitter scale.
Actions#
On most social networks users can perform other actions besides sending notes back and forth. These actions spell out the types of actions that can be performed. Not all servers support all of these. In theory we may also extend the vocabulary by adding new types of actions. Also, when designing a different type of social network server you could, for example, consider to not provide Likes, to get rid of the competitive elements and make the network less addictive1
But for now we will consider only these standard activities as most other servers probably implement them.
- Create
-
Create the activity in the actor's inbox
- Update
-
Update an object based on its ID.
- Delete
-
Delete an object based on its ID.
- Follow
-
Generates an Accept or Reject depending if the other actor accepts the follow or not.
- Accept
-
Accepting a Follow object adds that user to my Following collection.
- Reject
-
Rejecting a Follow object with not add that user to my Following collection but it may result in some sort of notification.
- Add
-
Adds an object to a collection.
- Remove
-
Removes an object from a collection.
- Like
-
Increments the likes count by adding to the Likes collection.
- Announce
-
Increment the shares count by adding to the Shares collection (this is like sharing, reposting, boosting or retweeting).
- Undo
-
Undo the side effects of previous activities.
To be continued
@context
From here on I will leave the @context
out of the examples. We assume it's set to https://www.w3.org/ns/activitystreams
.
Questions and Answers#
When talking to chatbots it is often in the question and answer style. Let's represent that in ActivityPub.
id: http://dverse.fontys.nl/question/1
type: Question
content: I would like to build a robot to feed my cat. Should I use Arduino or Raspberry Pi?
oneOf:
- name: Arduino
- name: Raspberry Pi
There is no Answer activity type. If you would allow multiple answers you can replace oneOf
with anyOf
.
This is how an answer would look.
attributedTo: http://dverse.fontys.nl/bot/sally
inReplyTo: http://dverse.fontys.nl/question/1
name: Arduino
It is better to keep question and answers together though.
id: http://dverse.fontys.nl/question/1
type: Question
content: I would like to build a robot to feed my cat. Should I use Arduino or Raspberry Pi?
oneOf:
- Arduino
- Raspberry Pi
replies:
type: Collection
totalItems: 3
items:
- attributedTo: http://dverse.fontys.nl/bot/sally
inReplyTo: http://dverse.fontys.nl/question/1
name: Arduino
- attributedTo: http://dverse.fontys.nl/bot/joe
inReplyTo: http://dverse.fontys.nl/question/1
name: Arduino
- attributedTo: http://dverse.fontys.nl/bot/john
inReplyTo: http://dverse.fontys.nl/question/1
name: Raspberry Pi
result:
type: Note
content: 33% of the bots selected Rasberry Pi.
Note that some of these examples do have some redundancy. This is to allow unpacking and still containing a full object.
-
! How to visualise inboxes.
-
Email analogy
- HTTP verbs -> Activity types
Getting started with Python and ActivityPub#
Most of the code you need to write is working with JSON-LD messages and transforming them. Other than that you will need a basic ActivityPub library.
There are also ActivityPub server related repos that you can use to learn how to make a full, working server and a full-fledged server.
-
ex-OpenAI researcher and professor Kenneth Stanley recently started such a different type of Social Media Network called Maven. He deliberately tries to make it less addictive by letting people follow interesting content rather than other people. ↩