I Replaced the Heart With a Library Stamp
#261007 Build

I Replaced the Heart With a Library Stamp

, 3 min read

How the like button on my Ghost theme became a red SEEN stamp that readers can sign, running on a Cloudflare Worker and a D1 database.

The heart button on this blog took an evening to build and never went live.

It had a count, a Cloudflare Worker to keep the count in, a setting in the theme and a setup guide. All that was left was a few minutes in the Cloudflare dashboard to switch it on.

I had asked for a heart because every blog has one, and nothing is really wrong with a heart. A post can call for more than a like, though, or less, and a heart is always one among a lot of them. People like to leave their mark, and so do animals. A heart is the more positive of the two. Leaving a mark is the one we all want to do. I wanted something that lets a reader say I was here, I saw this.

Every post on the homepage is a pastel card with a filing code in the corner. The first homelab post is #HO260924, the section followed by the date it went up. I was building the theme with Claude, and Claude was the one who noticed what the codes implied. Cards with filing codes are library cards, and the mark a library leaves on a card is a date stamp. So the heart became a stamp that presses SEEN and the day's date onto the post in red ink.

The first mockup had a large stamp settling into the corner of the card, a handwritten slip asking for a name with Sign and Skip buttons, and a borrower's slip listing every date and every reader. It was a faithful library, and a lot of furniture for one tap.

I cut it down to one button with a stamp icon and the count, and at zero it shows no number at all. Pressing it tips the stamp upright, gives it a small wiggle and lays it flat again. The impression appears with SEEN and the date, and inside it, in grey, a line that says "leave name". The line waits five seconds and fades. A reader who ignores it stays anonymous, which is a perfectly good way to have been somewhere. A second tap brings the offer back, above the three most recent names and "+ x more".

Choosing where a stamp lives took longer than the animation. A Ghost theme has nowhere to keep a number. The obvious home was the Dungeon, the NAS that already serves this site, but then stamps would stop whenever the house lost power or internet. Somebody else's hosted button was out as well, since the first post on this blog is about owning what I depend on. That left a Cloudflare Worker on the free plan. The abandoned heart kept its counts in Workers KV, which can lose one when two arrive in the same instant, and a stamp with a name on it is a row anyway. The Worker moved to D1, Cloudflare's small SQLite database, where each stamp is a row holding the post, the date and an optional name. The free plan allows 100,000 row writes a day, which leaves some headroom.

The Worker answers on its own workers.dev address. At home, Pi-hole sends christingeorge.com straight to the NAS, so a Worker on my own domain would be invisible from inside my own house.

A stamp is final. The Worker can't tell whether the person asking to undo one ever stamped, and an open undo would let anyone drain a count. Names appear straight away, without waiting for me, so they are filtered hard. A name can have letters in any language, spaces, full stops, hyphens and apostrophes, up to 24 characters, and there is a short list of blocked words. During testing, one visitor tried to sign as a link and was refused. That visitor was Claude, playing two readers against a test copy of Ghost.

A visitor's address is held in memory long enough to limit them to a few stamps a minute and is never stored, so the whole record of a reader's visit is a date and whatever name they chose to give.

The setup guide says about ten minutes. It was written for a Cloudflare dashboard that had since rearranged itself, so I went looking for D1 under Storage and Databases and found R2 Object Storage, and went looking for Bindings and found every tab except Bindings. The last step was opening the Worker's address for one post in a browser. It answered {"count":0,"names":[],"more":0}. Nobody had been here yet, in valid JSON.

Stamps replaced the heart on posts and on the Now cards. The likes Worker was deleted without ever having counted a like. The stamp is now being pulled out of the theme so it can go on any site, under the name Red Was Here.

There is a stamp button at the bottom of this post. As I write this, it has no number next to it.