This wiki post is a place for collaboratively drafting ActivityPub documentation with the hope that this material may be incorporated into proposed improvements to the activitypub.rocks site, see the conversation about the new main page and the demonstration .
About this guide
This is an informal guide to help new implementers get up to speed with the ActivityPub specification and how to built projects with ActivityPub support.
Why choose ActivityPub?
Why might you choose to implement ActivityPub in your project? Possible reasons include:
- “to move beyond the silos of social media sites run by individual companies” (ActivityPub as it has been understood)
- Interoperability with other software supporting the same protocols.
- Ability to build off existing implementations.
- Collaboration opportunities.
This guide includes lots of links to various projects. The point is not “this is the best implementation” so much as “here’s some stuff that could be useful”.
This guide is intended to supplement and expand on what’s in the relevant specs. Those specs remain the primary sources and will cover a lot that isn’t covered here.
Before deep-diving into technical details it is recommended to read An ActivityPub Philosophy which places the ActivityPub standard in its proper context, and helps taking the proper approach when building extensions.
If you’re used to specifications that are written in obscure technospeak, 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.
Sooner or later as an ActivityPub implementer you’ll find yourself spending some time with many or most of the relevant specifications.
- Specification for ActivityPub
- Specification for ActivityStreams, the basis of ActivityPub.
- Specification for Activity Vocabulary, which details “extended types” for ActivityStreams actors and object such as Person and Note.
- Some work has been done towards Litepub, a set of protocols extending ActivityPub. At time of writing (January 2020), that work appears to be on pause.
- ActivityPub and its two related specs, Activity Streams and Activity Vocabulary, all structure their data in JSON-LD. The JSON-LD specification is a hefty one and if you’re new to the format you may want to start with introductory material, such as:
- How to implement a basic ActivityPub server (Mastodon)
- Decentralizing Social Interactions with ActivityPub
- A Highly Opinionated Guide to Learning About ActivityPub
- Video (in Spanish), Mini implementación de ActivityPub en Ruby
Technical ActivityPub introductions
- LibreLounge podcast
- ActivityPub as it has been understood–includes lots of relevant links.
- ActivityPub in Pleroma.
API documentation for specific implementations
- PeerTube ActivityPub API.
- FunkWhale Federation
- What is ActivityPub? (MoodleNet)
- Mastodon ActivityPub API
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
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)
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.
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:
- Identify opportunities to collaborate and combine efforts.
- Identify examples and models to work from.
- See how your project relates to others.
- Connect with the ActivityPub community.
A wiki page on the Fediverse.Party site, watchlist for ActivityPub apps, contains one of the most extensive listings of ActivityPub projects. See in particular the developer tools section for lots of packages, libraries, and other software useful to ActivityPub implementers.
Additional places to look include:
- The Software category and Programming category on the ActivityPub SocialHub forum.
- A web spreadsheet of AP apps and their characteristics by @mayel from Social.coop.
- Fediverse.Party, which lists Fediverse projects including ones that support ActivityPub. You can find information in two places:
- The home page has links to a curated list of Fediverse projects. Click on each to find very detailed information on the projects, including the languages they’re written in.
- A list of more apps has more basic information on a bunch of other projects. On this page you can filter to see only apps that support ActivityPub.
- The various Fediverse aggregators, some of which include information on the software running each instance.
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
Announce. An activity is an action undertaken by an actor.
Actor objects, like a
Group. Actors undertake activities like following or creating content.
Object (and link) types such as a
Video. These objects types usually represent content of some sort.
Collectionobjects. These are groupings or listings of multiple other objects, such as a list of followers of a particular person or a list of public posts.
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
Video, the follower’s account receives a notification (in the form of a
Some data are tracked on both sides of the relationship. For example, if
@firstname.lastname@example.org, 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:
- GET fetches an object or collection of objects.
- POST receives an activity in JSON-LD format.
Note that instead of the other standard HTTP methods - PATCH, PUT, DELETE - there are different activity types (
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.
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
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:
There’s an unofficial ActivityPubSchema that may be useful if you’re validating the JSON-LD of ActivityPub requests and responses.
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.
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.
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
email@example.com (or, by convention in the Fediverse,
@firstname.lastname@example.org, 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
@email@example.com 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.
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
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 is the approach taken in most current implementations.
Usually you’ll need to both:
- Support HTTP signature verification from remote instances.
- Verify HTTP signatures on incoming activities.
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
it is important to be able to verify that the actors specified in the
attributedTofields 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.
There is a NodeInfo standard adopted by many ActivityPub implementations.
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.
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:
- How the biggest decentralized social network is dealing with its Nazi problem
- Statement on Gab’s fork of Mastodon
- Documentation on server-wide moderation
- List of instances blocked by mastodon.social
- Mastondon instance with a forked codebase allowing whitelisting of domains rather than the default blacklist
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.
- Mastodon uses an extensive set of non-ActivityPub extensions.
Frequently asked questions, tips, tricks
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
@firstname.lastname@example.org 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
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:
- Should any locally generated public content be posted to all known
- If you’ve implemented a
sharedInbox, what if anything do you want to do with publicly addressed content that arrives there?
What’s special about the
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.
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
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
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 to it, it may help to look at what that’s looked like for projects that have already done it. Places to look include:
- The issue to add ActivityPub support to Mastodon.
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.
There are initiatives towards complimentary standards that could help address issues left unresolved in ActivityPub.
brings secure storage foundations to the modern web. It allows users and application developers to reason about secure, private data online or offline.
Datashards might help with ActivityPub issues like account migration.
Join the conversation
See the accompanying guides: