Robin Berjon

¿Por qué no los dos?

ActivityPub Over ATProto

A Gustave Doré chimeric creature, sort of dragon-like with a woman on its back, in green hues.

There's a joke in movement politics that if you have one Trotskyist (and you can replace "Trotskyist" with any number of other denominations), you have one Party; if you have two Trotskyists, you have two Factions; and if you have three Trotskyists, you have a party split.

It feels like that's the vibe the open social media space is in nowadays. People feel that they need to pick a side and defend it, preferring to eat one another's face rather than make sure the leopards don't. I guess in a way that's very social media. It's also very stupid.

I would like to relieve the tedium of this posturing by making a suggestion that everyone can hate together. Or, more seriously, I would like to go through a short exercise to demonstrate that it might not be the dumbest thing to pay a little bit more attention to interesting architectural details. This can set the stage for a broader discussion of social media on the Web.

This is just a sketch and it has issues. I initially wanted to prototype this but the situation at work is such that I have precious little bandwidth for prototyping (so I'm mostly dumping my notes onto this blog). Think of it as a design provocation, and the provocation is this:

With relatively little work, we could run ActivityPub atop an AT Protocol PDS.

I won't try to convince you that we could do that without making some modifications to the current state of either or both, but I would like to draw your attention to why this is something worth thinking about and, with any luck, convince you that we should be moving in that direction (even if not necessarily this exact combo). Note that I find it hard to believe that I would be the first person to think of this, but I failed to find anything. Maybe you think that I ought to be the only person to ever have this idea.

The first thing to understand is that the AT Protocol is not Bluesky. ATProto is intended to be a generic toolbox for building social media applications, and it arguably extends (or can readily be extended) beyond that into supporting general infrastructure for a Personal Data Server (PDS). In fact, the ATProto architecture is explicitly described in terms of a PDS and of that PDS being a user agent. While Bluesky have been clear that they intend to keep control over the app.bsky API routes that implement Bluesky atop ATProto, they have also been clear that the com.atproto routes are intended to be an open standard. (This has yet to get the governance to guarantee it, but I'm not in the business of asking for permission anyway.)

The reason I draw your attention to this distinction is because ATProto has interesting properties, notably in how it supports pluggable identity that doesn't depend on the server you use as well as signed data repositories. This puts power in the hands of the users and not (as is the case with vanilla federation) in those of server admins. It means that you can always guarantee credible exit, you cannot get locked in. By default, email-style federation (which is the underlying model for AP) is subject to capture, and indeed email is captured (~85% Gmail and migration is at the domain level or by kindness of the admins who might accept forwarding).

However, ATProto on its own doesn't do any social media. It's "just" a layer atop of which protocols can be implemented. This doesn't mean that it can be used to implement arbitrary protocols, but ActivityPub/Activity Streams has a very good indirection that makes that a lot easier: the Actor document. An actor is any entity that can have an activity, and can therefore be doing things on social. You can get an actor document from more or less anywhere, for instance you can look at mine by adding .json to the URL of my Mastodon identity. You could obtain it by resolving a handle to a DID, and finding that info embedded in the DID document.

The actor document has a neat feature: it gives you the URLs of the API endpoints for a variety of operations. That is to say, instead of expecting endpoints for the entire API to be at predetermined locations, it specifies arbitrary URLs for each one of them. After cutting some of the cruft from my Mastodon actor doc, we can see several of these listed there:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://mastodon.social/users/robin",
  "type": "Person",
  "following": "https://mastodon.social/users/robin/following",
  "followers": "https://mastodon.social/users/robin/followers",
  "inbox": "https://mastodon.social/users/robin/inbox",
  "outbox": "https://mastodon.social/users/robin/outbox",
  "preferredUsername": "robin",
  "name": "Robin Berjon"
}

This means that we can use this as a level of indirection to point to one's own ATProto PDS with ActivityPub routes:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://mastodon.social/users/robin",
  "type": "Person",
  "following": "https://pds.berjon.com/xrpc/org.w3.activitypub.following",
  "followers": "https://pds.berjon.com/xrpc/org.w3.activitypub.followers",
  "inbox": "https://pds.berjon.com/xrpc/org.w3.activitypub.inbox",
  "outbox": "https://pds.berjon.com/xrpc/org.w3.activitypub.outbox",
  "preferredUsername": "robin.berjon.com",
  "name": "Robin Berjon"
}

Success? Not quite.

We Hit Trouble

I know you're sitting on the edge of your seat, biting your nails, for some of you clutching your pearls waiting to hear if we can just run ActivityPub over ATProto with just a bit of JSON tinkering and an ATProto app, without changing either spec. The answer is: not quite, but it's not so far as to be unbridgeable or even impractical.

The first issue is that an XRPC nsid (e.g. org.w3.activitypub.inbox) can either be a query (GET) or a procedure (POST) but as far as I can tell, not both. But the ActivityPub inbox property has to support multiple methods. Setting aside perennial debates over REST versus RPC (yaaaawn), this is not a major problem to bridge. Either the actor document could be update with separate properties for different methods (that can point to the same URL for non-ATProto implementations), or XRPC Lexicons could be change to support overloaded methods. The only thing standing in the way of a bridge here is that people get religious about this decisions. In the practical world, it's a relatively easy fix.

A second issue is that the identity and handle systems aren't fully aligned. I believe that nothing prevents com.atproto.identity.resolveHandle from resolving @robin@mastodon.social just as it resolves @robin.berjon.com, it might simply drop the leading @ and replace the other by a ., then use DNS to resolve the DID. This is even compatible with an IndieWeb ActivityPub in which actors have their own domains (as is relatively common for ATProto users). It should be possible to have an actor document also point to the DID, and the DID document link back to it for good measure.

(I'm not going to touch on the limitations of DIDs or specifically issues with did:plc here since they don't affect integration. I've been wondering if DSNP may offer the right foundation for a DID method, but that's a topic for another time.)

And… that's it? My bet is that, since I haven't had the chance to actually implement this end to end, I'm probably missing some snags. But at first blush this is more feasible than you would think from the bitter online debates.

Why Do This, Man

Overwhelmingly, our experience of social media is in siloes. They are closed environments that are designed to keep you on the inside and that integrate very poorly with the rest of the world.

Both the Activity* standards and ATProto break this siloing in different ways. Activity* are built around URLs and can sort of "socialise" more or less anything on the Web, which is great, but they don't touch the underlying substrate. The expectation tends to be that either you run your own server (which isn't for everyone) or you have to join a federated server, which tends to put you at the mercy of an admin (and, as some people are unfortunately finding out, not all admins are great). ATProto, on its side, provides a good initial foundation for an extensible PDS designed around user agency and credible exit. This means that your online presence can be custodially hosted (so you need not worry about running a server) but if you don't like your host, you can be guaranteed to be able to take your content elsewhere (verifiably) and nothing will change, you won't even need to update your handle or set up redirection.

Which is to say: they solve different problems and the solutions are complementary. By taking the small steps to make these two worlds compatible, we get significantly more from both.

Seems like it'd be worth a shot!


This post is part of a series on reimagining parts of the Web. You can read the other entries in the series at:

  1. Building the Next Web
  2. The Web Is For User Agency
  3. You're Gonna Need A Bigger Browser
  4. Web Tiles
  5. ActivityPub Over ATProto

Acknowledgements

Many thanks to the following excellent people (in alphabetical order) for their invaluable feedback: Amy Guy, Benjamin Goering, Ben Harnett, Blaine Cook, Boris Mann, Brian Kardell, Brooklyn Zelenka, Dave Justice, Dietrich Ayala, Dominique Hazaël-Massieux, Fabrice Desré, Ian Preston, Juan Caballero, Kjetil Kjernsmo, Marcin Rataj, Margaux Vitre, Maria Farrell, and Tess O'Connor. Needless to say, anything dumb and stupid in this article is entirely mine.