Update
June 6, 2026

Ghost Routes and Disappearing Data

Some bugs announce themselves loudly. A crash, a stack trace, an obvious error in the logs. Others are quiet. They sit in your codebase for weeks, working correctly in every test, never throwing an exception, simply returning {"detail": "Not Found"} whenever anything real tries to use them. This is about a quiet bug.

The inbox0 API had about twenty endpoints defined on a shared router -- credentials, config, OwnLifeOS integration, and more. The code was there. The functions were written and reviewed. And yet every single one of those endpoints returned 404. Always had. Not one of them had ever worked in production.

The root cause was a single line placement. When you register a router with a FastAPI app, it copies whatever routes exist on that router at that moment. The registration call was at the top of the file, right after the router was created. All the actual route definitions came thousands of lines later. Python executes top to bottom -- by the time any route was defined, the registration had already come and gone with an empty router. The app had been serving twenty ghost routes that existed in code but not in reality.

The fix was moving one line to the end of the file. Two minutes of work. Weeks of silent failure.

What made this especially insidious: the app started cleanly, reported no errors, and passed all tests. The test suite invoked route handlers directly, bypassing registration entirely. The type checker had nothing to complain about. The only way to catch it was to actually use the feature and notice the 404.

That same week, a second disappearing-data bug surfaced. Diagrams were vanishing between sessions. The investigation pointed at the E2E test suite, which had been deleting the live production databases before every test run -- hardcoded to the same paths the server uses. Every time tests ran, real data was wiped. The fix: teach the server to accept database path overrides via environment variables, then point tests at isolated throwaway paths. Tests now get a clean slate without touching anything real.

A third data issue was subtler. inbox0 stores its database in a named Docker volume, seeded from a file in the repo on first creation. That seed file had not been updated since May 21. If the volume was ever destroyed and recreated -- say, from an accidental teardown -- any accounts added after that date would be silently restored to a five-week-old snapshot. The fixes: mark the volume as external so Docker refuses to delete it on teardown, and replace the seed with the current live database.

Alongside the bug work, the suite got a rebrand. OwnLifeOS is now MyOwnLifeOS everywhere a human reads it -- landing pages, login screens, footers, Amplitude tracking labels. Not the domain. Not the environment variables. Not the config keys. Just the display text.

What made the rebrand effortless was a discipline that had been baked in from the start: a clean separation between the name as infrastructure and the name as identity. Environment variables, secret keys, and internal service references all used a consistent prefix that was deliberately different from the display name. That meant a single search-and-replace across HTML files was the entire rebrand. No config touched. No secrets rotated. No service endpoints broken. Thirty seconds of work, seven apps updated.

The type scale pass that followed told a similar story. All seven apps share the same CSS custom property names -- the same tokens for color, font family, spacing, and radius. When one app needed a pill badge or a footer link, it reached for the same variable names every other app used. Bringing them all into alignment was a matter of reading one app that had been done carefully and propagating those values outward. There was no archaeology, no hunting for magic numbers buried in component styles. The system made consistency achievable.

A rebrand that takes thirty seconds is not luck. It is the payoff of treating naming and structure as first-class decisions from day one.

The landing pages also got a type scale pass. Across seven apps, pill badges, card subtitles, footer links, and divider text had drifted to different sizes over time. The fix was not to pick the smallest and normalize down -- it was to bring everything up to the sizes that looked right on the app that had been done most carefully, then hold that line across the suite.

Each of these bugs had the same character: quiet, structural, only visible when you actually used the thing. No tests caught them because the tests were designed around what the code said it did, not what it actually did. The lesson is not to write more tests. It is to use the thing. Sit down with it as a user. Click the buttons. Watch the network tab. The bugs that survive code review and test suites are almost always caught by someone who just wanted to check their email.

#inbox0 #FastAPI #Debugging #DataIntegrity #Docker #Playwright #MyOwnLifeOS #BuildInPublic
Ovais Quraishi