Non-Microblogging Software Design on ActivityPub (FOSDEM 2025)
I had the pleasure of giving a talk in the Social Web room at FOSDEM 2025. It was suggested during the call for submissions that I talk about my project Postmarks, but I had a feeling while working on my pitch that I had something a little more complicated to talk about. Below is what I ended up writing and delivering to the room.
Apologies in advance for all the unnecessary punctuation; they were speed bumps put in place for my too-fast speaking. (They didn't work.)
Thanks so much to Gretchen, Mandy, Nozlee, my whole team at Glitch/Fastly, and my Monday night Coalhouse crew for helping me with the drafts and prep for the talk.
Non-Microblogging Software Design on ActivityPub: On the Inherent Tradeoffs of Interoperability #
Or: The Social Web Is a Flattened Web #
A Personal History of the Web #
Hi, I'm Casey. I live in Portland, Oregon, in the United States. I'm the developer of a small Fediverse experiment called Postmarks, but for the purposes of this discussion, I need to go back a little bit further in my time on the internet.
I was introduced to the open web as an adolescent in the early 90s, I watched the birth of blogging and the explosion of opportunity that was "Web 2.0" as a teenager, learned how to code so I could make my own websites, and in the 2000s went to Purdue University to get a Bachelor's degree in Computer Science. I treated it as a trade school, for better or worse—I wanted to learn what I needed so I could start building the web.
Now, in the 2020s, I'm an engineer working on Glitch, a website that aims to make it possible for "everyone [to build] the web." It's a platform where people who want to can learn how to make web sites and other apps that live on the open web, whether that means going in and changing a few lines of text on a webpage and some colors in a CSS file, or building your own websocket-based WebXR game from scratch. Whatever you make, we'll provide hosting for it, and a community where you can share what you've built.
In my free time, among my other hobbies, I've worked with APIs like Twitter, Discord, and Mastodon to build generative bots that make art or jokes. I've built CMSes to host blog posts and creative writing that I've done. (My first ever PHP app, built in 2001, was a link blog, because I was obsessed with sites like kottke.org and Memepool.) As a user I've spent countless hours in IRC, Slack, and Discord channels, on phpBB forums, on microblogging networks like Twitter and Mastodon, on unique platforms like Google Reader and Cohost—many sites that I hear myself and others use to explain how we've spent time on the web. But one thing I think that goes too often unsaid is how much time I and my friends have spent exploring the fullness of what happens off of those platforms, following hyperlinks in a web browser and seeing the full potential of the technologies that give us this world online.
So it's been good for me to remember that the thing I love most about the web is the breadth of experiences it enables. I've certainly read my fair share of blogs, dating back to meeting someone and reading their entire Livejournal, in order. But the web makes it possible to host multimedia; interactive games; and weird, explorable websites that unfold in unexpected ways as you search their corners for secrets. That variety, when it's present, makes being online feel, to me: vital, diverse, exciting, and engaging. We're able to find not just a variety of content, but a variety of experience.
What I'm here to talk about today is the result of reflecting on that breadth of design and experience that the web can provide in the face of what I've realized I've spent so much of my free time actually doing, and with the project that brought me here, how I've missed out on taking part.
Postmarks and a Social Bookmarking Lineage #
Near the end of 2023 I released the first versions of a platform I built called Postmarks. Postmarks is a social bookmarking platform that lives on the open web. You probably know what a bookmark is, but for the sake of completeness, what we're talking about here is basically a URL along with some metadata that lets you attach excerpts, describe the contents, and categorize it in relation to other bookmarks you have. By default, bookmarks you create are visible to anyone looking at your Postmarks site on the web, but those bookmarks are also broadcast to other interested servers via the ActivityPub protocol, which makes Postmarks not just a social bookmarking app, but a Fediverse-enabled bookmarking app.
Social bookmarking is hardly a new idea! I built it intentionally with many similarities to the first social bookmarking app on the web, Delicious.
Delicious was a tool that grew out of a group linkblogging site I was mildly obsessed with called Memepool. It helped its founder, Joshua Schachter, organize URLs that he wanted to store and categorize. Delicious may not have been a platform that became a household name, but as an example of its influence, the way we use the word "tags" to refer to keyword metadata? That comes from Delicious. I was a Delicious user; it was great to use as someone who was in college and was using more than one computer for the first time (my own desktop computer and computers in computer labs all over campus) to be able to store my bookmarks online instead of in my browser. Knowing other people on the site and browsing tags to see other people’s bookmarks meant there was a light social component as well, but for me it was first and foremost a form of external memory, "synced to the cloud" in a way that before then wasn’t really a way most of us talked about this stuff.
Delicious had a sort of tortured history as it got acquired and re-sold multiple times to different parent corporations. In 2009 another developer looked at Delicious and thought "I could make that" and the result was Pinboard, a very similar website, but one that came with slow-growth ideals that seemed to promise it would stay available as a sustainable business. I moved to Pinboard shortly after its launch and used it happily for years.
When it comes to the Fediverse, I've used Mastodon both publicly and privately since 2017, but first tried developing with the ActivityPub protocol in 2022, thanks to Darius Kazemi's ActivityPub Server tutorial and the great information available at activitypub.rocks. I ended up making a demo of what would eventually become Postmarks just before Christmas 2022, kept hacking on it in my free time throughout the months that followed, and eventually shared news that I was releasing an initial version in September of 2023.
The response was bigger than I was expecting, and in the months that followed I took in a number of great contributions from friends and new acquaintances that fixed ActivityPub-related bugs, added full-text search, and all kinds of other cool stuff. Unlike many of my software projects, my goals for Postmarks are actually pretty concrete; I know what I want its end state to look like, and in 2025 I'm hoping to finish off what I see as the original vision of the project so I can decide what to do next.
Postmarks Development Principles #
There are three core ideas that I decided early on were the most important things to the project: things that would define what kept it useful to the kind of user I had in mind, what would keep me moving forward in developing it, and what would get people (including me) excited about using it over any other bookmarking alternatives. After going over the list, I'll explain each one of them in more detail, and the impacts that one in particular had on the project as whole.
The first is single-user instances. Unlike most ActivityPub apps, Postmarks is a platform where a given "instance" (or server) is home to one "actor" (or user). That is to say, if you want to use Postmarks, you either need to set it up yourself, or find someone who will set it up just for you. The setup process is pretty simple, and using the Glitch platform, there are literally 4 required steps: two random values to generate and enter into a .env file, a username to enter into a JSON file, and then you rename a file to signal to the server that you've configured everything and are ready to go. With Glitch, you can remix the base project and do all of this in your web browser in less than 5 minutes.
We will also talk just a bit about how I decided while working on Postmarks that, despite the fact that for career reasons I have learned how to make "single-page applications" that use client-side JavaScript to offer slick auto-updating websites, I was going to use "old" techniques. This was less of a principled decision than it was a practical one, but either way I'm still convinced it was the right choice.
Lastly, it occurred to me that using ActivityPub meant I should look into how I could "interoperate" with Mastodon and other similar microblogging software. It would be possible, even unintentionally, to architect the whole platform in a way that led to activity on Postmarks, despite using the same protocol, being basically invisible to Mastodon users even if they successfully "followed" a Postmarks account. But "interop" seemed to have a number of advantages. In a way, interoperability ended up being the thing that kept development moving forward quickly, but it's also led to some of the most difficult challenges I've faced in making this. But first, let's step back to the first principle.
Principle #1: Single-User Instances #
If you have any opinions about Mastodon, some of them are probably related to its decentralized model and the concept of "instances". On Mastodon and most software that works with ActivityPub, instances are most easily represented by the domain name that makes up the "second half of your username", but the actual impact goes further than that. An instance is a collection of users who store their data on the same server, who abide by some set of rules, who are subject to administration by the instance's leadership, and who (in most cases) are seen as belonging to a group of some kind, whether it's a clearly-stated shared interest or identity group or a sort of reputation/attitude. Instances are homes to communities.
Like most communities, instances experience growth and decay; they go through good times and bad. They also are subject to political action among the other communities they are in contact with. Sometimes this leads to situations where instances shut down, or get isolated from other parts of the "fediverse". The way most ActivityPub apps work right now, these events can have real impacts on individual people who never saw it coming. On Mastodon today, moving your network is accomplishable (and the process for doing it has seen improvements over time) but moving your content is currently not. Some of this is due to difficulties that are specific to Mastodon, but some of it is due to ActivityPub itself. This isn’t for lack of care: there are efforts to improve this problem, and I look forward to seeing the results.
Web-based bookmarking, while having a social component dating all the way back to Delicious, has first and foremost been an activity I do for myself. In my private bookmarking account that I use as an authoritative source, I have about twenty thousand bookmarks accumulated over the last 20 years as I've moved from platform to platform. I use my recent bookmarks every day to track things I want to read across devices, and I search my full archive not quite daily, but pretty close. If I want to have a place to put bookmarks, I need to know it's not going anywhere, and I suspect most people who would install their own self-hosted bookmarking app feel similarly.
While Mastodon and many of the other popular ActivityPub platforms are very reasonably designed to be large, heavyweight (or medium-weight) platforms with live databases and search indexes and things that help them scale to hundreds and thousands of users and even more people's data cached from other instances, there have been some projects like Gotosocial that focus on a minimal footprint for a small number of users.
With Postmarks, I decided to take that a step further: one instance, one user. While this decision doesn't come without its own complications, it has done its job for me in keeping the project simple, manageable, and adding a constraint that says the software has to balance the utility it provides to its own user with the complication of its install & hosting process, instead of amortizing it across its large population.
Principle #2: "Old Web" Architecture #
Moving along, we turn to its "old web" architecture. I decided when starting this project to build this app as a web server that generates HTML on the server-side, delivers that to the browser, accepts some HTTP requests via HTML forms, and uses minimal javascript on the frontend to improve the user experience a bit. The app has predictable, readable URLs that a motivated user could build themselves when typing in the browser or when trying to make scripts or tools that would link to or query their bookmarks. I used SQLite databases persisted to disk to store everything so there’s no extra services you need to set up or pay a SaaS provider for. And yes, I promise I'm old enough to see a certain irony in referring to a node.js app as being part of the "old web".
All of this was in part related to my own tendencies and philosophies of software architecture, but it was mostly to force myself to stay moving forward on the actual functionality of the project. I'm not a UX expert and while I know some modern frontend techniques that make use of heavy client-side javascript to create an "app-like" experience, I knew I risked getting bogged down in learning new patterns and adding new dependencies. Sticking with things I already knew how to do in as many areas as possible meant I had time to learn the ActivityPub-related things I needed to learn to make this project happen.
The general philosophy of keeping things simple also let me build Postmarks on Glitch, which helped avoid a common problem when developing software for ActivityPub. To make something that talks to other ActivityPub servers, you need to host it somewhere that has a publicly-addressable URL. There are ways to do this while developing locally on your computer, using a full-on reverse proxy pointing to a static IP you provision, or using tools like ngrok.
But by developing on Glitch, I can spin up any number of copies of my app across any number of subdomain hostnames essentially instantly. Glitch doesn't offer the ability to build a network of services like a Postgres database and an Elasticsearch cluster out of the box, so sticking to configuration in static files and a database that's persisted to disk made it easy to host, and Glitch's "remix" feature meant I could fork the project and pursue other ideas while looking at a live, working copy of a previous checkpoint of my work at the same time.
Principle #3: Maximum Interoperability #
The last principle was a sense of desire to interoperate with Mastodon and its microblogging peers. There were a number of motivations for this: to ease development and testing, to improve demonstration and onboarding, and ultimately to rebuild a feature of Delicious and Pinboard that to me was never my most-used feature, but seemed like it had some interesting potential for Postmarks.
The testing and development benefits came pretty much from the very first demo I gave to my coworkers in 2022. With the way I had written the first prototype, an instance was capable of registering that other Fediverse actors had "followed" it and broadcasting out activity to them, but I didn't write any UI or protocol-level support for following anyone else from within Postmarks. I didn't need to! I could test my instance out by following it from my Mastodon account, seeing the posts show up in my timeline, replying to them to add comments, and so forth. I didn't add the ability to follow other Postmarks users (or anyone else) until the day after I announced the project in September of 2023, because it hadn't hit me yet what the purpose of that would even be. But Mastodon proved a very useful smoke-testing tool for my ActivityPub work. After launching, via members of the development community, I discovered other tools that would be similarly beneficial: FediDB gave me some idea that other people were using Postmarks and more recently the amazing Feditest project was gracious enough to run their test suite against Postmarks as part of their initial launch, which has given me a better idea of how well we comply with the ActivityPub spec (not perfectly, but not terribly!)
The first use of adding the ability to follow people from within Postmarks, it turned out, would be to add a prototype of what Delicious called the "Network" page, which was sort of a proto-feed (before Facebook had its News Feed or FriendFeed existed) of bookmarks that people you knew were creating. Postmarks' "Network" page, in its finished form, will show bookmarks from other Postmarks users that you follow but also should reformat URLs shared on platforms like Mastodon and make them easy to add to your own bookmarks. In that way it's also similar to the Twitter API app Nuzzel which was a private email newsletter of just the links that were shared on your Twitter timeline or any number of Mastodon-related apps that have come since then that do similar things, like Sill.
Finally, interoperability provides visibility for your project. It's an extra avenue for sharing the platform for people who get excited about it and an extra way to dip your toe in the water as an observer from established platforms like Mastodon. If one person loves Postmarks and starts using it, they may be encouraged to post about that on Mastodon. Rather than telling people to go look at a website elsewhere, they can link to what looks just like a Mastodon user by specifying their @username@postmarks-instance.org
. If someone who sees their post clicks through, they stay within the context of Mastodon. They're invited to "follow" that feed right in their Mastodon client. They'll start to see the bookmarks, and maybe once they're following a few of those, they'll notice the similarities and wonder how they can get their own. They reply to a post and that shows up as a comment on the Postmarks user's instance. In doing so, they've (hopefully) made the Postmarks instance more valuable for the person who's bookmarking on it. Over time, the result is that Postmarks becomes a useful tool to those who want it, and makes itself known organically to those who may realize they want it later.
Interoperability: Painful Realities #
Interoperability, unfortunately, turned out to be the most challenging of the three principles I kept in mind while developing Postmarks. Everything I've said about its benefits so far is true—but it's almost more complicated than that.
While using Mastodon to test the output from Postmarks was helpful, it also sometimes added complexity. There are things that I was doing with ActivityPub that I'm still convinced were technically compliant with the spec, but that resulted in posts that were broken or not doing what I wanted on Mastodon. Little details like having bookmarks' tags show up as hashtags that could be clicked in Mastodon posts to take you to a search in your Mastodon client for posts with that hashtag required extra work. It wasn't always clear to me if I was doing something wrong from the perspective of the protocol, or not just understanding a detail of how Mastodon expected data to be structured for the purposes of its own product decisions. Suddenly on top of reading the spec I'm looking at the source code for Mastodon, third-party clients, similar platforms like Misskey, and so on.
The biggest difficulty I faced when handling interoperability, though, ends up being more philosophical. It relates to what I said earlier about what’s great about the web that captured my attention in the first place—or rather, what's missing from that today.
Mastodon and many of its Fediverse and broader social web kin were heavily inspired by Twitter. In general, they are designed to reproduce a specific core experience: a timeline of posts made by a selection of users that you have chosen to "follow". To break down what a "post" is on these platforms, we can say that it's a block of text and/or a collection of media "attachments". Some text, like a username or a hashtag, can create useful hyperlinks; a single URL in the post can be given a visual "card", but no other programmable content or even significant structured display can be added.
What this means is that Postmarks activity, in order to interoperate, has to find some way to fit within that box, a box that is essentially a "Note", as ActivityPub sees it. As a result, when you see a Postmarks bookmark on Mastodon, your experience of it is as a Mastodon post that follows a certain template that makes sense for a "bookmark": link at the top, description in the middle, tags at the bottom. Postmarks "Comments", which have a distinct secondary importance to the actual bookmark itself are presented in Mastodon with equal visual importance, because they're just Notes that are "replies" to the original Note. But in doing so, it creates a different social context by which the activity is observed depending on which platform you’re observing it from.
As a sort of cynical flipside to the organic growth opportunity I outlined earlier, it also raises the question for me: what's the point? Why bother with Postmarks when you could instead write a Mastodon client that formats posts like a linkblog and submits them to a Mastodon instance? You could search your posts with some kind of combination of operators like from:@me https AND #bandcamp
to find all the bandcamp pages you've "bookmarked". So why build a whole platform just to feed it back into this "flattened" web?
This isn't Mastodon's "fault", to be clear. It's only natural that in an ecosystem with one dominant player, its product decisions will influence platforms that seek to interoperate. But I do think we should be aware of, and have better ways to discuss, the influence it has on what we do, in the same way that I've started to see good discussion about other topics that reveal the status quo not as neutral, but a result of real cultural conditions that have impacts on how we live.
After seeing how this plays out with Postmarks and the current Fediverse ecosystem, I've come to start thinking about the result of all of these posts having to fit in this one place as not just a social web, but a "flattened web".
The Flattened Web #
It may sound harsh to speak of a "flattened web" but I don't say this as an outsider. I feel confident that I've read more posts on a flat timeline than most of the population of the world, having been very heavily invested in communities on these platforms for almost as long as they've existed. And up until recently, I wouldn't say I regret it! It's hard to escape these kinds of experiences today once you have friends online, because this kind of social space is where lots of smart, funny, wonderful people congregate and share really interesting and great stuff. Right now I consider it inescapable for the social circles I find myself drawn to. In 2025, choosing not to be on a flattened web makes me feel more alone.
In the case of a microblog timeline, something as respectful of my attention as Mastodon at least limits itself to stuff from the people I've chosen to follow. But short of some small innovations I've seen in apps like Phanpy, it's still a flattened timeline. Everything is condensed into this same rectangle, whether it's an amazing announcement, a funny joke, news of a tragedy, a friend looking for help or even just validation, or a fresh Postmarks bookmark saved. The best chances we have to set context feel like band-aids to me: content warnings, obsessive list-curating, multi-account switching. Otherwise, you’re left to absorb whatever shows up that represents the fullness of the people you follow.
Maybe when I was younger I was better at "surfing" from vibe to vibe, but there's something that's made it harder for me recently to want to subject myself to contextless chronological information, columnized and compressed to a dozen second's worth of attention, repeated hundreds of times in a row. It's not pure rejection, because some days I do love it, or find it useful, or just feel like I have to submit to it because... I'm wondering what's happening, or maybe I'm just feeling alone and want to feel connected to others.
I'm now at the point where I have friends online that I've known for 30 years, and there are people I've known for 6 months that I hope will be in my life 30 years from now. But right now, and maybe because of the moment that we're in, I keep wishing the way I related to them didn't involve this flattened timeline.
So it took the realization that no matter what I did with Postmarks to make it interoperate, I had made an important component of it something that fed into other people's flattened experience of the web... it's made it difficult to be excited about doing more. Seeing that other people actually cared about Postmarks felt like a responsibility, and contributing to an experience that I'm feeling so ambivalent about feels like a betrayal.
What To Do? #
I still haven't really figured out what to do with these feelings. Postmarks has been fairly neglected, as open source projects are, I suppose. I'm trying to find ways to structure my 2025 in a way that I'll have more time to devote to working on some of the lingering bugs and missing pieces in the project, but at this point it's more out of a desire for closure than it is excitement.
I've pondered two drastic actions: ripping ActivityPub out entirely and focusing on making a self-hostable bookmarking app, or continuing to use ActivityPub, but in order to focus on building a network of Postmarks instances, using structured data in ways that won't necessarily work with other ActivityPub software. But neither feels right to me, even though they would be "easier" in some ways.
To be clear, none of this makes me think ActivityPub itself is less good or interesting, and all of it comes down to a personal feeling that I just can't keep going the way I have been. At some point last decade, I threw myself deep into the flattened web, and it's taken me some time to realize recently that there's nothing about it that says it's the absolute end-state of relating to other people online.
So instead I'm looking to understand: how do I seek interoperability in a way that I can feel good about? How do I build a product that serves a specific purpose but also benefits from the opportunities that are created through something as open-ended and cool as ActivityPub? Are there ways for the platform itself to receive the new data as a flattened queue, but to make the experience of using it feel relaxing, exploratory, or even joyous?
After over 20 years of making things with software, I'm a firm believer that very few choices can provide purely positive effects. Advancements in software are built on layer after layer of tradeoffs, and in this case something that has given me a lot of benefit in the course of building this project also resulted in some questions that I'm forced to reckon with. After struggling for a while to ignore them, to push through and keep doing what I've been doing, I think instead I'd like to try to find an alternate path. If any of this resonates with you, I'd love for you to get in touch and try to move forward together; to turn the social web into something that feel less flattened.
- Previous: Cohost Imported