Static ActivityPub Publishing
One of the requests I (Evan) have heard often over the years about ActivityPub is to make publishing on the Fediverse as easy as publishing with RSS. To make an RSS feed for a person or a site, a publisher provides, dynamically or even statically, a single file, with a simple format, and makes it available over the Web. Other people’s feed reader software can download the feed and show the user either the full content of the site, or enough of a summary to get the user to click a link and read in their browser.
So… why can’t ActivityPub be like that? Make a feed, and let others read it if they want to?
The good news is, it can. ActivityPub defines an entire read-only structure for actors, content, and activities. Pushing content to other servers is entirely optional — you can just tell other servers that you don’t accept remote messages, and they won’t push them. The read-only interface to ActivityPub is rich and extensible — it covers content like text, images, video and audio, but also activities like liking, sharing, and replying. It’s not quite as simple as RSS — it requires a lot more, smaller files — but it’s pretty straightforward.
The bad news is that most widely-used ActivityPub software doesn’t recognize read-only ActivityPub feeds. Most ActivityPub servers expect to send messages to every actor, and to receive messages in reply. They don’t have internal facilities to check ActivityPub feeds regularly for new content and activities.
This isn’t just a technical issue; it’s also an adoption issue. If implementing ActivityPub requires all the infrastructure of our message-passing services — queues, signatures, filtering, fanout — it inhibits software developers and publishers from experimenting with the protocol. Implementing RSS can take a day or maybe a week of developer work. Implementing ActivityPub’s federation protocol can take months. Which one is going to be done in a hackday or a medium T-shirt-size sprint task, and which one is going on the back-back-back-backlog?
To make it easier to understand and support this proper subset of ActivityPub, I published FEP-b06c: ActivityPoll last summer. (“Poll” is in contrast to “publish” — emphasizing the read-only structure.) It’s a profile of ActivityPub, identifying parts of the ActivityPub spec that will work, or not, with a static, read-only ActivityPub publisher. It also includes new terms to easily identify a read-only ActivityPub actor, as well as a way to declare how often a feed is updated (very useful for limiting the load on both the reader and the server sides).
In order to test the profile, and identify shortfalls, I implemented a plugin for the static site generator (SSG) software, Jekyll. Using a static site generator keeps this process honest — there’s no temptation to add just a teensy bit of dynamic content generation; it has to be entirely static. If we can show interoperability with actors implemented with an SSG, we’ve shown that static ActivityPub publishing is truly possible.
The plugin is called jekyll-activitypub-static (there’s already a jekyll-activity-pub plugin, which uses off-site dynamic endpoints to implement the federation protocol, so this one emphasizes that it’s static). It’s in the rubygems.org repository, so you can just add the gem name to your Gemfile for your Jekyll site, and it should install correctly. The configuration is pretty straightforward, and has pretty reasonable fallbacks, but you can tweak it to get more specific features.
I’ve got an example actor on GitHub Pages at @evanp . You can search for it in Mastodon, and view the profile, but you can’t follow it directly. Other front ends can view it more directly — please tell me if yours can!
If you’ve got a Jekyll site, please try out jekyll-activitypub-static and let me know what you think. Fair warning — the Webfinger support depends on having your site deployed at the root of the domain. There are probably some ways to work around that — let me know if you find one! If you have changes or enhancements you think can be made to the plugin, let me know.
And if you’ve got a full-featured implementation of the ActivityPub federation protocol, please consider implementing a polling structure for static actors. FEP-b06c covers most of the caveats, but please feel free to leave a comment if you find another gotcha. I’ll be right in there with you — I’m going to implement it in onepage.pub and activitypub.bot.
Thanks to everyone who has commented on FEP-b06c, and to people who’ve tried and contributed to jekyll-activitypub-static. I look forward to more implementations!