Postecard

It’s been more than a year since Elon Musk bought Twitter. It got demonstrably worse this January when third-party apps were cut off, and the gradual decline has continued since then. The (many) ads I see are almost all from newly-created accounts for dropshipping companies, whether under their AI-generated storefront names, or AI-generated “real people” and their testimonials. Many of the actual real people I followed have left, most of the media sources I followed are still there and it’s still the most reliable and quick way for me to follow them, so I’m still on Twitter. I’m also on Mastodon, Bluesky, and Posts. I made a Threads account to reserve my handle. Too much, right?

Too much! So I also made a one-person Twitter app for myself. Yes, another Twitter clone, the worst one possible, to add to a roster of feeds of small text posts to refresh compulsively. The goal is for it to be the place where I post tweet-sized updates that are actually meaningful to me, and I will try to reduce my use of all the other places to only reading or not at all. And this new one is broadcast only, no replies or likes or other engagement — great for mental health! Followers can either go to my website to read the latest (and only the latest) tweet, or subscribe to an RSS feed of every tweet to automatically get new ones and go through the old ones.

I call it Postecard because it reuses the backend from Pastecard, but, you know, it makes posts.1 Of course, there were a few hiccups along the way. For example, it literally uses the Pastecard backend, at the Pastecard domain, but I want it to display on my personal domain. So the actual tweet text is an iframe within the little tweet embed, to avoid cross-domain restrictions. Otherwise, the base functionality is the same: a little PHP script that takes my tweet input and writes it to a text file, and a little Javascript that reads the text from that text file when the page loads. There’s a little extra Javascript in that iframe to parse out and hyperlink URLs, making sure to load them in the parent frame. And, naturally, a little extra PHP to add the new tweet to the RSS feed file. I have a simple Shortcut saved as an “app” to both my phone and laptop that prompts for the new tweet text and sends it to the PHP script.

One thing to come out of the downfall of Twitter has been a resurgence in the mindset of owning or controlling your content online. A blog at your own domain name, a social media presence that you can decouple from the provider and take your follows, followers, and archives with you. This simple little project is a combination of both those things. I may not stick with it for as long as I have with these other things, but I do admit it feels pretty cool here at the start.

Update July 14, 2025

I’ve been posting tweets to my own little single-player Twitter for over a year and a half and really enjoyed it, with one exception. The front end lived here on my personal website and the back end (and feed) lived at Pastecard. So I started looking into a solution that would let me combine the HTML sandbox that is my personal website, with the freedom of a server that I could program and save data to, while still being completely free. And I found it in Cloudflare Workers.

The amount that you can do at Cloudflare for free, let alone the amount of internet stuff that Cloudflare does, period, is kinda mind-blowing. Their Pages product offering is a lot like Github Pages, where this website used to be hosted (spoiler!) — essentially unlimited static files hosted for free and distributed across a fast, global CDN. Their Workers product is managed infrastructure for web apps written in Javascript with a free tier that exceeds any usage I’d ever have, and the same for their KV data storage. I migrated my personal site as-was to their Pages product no sweat, and started to think about a standalone Worker that would handle the storage, display, and feed generation of my lil tweets.

Being my first attempt at a JAMstack project, every step forward required at least one Google–Stack Overflow round trip. So, begrudgingly, I created a Claude account. They advise being really comprehensive and verbose in your initial prompt if you’re going to stay on the free tier, so I laid out my whole plan to use Cloudflare Workers and KV, and all the functions I would need, and it basically nailed it on the first try. There were plenty of things I finessed to make it look the way I want, but it wrote endpoints to post (i.e. save to KV) a new tweet, bulk-post a CSV of all my previous tweets, return the newest one for my home page, return all of them for an archive page, and return RSS and JSON feeds of all of them for following purposes.

The biggest hangup was purely my fault. I thought I could leave the site in Pages, the now-working tweet functionality in Workers, and set up routes on the main site domain that pointed to the worker endpoints. There’s documentation and settings that imply this is still possible, but I couldn’t get it to work. So now the entire site is actually a Worker web app, with routing logic such that the tweet-related endpoints go to their JS functions, and everything else goes to one big folder representing the site as it’s always been. Everything outside of these tweets I can continue to play around with as static files, and never have to worry about how they interact with the functional code.

There were some things Claude did that needed to be corrected. One was a sequence of helper functions I had to turn plain quotation marks into smart ones, and hyperlink URLs. Claude implemented them in the opposite order, meaning <a href=""> turned into <a href=&ldquo;&rdquo;> and that was no good. I also spun my wheels a long time on timestamps. Since Cloudflare is distributed globally, calling Date.now() as Claude did kept giving me the Unix epoch, since otherwise you could get one time from one server and a different one from another. After working around that and then also dealing with accounting for daylight saving time, I shifted the responsibility for attaching a timestamp from the server side to the client side — which, by the way, is now a full iOS app that Claude helped me write too.

All of that to say, I now have my ideal setup for this little personal Twitter app. It is fully integrated into my personal website, as it should be. They’re one and the same. So the Postecard name doesn’t really apply anymore, sadly.