Applet — The Autopsy
Alflora Research · 05 Aug 2026
Tl;dr
- Applet: log in with your portal account, get an emulated Android 15 device in a browser tab. One device per user, one free compute hour a month.
- redroid + ws-scrcpy + a Node session manager with a SQLite quota. Everything worked: boot, stream, auth, billing.
- Killed the same day it shipped — the result was a laggy emulator in a tab that nobody needed.
- The domain now serves a permanent 402 Payment Required. Correct tombstone for a service that died of its own business model.
The patient
Applet was supposed to be a platform feature: sign in with your Alflora account, press START, get an emulated Android device streamed into your browser. One device per user at a time, four devices service-wide, and a quota of one compute hour per month to keep the rack honest. The pitch wrote itself: your apps, testable anywhere, no local setup.
The stack was the light one, chosen deliberately. No full emulator, no GPU: redroid — containerized Android 15 rendering on CPU via SwiftShader — plus ws-scrcpy pushing H.264 over a WebSocket to a web player. A small Node backend owned the lifecycle: who you are, whether you have minutes left, which containers are yours.
Everything worked
Read that again, because it's the whole point of this autopsy. Android 15 booted in the container (once we taught it use_memfd — kernel 7.0 has no ashmem). The streamer connected, the video flowed, touch input worked. Login was wired straight into portal's own account store — same email, same bcrypt password, no second account system, no identity provider in the middle. The quota ticked down only while a device actually ran, banked on stop, and hard-stopped you at zero. Four slots, one per user, idle auto-stop, orphaned containers swept on boot.
We fixed real bugs to get there: adb retry loops for cold boots, a WebSocket upgrade path that ws-scrcpy builds with no path prefix, a duplicated Upgrade header that nginx helpfully supplied twice. By the evening the service was, by any engineering measure, done.
Cause of death
And then we used it. A soft-streaming Android in a browser tab, a beat behind every input, good for a demo and not much else. Android Studio runs a better emulator on any laptop. A real phone is a better device. The one free hour was too little to do serious work with — and had it been more, it would have been a standing invitation to turn our electricity bill into somebody else's CI farm. The service was perfect and pointless at the same time.
So it was shut down the same day. Not rolled back in anger — dismantled politely: containers down, images deleted, kernel module unloaded, code archived, domain repointed to a 402. Payment Required. The only status code that doubles as a cause of death.
What the corpse taught
De-risking first was right: we proved redroid and the streamer by hand before a single line of backend existed, which is why the build itself never got stuck. The failure was upstream of all that — in the decision to build a quota system before there was demand worth rationing, and to assume "it runs in a browser" is a feature rather than a compromise.
A quota is easy to write and worthless without users who'd hit it. A free compute tier is a euphemism for an open invitation to abuse. And a service that dies in a day is not a wasted day: it's the cheapest possible way to learn that an idea was a demo, not a product. Tuition, paid in hours, receipt published here.