How I consume Bluesky (and Twitter and Mastodon) #

tl;dr: I’ve added Sky Feeder and Tweeter Feeder to join my earlier Masto Feeder tool, allowing me (or anyone else who signs in) to follow accounts from all three social networks in a feed reader.

NetNewsWire showing Bluesky, Mastodon and Twitter posts

In what has now become a trilogy of posts1 I thought I would write up some of the custom tooling that I’ve written to allow me to keep up with Bluesky in a feed reader (in my case, still NetNewsWire). The social network started to take off in mid-2023, and though Mastodon covered my tech/retro interests pretty well, there were some folks (especially wraiters) who had migrated over. I initially used SkyBridge to allow me to treat it as a Mastodon instance in Masto Feeder, eventually moving to my own fork/instance after the shared one got overwhelmed. But the indirection2 and friction when hacking on it3 made it so that it never felt quite right. Some content (like videos) was lost in translation, and the lack of control over the timeline was frustrating - the options were either all replies (including to accounts I don’t follow) or none at all (which filtered out threads). I kept thinking of making my own native tool in Stream Spigot to generate a timeline feed for Bluesky, but the activation energy was too high.

In the meantime, Future Mihai did finally modernize Masto Feeder - in April and May of 2024 I rewrote it from Python 2.74 on App Engine as a SvelteKit app running on Cloudflare Workers. This was a way to both coalesce things on my preferred hosting platform and experiment with a new framework. I made some tweaks since then, but have not had a reason to touch SvelteKit much on this or any other project. Thus the codebase became effectively foreign to me, especially since Svelte 5 introduced runes and made other changes that I had not kept up with. This was definitely a contributing factor to the high activation energy I mentioned above.

Luckily it’s now 2026, and GPT-5.5 (in the Codex desktop app, combined with Context7) is a more qualified SvelteKit programmer than I am or have time to be at the moment. I still have opinions about architecture; for example I directed it to do some preparatory refactoring to share display code with Masto Feeder. The Codex reviews also gave me some additional confidence that I was on the right track, and so Sky Feeder was born. It generates an Atom feed from your Bluesky timeline, publishing it under a “secret” URL that can be used with any feed reader.

The only thing that was missed in the initial Codex-authored PR was how concurrent OAuth token refreshes should be handled - it turned out that serialization was necessary. I doubt that I would have avoided this bug if I had hand-coded everything - it’s the kind of thing that only happens after things have been running in production for a while. Fixing it was also a collaboration with Codex - I had it add additional diagnostic logging and then gave it access to the Cloudflare Observability MCP Server so it could inspect the output and validate the solution.

Empowered by all this, I had Codex build a third tool - Tweeter Feeder for generating a feed for any Twitter/X account. While I had mostly given up on the site, I had been using Nitter’s generated RSS feeds to follow a few people. Nitter is a great service to have around, but it’s quite inflexible5 and even less hackable6. I thus pointed Codex at the Nitter repo, and asked it to reuse the same reverse-engineered Twitter endpoints and auth mechanism for my tool. I only skimmed through the generated code (which has been getting more and more tempting), but it also pretty much worked out of the box, except for some escaping issues7.

Codex also worked out pretty well for more exploratory work. It bugged me that NetNewsWire (possibly due to this change) would show the Stream Spigot favicon for every post, adding redundant visual noise. I had a vague recollection that NNW had some support for per-post images, and Codex validated that the capability exists, but Atom feeds do not populate the avatarURL property of authors while JSON feeds do. It was thus a matter of adding this alternate output mode. Iterating on this was made pretty easy by the Codex remote control feature - it was pretty satisfying to see a bug in NetNewsWire on my phone, switch to the ChatGPT app, find the Codex thread and ask it to fix and redeploy the worker, go back to NNW and see the fix when refreshing the feed.

Stream Spigot homepage showing three tools: Masto Feeder, Sky Feeder and Tweeter Feeder
Stream Spigot: no longer a unitasker

This post came out as being more pro-Codex than I expected. I am also mourning my craft and am not sure how it will all turn out. While I have some general sense of the architecture of this codebase (since the fundamental decisions still date back to the hand-crafted era), I definitely don’t have the same intuition or map of everything in my head that I’ve had with other side projects. I did force myself to fix the escaping issue mentioned above by hand, just to prove that I can find my way around, but we’ll see how long that feeling lasts.

  1. See also 2012’s How I Consume Twitter and 2023’s How I Consume Mastodon 
  2. Bluesky (over atproto) to SkyBridge’s reimplementation of the Mastodon API to my tooling in Stream Spigot 
  3. SkyBridge is a server-side Dart app that runs on Fly.io, neither of which I was interested in learning more about 
  4. 😱 
  5. For example, the entire tweet goes into the feed item title, which worked in the days of 140 characters, but not in today’s world of think pieces disguised as tweets 
  6. I had somehow never heard of Nim before this 
  7. That universal constant - I assume the answer to the last question will also contain a wild " 

Post a Comment