This is an answer to the blog post ActivityPub – one protocol to rule them all? of Dennis Schubert. Dennis was asked if Diaspora would implement ActivityPub, this blog post was his answer.

What is ActivityPub?

In a nutshell: ActivityPub is a new standard that has been defined by the Social Web Working Group of the W3C. In combination with ActivityStreams it is meant for decentralised communication, like in Friendica, Pleroma, Mastodon, Hubzilla and Diaspora. It is already implemented in most of these networks (except Diaspora). Additionally, several new systems have been created that support this protocol like PeerTube, PixelFed or Mobilizon. And there are even ActivityPub extensions for Nextcloud and WordPress.

Critique

Missing specification for profile discovery

Dennis points out that there is no specified way to convert a handle in the format user@domain.tld to a profile URL like https://domain.tld/profile/user. Most current implementers have integrated Webfinger for that purpose, since users like to work with handles rather than URLs. So although not explicitly specified, this is a fine solution for the problem.

Custom Fields in the profile

The responsible person in an ActivityPub object is called “Actor”, which is similar to what is called “Profile” in Diaspora. Due to the character of AP, you can not only use the fields that are defined in ActivityStreams for this, but also fields that are defined in the vCard specification – and you can even create your own namespace to define any field that you need. Dennis sees a potential problem in this, because it could end up in confusion, because different systems can use different fields. First of all: People can speak with each other, so when you are in the need of some custom field and you don’t know if anyone already implemented it, just open an issue on the GitHub repository of the W3C and ask for a solution. Additionally, you can ask in the IRC channel of the SocialCG (I did this a lot while implementing AP for Friendica).

Is it a problem if some implementations do implement fields that others don’t implement at all? I don’t think so. When implementation A implements the birthday field and implementation B doesn’t, why should it confuse a user of implementation B that there is no birthday field? It is a different software with different capabilities.

Even when two different implementations would have implemented the same content in different fields, this shouldn't be a huge problem, since - once discovered - that issue can always be worked at.

sharedInbox and private activities – or vice versa

The endpoints for the communication (incoming and outgoing) are defined per actor. They can – but needn’t – contain a “sharedInbox” in addition to the “inbox” field that is mandatory for incoming content. (Additionally you can - but needn't - define an "outbox" for fetching the actor's posts). You are perfectly fine with sending public content to the personal inboxes only – or to send non-public content to the shared inboxes. This is no security issue at all, since on private posts that are directed to the shared inboxes, you just have to set the receivers in the “to” or “cc” fields, then they will land in the correct inboxes – and nowhere else. When you don’t want to include a receiver’s profile link in the "to" or "cc" field of a post, then you have to send this post to the receiver’s personal inbox as well.

Multiple sharedInboxes on a single server

Dennis points out that on the same server you could have multiple sharedInboxes. This is true – but this is no problem at all. To find all needed inboxes, you just have to collect all receivers (you should have them on your system) and then add and condense the inboxes. At the end you've got a list of personal and shared inboxes where you have to send your content to. In fact, you should mostly send all stuff (public or private) to the shared inboxes when possible. This saves bandwidth.

Pulling private posts

The specification defines that you can pull all activities (so it is possible to fetch the content when you only have the URL). This is a fine feature that is used extensively. Dennis says that it isn’t defined that you have to use authentication when pulling non-public content. But: It is surely a bug when someone wouldn’t do so, and in fact all systems that I have tested, are acting that way or prohibiting access to all non-public content. I would surely file an issue when a system doesn’t do so – and I wouldn’t be the only person, I guess. Of course an implementer could refuse to change such a behaviour. But the same reaction could occur when the specifications would make this behaviour mandatory.

Creating relations between users

ActivityPub has a simple model of “person A wants to follow person B”. In fact this model is used on most systems that I know of, including the central ones like Twitter or Facebook, but also the existing distributed ones like GNU Social or Friendica. If person B then wants to follow back person A, then person B will simply send a follow request as well. This results in having three possible states of a relationship: A is a follower of B, A is followed by B, A and B have a mutual relationship. This is an easily understandable model.

Additionally, ActivityPub even supports the manual approval of a "follow" request (which is the default behaviour in Friendica).

I don't think that there are flaws in that concept at all, it works fine for public content. For the distribution of non-public content see the following paragraph.

“aspects”

Diaspora uses so-called “aspects” which are collections of persons that have a relationship with the respective user. When creating a post to such an “aspect”, in ActivityPub you would simply collect the list of persons in that aspect and direct the post to their inboxes. (Optionally - when you don't want to send the content to each personal inbox, but you want to use the shared inboxes - you have to add the receivers to the “to” and “cc” fields).

This means that you have full control about who can see your non-public posts, like you have in Diaspora or Friendica since their beginning.

Private messaging

In fact, as Dennis mentioned, there is no definition for private messages in the ActivityPub specification. I opened up an issue for that, but sadly it has been postponed. Lately though, there has been some movement in this area. Pleroma has implemented a field in the “litepub” namespace for this. Litepub is meant as some enhancement to the ActivityPub specification. Since implementers from Pleroma and Mastodon already realised that the original specification is missing this important part, I expect some agreement in the future that other implementers will adopt, in the sense of interoperability as well.

What to use? “Article”, “Document” or “Note”?

Diaspora doesn’t know different content types, Friendica doesn’t really know a difference at all either, but we do have posts with a title and posts that don’t. So we simply transmit “Note” on posts without title and “Article” on posts with title. Since Diaspora doesn’t know a title field at all, I would suggest sending “Note” in any case. This is no problem with any current implementations, since even the length restricted ones will limit the visible content anyway, but the content can be expanded on request.

Concerning the receiving of the content, just accept everything that you are able to process. "Article" and "Note" contain the same fields and only differ in their intention, so it's no problem at all to handle them identically. And if you really can't process some content field at all, then don't do so and ignore it. For example: If you don't support events in your implementation, then it doesn't make sense to try and process the "Event" content at all.

HTML, BBCode or Markdown?

The “content” field should contain HTML for best interoperability. Of course you could use something else, but for the sake of interoperability, you should transmit a HTML that is known to be compatible with most receivers. But since you might like to transmit the post in your own content type as well, you could simply use the “source” field – as we are doing on Friendica.

This is also a solution for the problem described in the blog post where users want to attach images in specific positions. If some other implementation wants to benefit from this, it can simply parse the “source” field instead of the “content” field as well.

Diaspora does a strict way of distributing comments: This is the job of the thread starter. So a commenting person will always transmit the comment to the thread starter. This person will then transmit it to all receivers of the starting post. This is a simple and reliable process. Sadly ActivityPub hasn’t been that clear in the specification. Instead, the comments will be distributed by the commenting persons to all persons that are in the “to” and “cc” fields of all posts in that thread – and on public posts additionally to the receivers of the commenting person. This is not a perfect situation – but there is a solution to that problem:

In Diaspora the distribution of comments by the thread starter is possible, since the post is signed. This signing isn't mandatory in ActivityPub, but can be done, using LD signatures. (For example Mastodon, Friendica, Hubzilla and Osada are doing so). When posts are signed that way, they can be transmitted easily via the thread owner. Additionally even (public) non-signed posts can be transmitted, but then they should be verified by the receiver by fetching the original content from the original server. This concept is followed in Friendica’s implementation. The distribution of signed posts via the thread starter is done in Mastodon as well.

This means that in reality there is no problem with incomplete threads. Additionally, possibly missing (public) content can be fetched without a problem. There is only one drawback: There are some additional transmissions that are unnecessary.

Vagueness in the specification

While I agree that things like signing of content or the message distribution should have been specified more precisely, the vagueness in most other parts does make sense. It's impossible to build a specification that suits everyone. Luckily the working group had a sufficient varied type of people in it that this flexibility ended up in the specification, mostly because the members couldn't find stricter solutions that fitted everyone's needs.

Conclusion

ActivityPub is not perfect. For example, there is no encryption model supported, it only relies on the use of HTTPS. And there are some more things that hadn’t been mentioned in Dennis’s post, but that I had experienced while implementing ActivityPub in Friendica that led to some incompatibilities with other implementations. But they have all been solvable.

Important lesson

While implementing the protocol I learned that communication is important. The specification leaves space for interpretation. So - to ensure interoperability - the different implementing persons should talk to each other and should discuss solutions that are fine for all.

When you think that some implementation should change its behaviour, just ask them to do so and explain why. I did so, and in fact most issues have then been solved.

I'm always having a look at the SocialCG Matrix channel and every once in a while some new implementers appear there and do have questions about how to implement specific stuff. The people there are really helpful and with their help, they assure that the systems keep talking to each other.