ActivityPub paper cuts

Everyone who has implemented ActivityPub from scratch knows that there are implementation-specific quirks that trip up developers, making compatibility between apps more difficult. Some of these issues are being clarified by the Social Web Community Group. Test suites will help too. has had ActivityPub support for years and we’re still finding edge cases.

With Ghost making progress on adding ActivityPub, I tried following their first test account today and immediately ran into a small issue. It’s simple so I think illustrates the kind of problem that developers might hit. You only need to know what JSON looks like to follow along.

When you download an actor’s profile, you get a bunch of fields like the inbox to send requests to, the user’s full name, and the user’s profile photo. The photo is set in a field called icon (or image). If you skim through the ActivityPub specification, you’ll see this example:

"icon": [

Pretty simple. The field is an array with a single URL to the image. But most implementations don’t follow that example. Mastodon and both use something like this, which is in the more complete ActivityStreams spec:

"icon": {
  "url": "",
  "type": "Image",
  "mediaType": "image/jpeg"

Which one is correct? Both, of course! Back to the ActivityPub documentation for icon:

A link to an image or an Image object which represents the user’s profile picture

Now let’s check out what Ghost is doing:

"icon": ""

This is a third variation of the same field, this time using a simple URL value. Not an array and not an object with multiple fields.

As programmers, we often try to follow Postel’s law: “be conservative in what you do, be liberal in what you accept from others”. In other words, we should gracefully handle all these different JSON responses, but we should only send out the best one, the one that is documented by the standard. And yet the standard is itself not very explicit about this.

I’m not attempting to blame anyone for this. Certainly not Ghost who has just barely got their implementation up and running and will likely have many changes coming in the next few months. But if you imagine this icon variation spread out across the whole suite of specs — not just ActivityPub but ActivityStreams, WebFinger, HTTP Signatures, and others, with potentially dozens of minor differences — you see why interoperability has sometimes been a challenge.

Manton Reece @manton