RawData

I made deletion a server guarantee, and it cost me a recording and most of my sanity

Phase 2 of BoomerClip's fix plan: a front door that trusted everything, videos anyone could grab, a server that forgot its viewers when it restarted, and a test that deleted my own recording. I made it to phase 3. Barely.

Let me set the scene.

BoomerClip has exactly one promise: seen once, then gone. Record your screen, share a link, and the video disappears when the viewer is done.

A few posts ago I admitted the promise was held together with good manners. The recording got deleted when the viewer's browser politely mentioned it was leaving. The code review had found ways to fetch a recording, or keep one alive, that never involved the viewing page at all.

So Phase 2 had one job: make the server guarantee deletion, no matter what the browser does.

Check the changelog dates. The repo clean-up, the viewing-page fixes, the entire code review, all of Phase 1, and both halves of Phase 2 are stamped with the same day: 13 September. I don't know how. I've been told it happened. This is what I remember.

Part one: the front door was wide open

I started with uploads, feeling optimistic. That lasted about four findings.

The upload form could simply ask for a recording that never expires.

Read that again. The app whose entire identity is disappearing videos had a code path that let an upload mark itself as permanent. It came with its own storage branch that dutifully kept it forever. The self-destruct button had an off switch, and the off switch was reachable from the form.

It's gone. Every recording is ephemeral now, and the branch that honoured "please keep me forever" is deleted.

Then: the server trusted whatever file you handed it. Say it's a video? Sure, it's a video. Uploads are now probed in a folder the web server never serves, and kept only if they genuinely are WebM or MP4 with an actual video stream inside. The extension and content type come from what the probe found, not from what the upload claimed. Empty or broken files get rejected with a message, and a failed upload leaves nothing behind.

Error responses stopped handing out stack traces like party favours.

And the duration bug, the one from the very first post, the one I thought I'd fixed in 2025? Finally dead. Chrome's recordings have no duration header, so the length is now measured from the last packet in the file, instead of being stored as the string "N/A".

Tests went from 10 to 39. I felt great. I should not have felt great.

Part two: anyone could just take the video

Here is the finding that made me put my head on the desk.

The recording files were served from a public folder.

If you had the address, you could download the video directly. A direct download never joins a viewing session, so nothing would ever know you'd watched it, and nothing would ever delete it. All the careful confirm-before-refresh, grace-period, reconnect logic from earlier posts was guarding the front of a building with no back wall.

The public folders are gone. When the viewing page opens now, it joins a viewing session and receives a token. The video and its thumbnail are served only to a request carrying the token of a viewer who is present right now. No token, no video. The thumbnail is locked too, because a thumbnail is literally a frame of someone's screen.

Then came the question that ate an embarrassing amount of thinking: how long should that token live?

The answer: the token lives exactly as long as its viewer is present. It never dies under a paused player, and it dies the instant that viewer leaves. Not a second before, not a second after.

The server that forgot everyone

I thought the token was the hard part. The token was not the hard part.

Viewers were tracked in the server's memory. Lovely, right up until the server restarts. A restart forgot every single viewer. A recording being watched at that moment would then sit there until its 24-hour expiry, and its files would stay on disk forever. A crash doesn't send a goodbye. A crash just leaves.

I went through three designs. Two of them tried to hurt me.

  1. Copy the viewer list into Redis, and on startup start deletion timers for everyone found. Simple. Also, if two server processes ever share Redis, each one starts timers for the other's live viewers and deletes recordings people are actively watching. Beautiful. Rejected.
  2. Have Redis announce when a viewer's key expires. Those announcements are fire-and-forget. If no server happens to be listening at that moment, the event evaporates. A deletion guarantee built on "hopefully someone heard" is not a guarantee. Rejected.
  3. Leases. Every viewer gets a "present until" time 30 seconds ahead. The server holding their connection renews it every 10 seconds. A regular check deletes any viewed recording that nobody is present for.

Leases won, because they need nobody to report the failure. That's the whole trick. A crashed server can't tell anyone its viewers are gone, and it doesn't have to. The leases just run out, and whichever server is alive next does the deleting. A viewer whose Wi-Fi blinks still gets their 15-second grace. A viewer who reconnects presents the same token and carries on watching.

And a graceful shutdown, like restarting for an update, stops tracking before the connections drop. Otherwise every restart would look like every viewer storming out at once.

Cleaning up the bodies

Deletion itself turned out to have been in the wrong order.

The record pointing at a recording could be removed first, and if deleting the file then failed, you had a video on disk that nothing referred to. An orphan. Unfindable, undeletable, eternal. Now files go first, and a failure retries instead of shrugging and moving on.

For anything that still slips through, there's a storage sweeper. It runs at startup and every ten minutes, removing recording files whose record has vanished and abandoned uploads, once they're 15 minutes old.

(Remember that sweeper. It matters later.)

Meanwhile, in the smaller-fires department:

The sweeper strikes

Here's where I nearly lost it.

I don't trust a test until I've watched it fail. So every fix in this phase got broken on purpose, to prove its test would catch the break. Twenty-nine deliberate sabotages.

One of them switched off the sweeper's safety check: the part that confirms a file really has no record before deleting it.

I made that edit in the real source file.

While the development server was running.

Configured to restart whenever a file changes.

It restarted. On my sabotaged sweeper. Which immediately ran its startup pass. Which deleted the video and thumbnail of one of my real local recordings.

I didn't even see it happen. I found it later, because a recording's record was still there and its files weren't. So I did what any calm, well-adjusted developer does: I stared at modification times.

The storage folder was last changed at 10:09:44.

The sabotage went in at 10:09:45.

One second. The files were gone, and there was no getting them back.

~ Jer