Guide for new ActivityPub implementers

nedjo
January 21, 2020

This is an informal guide to help new implementers get up to speed with the ActivityPub specification (published as a W3C Recommendation) and how to build projects with ActivityPub support.

:information_source:   Note: This is a Wiki Post that anyone can edit. Please help improve and extend this document !

Why choose ActivityPub?

Why might you choose to implement ActivityPub in your project? Possible reasons include:

Disclaimers

This guide includes links to many projects. The point of such links is not to recommend any specific project but to inform about the kinds of projects that would be useful to know about.

This guide is intended to expand on what’s in the relevant specifications. Those specs remain the primary sources and should be referred to during implementation.

Resources

If you’re used to specifications that contain a lot of jargon, you may be pleasantly surprised when you look at the ActivityPub specification. The authors put in a lot of effort to try to make the spec readable and easy to understand. A good starting place is the overview.

This guide is maintained by members of the SocialHub, a grassroots community of technologists and fedizens who together build the Fediverse ecosystem and evolve its standards. The community has a non-technical brainstorming area on Lemmy, called Fediverse Futures where you can gather feedback and elaborate ideas.

Specifications

As an ActivityPub implementer you may find the following documents to be useful:

Tutorials

Technical ActivityPub introductions

API documentation for specific implementations

What’s already out there?

As you start in, it’s useful to get a sense of what ActivityPub implementations and code projects already exist. Doing so will help you:

The delightful project maintains three up-to-date lists of Fediverse-related open source projects (these were formerly the ActivityPub Watchlists, migrated from Feneas when it shut down):

Additional places to look include:

Implementing ActivityPub

The ActivityPub, Activity Streams, and Activity Vocabulary specifications are the main places to look for details on what’s required to implement ActivityPub support. The notes here are intended to give pointers on areas not covered by the specs or where clarification or subsequent work is relevant.

ActivityPub objects and activities

While formally there are eight core object types defined in the Activity Vocabulary spec, with many “extended” types, it can help to think of these objects as falling into four main groups:

Implementing ActivityPub support is about letting actors like site members on your application or site - in the Fediverse, often called an instance - have back and forth exchanges with actors on other instances. As discussed in Episode 12: ActivityPub Part 1 of the LibreLounge podcast (15:26 - 20:24), ActivityPub structures these exchanges in a way that’s analogous to a simple subject-verb-object statement. In every message that’s sent server-to-server:

Somebody is doing something to some object. In ActivityPub terms, that’s an Actor is performing an Activity on an Object.

In ActivityPub, two servers are federated with each other when there is a relationship between one or more actors on each. A common relationship is following: an actor on one server follows an actor on another server. From there, a whole set of other activities are opened up, like when the followed actor posts a Note or Video, the follower’s account receives a notification (in the form of a Create activity).

Some data are tracked on both sides of the relationship. For example, if @chris@example1.org follows @georgie@example2.net, that relationship is tracked both as a record on example1.org (georgie is in chris’s following collection) and on example2.net (chris is in georgie’s followers collection).

ActivityPub URIs serve a dual role depending on the HTTP method used. Pretty much, a given URI such as the id URI of a Person or the URI of an actor’s inboxsupports two types of operations:

Note that instead of the other standard HTTP methods - PATCH, PUT, DELETE - there are different activity types (Create, Update, Delete).

The id of an object is a URI that can be “dereferenced”–that is, assuming appropriate access authorization, a GET request to the URI will look up and return the full object.

So URIs that are the id of a single object such as an Actor return data about that object. Other URIs, such as that of an actor’s outbox, can return a Collection of objects.

JSON-LD

If you’re developing for ActivityPub, you’re going to be dealing with JSON-LD, since that’s the format ActivityPub JSON is in. While JSON-LD can be handled as regular JSON, there are also specialized tools in JSON-LD that may be valuable to your application.

Expansion, compaction, flattening

One thing you’ll quickly notice when reading through code examples in the ActivityPub specifications is that the JSON-LD can be super variable in its format. Add to that the fact it will be coming from any number of different implementations and you’ve got a big challenge when it comes to parsing the incoming information.

So it’s worth noting that JSON-LD supports compacted and expanded formats as well as flattening of either. To avoid having to directly manage the inconsistencies of the compact format, you may want to standardize on expanded and flattened, then compact before returning. Flattening:

may drastically simplify the code required to process JSON-LD data in certain applications.

Discussion: More than JSON: ActivityPub and JSON-LD.

Conversion to and from RDF

If you’re working in an environment where there’s existing support for RDF, you might want to look at JSON-LD support for RDF serialization and deserialization.

JSON-LD libraries and packages

The JSON-LD specification is widely supported and you may find an implementation in your programming language that will provide basic functionality such as methods to expand, compact, and flatten. Packages include:

Validation

There’s an unofficial ActivityPubSchema that may be useful if you’re validating the JSON-LD of ActivityPub requests and responses.

The “missing” pieces

While the ActivityPub specification covers a lot, there are also areas that aren’t spelled out and left to implementers to decide. As Serge Wroclawski put it in an episode of the Libre Lounge podcast (1:38 - 1:52):

If you’re an implementer and you first look at ActivityPub, it seems like there’s a lot missing, and there absolutely is. And so what you have to do is you have to look at best practices at the time, which aren’t necessarily in the standard itself.

This section is about those current practices.

Discovery: Webfinger

How to look up actor accounts on another server?

In ActivityPub, an Actor such as a Person (often, a user with an account on a given site) is identified by an ID in the form of a URI. That URI could be what’s used to identify the actor on other ActivityPub sites. But URIs can be hard to work with and understand. A much more familiar format is user@domain.tld (or, by convention in the Fediverse, @user@domain.tld, to distinguish the handle from an email address).

The ActivityPub spec doesn’t cover profile discovery, that is, the ability to look up a handle like @user@domain.tld and find the URI for the account’s profile. However, per ActivityPub? Well, it’s not that bad:

Most current implementers have integrated Webfinger for that purpose, since users like to work with handles rather than URLs.

For example, Webfinger is implemented in Mastodon and is the recommended way to implement interoperability. See the Mastodon documentation on Webfinger and this tutorial.

Other resources as suggested in How to retrieve “user@server.tld” handle from actor’s URL:

The tutorial How to implement remote following for your ActivityPub project outlines how to use webfinger to help users follow local actors from remote ActivityPub applications (and vice-versa).

Authentication and authorization

ActivityPub doesn’t itself specify how to handle server to server user authentication, but it provides some hints in a wiki page, SocialCG/ActivityPub/Authentication Authorization. In your implementation you’ll probably find yourself implementing at least one of the solutions they point to.

Client to server

As noted in the SocialCG wiki:

ActivityPub clients authenticate against a server using OAuth 2.0 bearer tokens.

So if you’re building a client app, or if you’re building a server app that supports client connections, you’ll need to build in OAuth 2.0 support.

Note: There are a lot of intricacies to C2S authentication. See this AndStatus issue Basic implementation of “client to server” ActivityPub protocol (#499) for a detailed discussion with many cross-references.

Server to server
HTTP Signatures

HTTP Signatures is the approach taken in most current implementations.

Usually you’ll need to both:

The tutorial How to implement a basic ActivityPub server includes a good introduction to HTTP Signatures and how to use them, including how to add a publicKey attribute to an actor object.

Linked Data Signatures

Linked Data Signatures are recommended when:

it is important to be able to verify that the actors specified in the actor and attributedTo fields really did author the objects they are being claimed to have authored.

Information about the instance/node

ActivityPub is all about actors, activities, and objects, but doesn’t say anything about the instance itself–the site that’s hosting all this. Various approaches are filling this gap.

NodeInfo

There is a NodeInfo standard adopted by many ActivityPub implementations.

NodeInfo is:

an effort to create a standardized way of exposing metadata about a server running one of the distributed social networks. The two key goals are being able to get better insights into the user base of distributed social networking and the ability to build tools that allow users to choose the best fitting software and server for their needs.

There is an alternate NodeInfo2 standard.

NodeInfo2 is a fork of NodeInfo which was seen as too complex for the problem it is solving.

There has also been discussion about the possibility of ActivityPub-specific discovery.

Federation.md

Complementary to NodeInfo, some implementations have introduced a federation.md file to describe instance capabilities in a human readable way. See this discussion for more information. The federation.md files are a good way to get a sense of how different projects are using and adapting ActivityPub. Examples include:

Filtering out abuse and hate

ActivityPub provides a way to connect to other instances. But what if there are instances a particular site’s administrators don’t want to connect to? How to deal with the fact that the Fediverse is by default unfiltered and there are nodes that permit or actively encourage hate speech and abuse?

Background on the problem and proposed solutions:

A very basic approach is to enable whitelisting/blacklisting of ActivityPub domains so that an individual site’s admins can decide who to or not to federate with. That’s what’s supported in Mastodon.

Relevant background from the Mastodon project:

Extensions

While you may find the set of objects and properties spelled out in the Activity Streams specs is sufficient for your use case, in many cases it won’t be.

The upside of custom extensions is they meet a specific need. The downside is they limit interoperability with other ActivityPub implementations.

While you can definitely create your own extensions, it’s worth getting familiar with what other implementers have already done.

Frequently Asked Questions

Designed to address the most common questions, this section is a compendium of tips, suggestions, and ideas around specific implementation questions.

What do I need to present to my users?

For starters, if you’re building a server app, you’ll probably want to display the ID of the user’s ActivityPub profile, since this is the way that others can interact with them in the Fediverse. If you’re using Webfinger, you may also want to display the @username@example.com format so that users on other instances can use that to mention or look up your users.

What document object types to use?

Activity Vocabulary defines several types of objects, such as Note, Article, and Video. Currently there is some tension between interoperability, on the one hand, and adherence the spec, on the other. Some implementers are focusing on what’s currently supported in Mastodon, where a Note will be fully displayed (minus certain markup) while an Article, for example, will appear as a link.

How to handle public content?

ActivityPub content may be publicly addressed. It’s left to implementers to decide how to handle such content.

Questions to consider include:

What’s special about the sharedInbox?

Among other uses, a shared inbox can optionally be used to send a single post to multiple actors who would otherwise need to each be messaged separately. When you are sending an activity to remote instances, instead of sending one per Actor, you can instead iterate through all your actors and post to the list of unique sharedInbox URIs. The ActivityPub spec notes:

in this scenario, the remote/receiving server participates in determining targeting and performing delivery to specific inboxes.

Participates? How?

The idea here is that in theory a following relationship is tracked on both sides: as part of a followers collection associated with the actor on the originating server and as part of a followed collection associated with the actor on the receiving server. So the originating server doesn’t need to explicitly list recipients. Instead, the receiving server can take the ID of the activity’s actor, look up its own list of who’s following that actor, and deliver the object to each member of that list.

What data do I need to store locally?

Many web application developers will be used to a model where there’s a clear separation between (a) what’s created and managed within the application and (b) any data objects that are externally hosted. For example, in the most simple case, you might have your own User and Article entities which you store locally. If you also, say, display remote content from an RSS feed, you might might model and store that content and its authors quite differently than your own User and Article data.

With ActivityPub, there’s at least the potential to use the same models for both local and remote objects. For example, if a user on your site both (a) posts Article content of their own and (b) follows actors on remote instances and so gets content when those remote actors make Article posts, you may decide to handle both local and remote articles in the same local model.

How can I add ActivityPub support to my existing software project?

If you have an existing software and want to add ActivityPub support, it may help to look at what that’s looked like for projects that have already done it. Places to look include:

Improving ActivityPub

Aided by a culture of collaboration among Fediverse advocates and participants, there is ongoing work to improve both the ActivityPub specification and interoperability among different ActivityPub implementations.

Join the conversation

Initiatives like FediConf, Feneas , and the ActivityPub forum on Activitypub.Rocks are great places to connect with other implementers and help move things forward.

Areas of critique and debate

As you assess the work of implementing in ActivityPub it can help to be up on some of the areas of discussion within the community.

Decisions against adopting ActivityPub

The Diaspora project has effectively decided against integrating with ActivityPub, see this issue which includes some critiques of the protocol. See also ActivityPub - Final thoughts, one year later.

Private content and trust

At https://blog.dereferenced.org and elsewhere, Ariadne Conill has critiqued the ActivityPub specification from a trust and safety perspective. See ActivityPub: The “Worse Is Better” Approach to Federated Social Networking and subsequent posts.

Others have critiqued the ActivityPub federation model’s reliance on external sites maintaining privacy restrictions. One commenter noted:

For the sake of privacy and safety I think that private posts should still only be pushed in stub/notification form (mostly because I don’t trust most ActivityPub implementations to keep private stuff private)

Federation issues

From A People’s History of the Fediverse

ActivityPub didn’t solve the longstanding federation issues which had been known about since before its creation, such as nomadic identity, key based identity rather than domain based identity and the ability to easily migrate accounts between instances.

Developer vs. user terminology

While “ActivityPub” may mean something to people with a technical bent, it’s probably not the best way to convey to site members what your software is and does.

“Fediverse” is probably a bit better, in that at least if you look it up you’re slightly more likely to find something basically readable. Still, it’s worth looking around at how other software are handling the question of presenting the ideas of federation and ActivityPub to their users.

More information

See the accompanying guides: