AI News Hub Logo

AI News Hub

One-trick ponies are easy to replace

DEV Community
Alfredo Rivera

There's a structural weakness in almost every app built in the last decade and we don't talk about it much because we're all doing it. The app requires our infrastructure to function. The moment we stop paying the server bill, the moment the company gets acquired and wound down, the moment we make a business decision that doesn't work out — the app stops working. Everything the user put into it becomes inaccessible, or gone. We built a generation of software where the app and the company are the same thing. One-trick ponies. Easy to replace, but only after the damage is done. The user bears the cost of that fragility. Not us. Most apps have a line in their terms of service that says something like "you own your data." It's technically true in a legal sense. In practice it means almost nothing, because the data lives on infrastructure we control. We can read it. We can lose it. We can hand it over. We can take it with us when we shut down. "Your data" and "data we're storing on your behalf" are very different things. The gap between them is where most of the broken trust in modern software lives — data breaches, forced migrations, shutdown notices, subpoenas, acquisitions. Every one of those is a moment when users find out what "your data" actually meant. We built a world where users have to trust us not to misuse what we can never stop ourselves from seeing. This isn't a policy problem. Strong privacy policies don't close the gap — they just describe it more politely. The gap is architectural. The only way to close it is to change where the data lives. The idea I've been exploring: each user has their own SQLite database, on their own device, encrypted at rest with a key derived from something only they know. The app reads and writes to it directly. There is no server-side copy. Not "we anonymize your data." Not "we encrypt it at rest." It simply isn't there. This isn't a new idea. Local-first software has been a research topic for years. What's changed is that the primitives to build it properly in a browser finally exist. OPFS landed in all major browsers in 2023 — real persistent file system access with the synchronous I/O that SQLite needs. Before that you were stuck with in-memory databases that evaporated on tab close. cr-sqlite brings CRDT semantics to SQLite tables, which means two local databases can sync and merge correctly without a central authority to resolve conflicts. libp2p handles peer discovery and transport so devices can find each other directly — on a local network, over WebRTC, through a mesh that includes cloud nodes as peers rather than authorities. The server component doesn't disappear entirely. But it changes its role. Instead of holding data, it helps devices find each other. Instead of being the source of truth, it's a well-connected peer with better uptime. If it goes away, the app keeps working. The data is still there, on the device, where it always was. The direction I've been exploring is encoding behavior directly in the schema. SQLite lets you declare any string as a column type — it stores it verbatim and applies the closest standard affinity, but ignores what it doesn't recognize. A sync runtime can read those type names at open time and self-configure: CREATE TABLE notes ( id TEXT PRIMARY KEY, body REALTIME_TEXT, is_pinned LWW_INTEGER, cursor EPHEMERAL_INTEGER ); REALTIME_TEXT streams changes to connected peers as they happen. LWW_INTEGER gets last-write-wins semantics from cr-sqlite's CRDT merge. EPHEMERAL_INTEGER broadcasts for presence and expires — never persisted, never synced to offline peers. The same CREATE TABLE statement runs against a vanilla SQLite database. The schema is the configuration. No separate sync layer to set up, no registration calls, no framework to learn on top of the framework you're already using. I find this direction interesting but I'm genuinely uncertain whether it covers real developer needs or just the ones I've imagined. This is one of the things I most want to stress-test. If the data lives on the device and moves between devices as encrypted payloads, identity has to work differently too. There's no server handing out session tokens. The approach I've been working through is a keypair derived from a mnemonic — twelve words that generate the same Ed25519 keypair deterministically on any device. Your keypair is your identity. Your data is addressable by your public key. Messages for you are encrypted to it. Lose the mnemonic, lose the identity. No password reset. No support ticket. That's technically clean and I'll be honest — practically uncomfortable for most consumer use cases. Whether it's an unsolvable friction point or an abstraction problem that hasn't been cracked well enough yet, I'm not sure. Hardware keys, biometrics, social recovery schemes all exist as partial answers. None of them feel complete. I've been building toward this for a while and I'm at the point where the architecture makes sense to me but I'm aware that's not the same as it making sense to anyone else. The one-trick pony problem is real. Apps die and take user data with them regularly enough that we've stopped being surprised by it. The ownership gap is real. "Your data" as a marketing claim rather than a technical property is a kind of slow dishonesty that erodes trust in software broadly. Whether this specific approach to fixing those problems is the right one, or whether there are practical walls I haven't hit yet — that's what I want to find out. If you've thought about this problem and decided the tradeoffs weren't worth it, I want to hear why. If you've built something in this space and hit walls I haven't mentioned, same. The parts I'm least certain about: whether schema-encoded sync is actually usable or just elegant on paper, and whether keypair identity is a dealbreaker or a solvable UX problem. Both feel important to get right before spending a year building.