Skip to main content
A cartoon depiction of the author, wearing a hoodie and smiling

Latest 3 Posts


What is Postmarks? #

I'm finally sharing my project Postmarks, a single-user bookmarking website designed to be easily hosted on Glitch.

Postmarks aims to provide a few different functions. Those functions are:

  1. Tag-indexed bookmarking
  2. ActivityPub publishing (soon to be optional!)
  3. Simple commenting, also powered by ActivityPub (also optional)

It's inspired by a number of websites I've loved over the years, including, Google Reader, and Branch.

You can see a basic demo of the site at, or an example of an instance with a lot of bookmarks in it at

Tag-indexed bookmarking #

If you're at all familiar with or Pinboard, you should hopefully be comfortable with the basics of using Postmarks to store bookmarks. If you're not already familiar, the idea is to store a collection of URLs along with some associated, browsable data—a customizable page title; a "description" field for summaries, notes or excerpts; a list of category "tags"—for your own reference over time and/or to share with other people.

On the centralized bookmarking sites of yore, you'd create an account and exist along with a collection of other people, meaning there were some lightweight community features like seeing who else bookmarked a given URL, seeing a public feed of all bookmarks for a given tag, or even "following" users. The Postmarks site itself is made to operate for one user, but the goal is to make setting one up as easy as possible so a single person can set up many of them for different purposes or keep all their bookmarks in one place and use the tagging features to make different kinds of things easily browseable.

ActivityPub publishing #

Postmarks, by default, also publishes all new bookmarks via the ActivityPub protocol, which means that people on web applications like Mastodon can "follow" your site in their timelines and lists and see new posts as you make them. And in turn, you can follow other people—other Postmarks users, or users on Mastodon, FireFish, or other text-based ActivityPub applications—and the links they share will show up on your Network page, with an easy way to grab any link you see and bookmark it for yourself.

Simple commenting, also powered by ActivityPub #

Once we've got bookmark posts showing up in Mastodon, it would be a shame if we didn't take advantage of that, right? Postmarks listens for incoming replies to its posts and absorbs them as "comments", which by default are unpublished/only visible to the admin when editing a bookmark. Controls are available at the site-wide level, and on individual bookmarks, to both auto-publish comments and just ignore them entirely.

You own your instance #

Postmarks isn't as complicated a piece of software as Mastodon! At the very least, you should feel welcome and empowered to edit the CSS to set your own color scheme, fonts, and so forth. If you're feeling more adventurous, you can reshape the Handlebars templates entirely in the src/pages directory. Or fork the project entirely and write new features from scratch! I'd love to accept general improvements on the frontend experience especially; feel free to open issues or submit pull requests on the Github repo.

A work in progress #

I've got a whole future ideas document included in the default bookmarks for the site, but to summarize I'd like to make Postmarks a fully self-sufficient platform, supporting all of its own features without ever involving a Mastodon or other microblogging platform. I'd also like to find ways to increase the usability of the site while keeping it compact, lightweight, and able to run on a broad variety of hosting platforms. I'd also like to see support for other indie web concepts, whether that's Micropub, Webmentions, or anything else I can harness to make Postmarks really useful to the people who want it!

Acknowledgements #

Thank you to Darius, Mouse, Ben, Anil, Casey, and everyone else who has inspired me, advised me, or lent a hand to Postmarks. (The more complete version is in the project README!) It's been fun to learn all the things required to make this platform come into existence and I hope to keep improving it and sharing bookmarks for a long time to come.

Importing Unread articles from Raindrop to Readwise Reader

I'm trying out Readwise Reader, starting today. I've tried... almost all of the Read It Later services, but had kind of put off checking this one out because the marketing around their original product Readwise seemed very geared towards AGGRESSIVE SILICON VALLEY EXECUTIVES who need THE POWER OF MACHINE LEARNING AND MIND HACKS to ABSORB THE MOST KNOWLEDGE and CREATE LEARNINGS FOR THE FUTURE or whatever. But some really thoughtful people I know seem to dig it, and so I wanted to give it a fair shake.

I've been using Raindrop for both bookmarks and "Read it later" for a while. The mobile app is nice-but-not-amazing, the bookmark saving workflow is nice-but-not-amazing... but it does have one really killer feature I haven't seen in other services: the ability to mass-bookmark all tabs in a given browser window. (It's even window-specific, so if you have multiple windows you can shift tabs around to control exactly which ones you want to mass-bookmark without having to temporarily close any other tabs you happen to have open).

The only problem is that getting a real test of the system into place meant importing my real backlog into Readwise Reader. Their Raindrop integration is built around their main product (Readwise, instead of Readwise Reader), which is a highlighting/knowledge-base type thing, so it only works on bookmarks in Raindrop to which you've added an annotation using their highlighting tool.

Poking around a bit, I did notice that Readwise Reader does support CSV imports, but in an undocumented format that I guess you have to join a "community Discord" to be told about. I hate this practice and would sooner walk away from a service entirely than be forced to join a Discord just to look up information, something it is not good for, but was lucky to find that a few people on Reddit (lol) have shared a link to a Google Drive URL (lol) that outlines the CSV format, so I was able to learn that they require this series of columns:


the Folder can be "Unread" or "Archive", and the Timestamp must be in "Unix time", the number of seconds elapsed since 1970-01-01 00:00:00UTC.

Raindrop offers easy exporting of any given view to CSV (along with other formats), so I was able to quickly get access to that data. Unfortunately, it wasn't quite the right CSV format. So I wrote a quick Ruby script to reformat the files:

require 'csv'
require 'time'

unless ["Unread", "Archive"].include?(ARGV[2])
raise RuntimeError, "third argument must be \"Unread\" or \"Archive\", found #{ARGV[3]}"

rows =[0])[1], "wb") do |csv|
csv << ["URL", "Title", "Selection", "Folder", "Timestamp"]
rows.drop(1).each do |row|
csv << [row[3], row[0], nil, ARGV[2], Time.parse(row[5]).to_i]

I generated two CSV files from Raindrop, one with just my "Unsorted" folder (which is where I put things when I bookmark them to read-later) and another with my "Misc" collection, which is the general-purpose collection I use for saving things that don't belong in some project-based set of bookmarks. Then I ran the command twice:

ruby csv-mutate.rb raindrop-later.csv readwise-inbox.csv Unread
ruby csv-mutate.rb raindrop-archive.csv readwise-archive.csv Archive

I imported both of these files via the "Upload file" option on Readwise Reader's Import Page and within ~5 minutes they had processed both files.

This process works as of Sep 4 2023; I can't guarantee that it will work even tomorrow. But it worked for me just now and it occurred to me that this is exactly the kind of thing I always wish would show up in search results when I have the same problem, so if that's you, good luck!

Chorus: The Modern Media Stack

I was joking, as a defense mechanism, that I had just told an intern I've been working with this summer (with a great deal of self-consciousness for being preachy, but continuing nevertheless) that something I found challenging about being a professional software developer was feeling okay—even good—about code I've written being removed from a codebase, features being turned off after I had agonized over the lines of code required. I meant it. I had watched open source projects develop over my high school years, seen people receive acclaim and job safety from their contributions to CVS repos and kernel mailing lists and whatever else. When I finished college and some QA work that I did, I was proud to be taking problems from product managers and implementing fixes and additions that would make software more valuable, more capable. So of course your measure of success is based on you using your brain and fingers to accomplish something, and then have that manifestation continue accomplishing good things forever.

When I worked at a small ecommerce startup in 2011, the week I arrived I was invited to a funeral. It was a meeting, actually, one held in the open-plan office, for everyone in engineering, design, and product to attend. The funeral was for all the features and experiments that had been discontinued in the past year—the greatest of all a huge Facebook integration that lots of people had worked hard on, and that had brought them so much frustration, and they were proud that it ever worked, but so, so happy to be free of the consequences of all of it. It was mind-blowing! I stood next to the VP of Engineering as a coworker sang "Danny Boy", an honestly startlingly good rendition. Afterwards, another person did an interpretive dance. We all laughed. I realized that these features, these lines of code, these product specs were just as well sent off into the sea if we took a look at what we had and realized we were better off without the things we had piled up around us.

Working on and around Chorus was what taught me the difference between a product and a piece of software. I met people who broadened my perspective on... well, pretty much everything, really, but in the most focused way on what it means to build software "products". I had worked places where we actually published periodic updates, some of which got burned to CDs and shipped to important clients with high-level security requirements, and still thought of my job as writing code. With the help of the people I learned from, people who worked across a lot of disciplines, I came to understand why you would refer to a team made up of many different skillsets and job requirements as a "product team". I still kind of bristle that we had to rebrand as a "Product, Design, and Technology team" while I was there. We were part of a team, with all the good and bad that can contain, and not a bunch of different departments somehow finding a way to work together.

Chorus was a complicated set of interrelated parts. It had a monolith at its core, one built in Ruby on Rails like many web servers deployed in the late 2000s. Over time it picked up some other components; a separate Rails app that stored "structured data", a realtime multiplayer rich text editor and a companion database/all-in-one API, a federated GraphQL API, a video asset management tool and probably half a dozen other components that I'm forgetting or that were spawned after I left. In teams that I worked on that were outside of the Chorus umbrella there were solutions for deploying works-in-progress of the entire software suite so you could run QA and demonstrations, there were ad authoring tools and ad marketplace header-bidding gizmos and any number of other things that I'm sure have evolved into all kinds of dizzying forms; some of which will survive this transition, others which won't.

So thinking back to the comparison that I had made, to the point I had so heavy-handedly laid in front of this intern in an attempt to explain why there was value in building something that felt temporary but would inform a lot of decisions that my team at Glitch will be making over the next year: I don't think it's actually the same at all to grieve Chorus and what it is and has been. Because I'm not actually grieving the individual lines of code I wrote, or even the ones that other people wrote that probably worked much better. I'm grieving the product, grieving the loss of jobs for people who found value in working on something that I also found value in, and grieving the loss of a time when the type of people who work in management (and VC and whatever other roles are involved in this kind of decision-making) understood and valued the kind of work that I found so fulfilling, that I witnessed bring happiness to the people who used it, that gave me the opportunity to be around people who made me a better engineer, a better product thinker, and a better person.

When Buzzfeed News shut down recently, I was pretty sad. It felt like a notable moment among a lot of signs over the past few years that digital media, much like many other areas where tech investors happily deposited piles of money, though maybe to not quite as wild of a degree, was evolving; and generally not in a way that favored readers, writers, or anyone who built "platforms". I was happy to see that on its way out the door the editorial folks managed to write and publish an "oral history" of Buzzfeed News. I guess I had been out of the game just long enough to be surprised, and a little hurt, when I realized the story was being told entirely from the editorial perspective. It's a perspective I value, but I can't help but feel a little bit sad that a product came and went, one that based on my experience as an "audience member" worked pretty well, and I still have no idea who was involved in making that, what difficulties they had, what successes they celebrated. Did they have funerals for features that they removed? Did they ever have to scramble to build a feature for some momentous story that was on the platform? I hope I get to ask someone from that team some day, but until then, their side of that story remains untold.

Today I've seen a lot of weird takes from people who don't actually understand anything about what "digital media" has done, or what value Chorus provided to the people who worked at Vox Media or any of the websites that licensed Chorus during its SaaS period. A couple reactions seemed to indicate a confidence in a simple idea: that leadership retiring it in favor of Wordpress means that the platform was a failure. This is the most bizarre idea to me, that only a platform that survives until the heat-death of the universe can be considered a success. The fact that many of them were saying it on social media sites and Mastodon instances, neither of which have exactly been a paragon of durability on the Internet, was not lost on me.

I'm sure there were lots of people from Vox or newsrooms who licensed Chorus who had negative experiences. Maybe some of them even know and prefer Wordpress. Chorus wasn't always perfect. It didn't always do what they wanted. We had outages and data loss and features that we should have been able to prioritize but never did. It's an unfortunate truth that the team didn't always have the shared vision, the focus, the teamwork and the energy to solve every problem they wanted to. And it may even be true, in the sense that Vox Media as a corporate entity has to make decisions that preserve some abstract sense of success that cannot be perfectly calculated by any one person, that retiring Chorus as the CMS at Vox Media is the "correct choice". But one thing I'm fairly sure of is that none of the people who have already been laid off, or will be laid off next month, or whenever the Wordpress migration is "complete", are to blame for Chorus not succeeding in a way that may have made it look more appealing to the kinds of people who decided that it's time to end it. And that just feels really shitty to me.

540 more posts can be found in the archive.