DEV Community

Jin Xu
Jin Xu

Posted on Fully Autonomous

Shipping a Privacy-First "Super App" With Expo SDK 57, SQLite and Zero Servers

At one point my phone had a calendar app, a budgeting app, a habit tracker, a notes app, a password manager, a mood journal and a shopping list app. Each wanted its own account. Three wanted a subscription. All of them wanted my data on their servers.

So I built LifeDesk, a single app that covers all of that and more. I gave myself one hard rule: no backend. No LifeDesk account, no LifeDesk server, nothing to breach. This post covers what that rule forced me to figure out.

1. The rule: your phone is the database

Everything lives in one SQLite file on the device (expo-sqlite, WAL mode). Onboarding asks for one thing, your name, and even that never leaves the phone.

This changes a lot. Without a server there's no "fix it in the API": every bug fix that touches data has to ship as a client migration and run correctly on thousands of devices in states you can't see.

2. Stack

  • Expo SDK 57 / React Native 0.86, expo-router for file-based navigation (~120 screens)
  • expo-sqlite for storage, expo-secure-store for secrets, expo-crypto for encryption
  • i18next for 5 languages (EN, VI, ES, FR, ZH)
  • Firebase used only for force-update config (Remote Config), system push topics and the AI gateway. No user data is stored there.

3. Migrations are your API versioning now

The schema has gone through 20+ versions. What I learned:

  • Run migrations sequentially and idempotently. Each step checks whether its table or column already exists, because dev builds and interrupted upgrades leave devices half-migrated.
  • Turn on PRAGMA foreign_keys from the start. I didn't, and later had to write explicit clean-up code for orphaned rows (habit logs, order items, budgets of deleted categories).
  • Never lose history when a user deletes a category. Records move to a fallback "Other" category instead. People forgive a missing feature. They don't forgive lost data.

4. A vault on a phone: don't derive the key from the PIN

The Vault stores passwords, card numbers and CVVs encrypted with AES-256-GCM. The tempting design is "derive the key from the user's 4-digit PIN". I chose not to:

  • A 4-digit PIN has only 10,000 possibilities, which is too weak to be a key on its own.
  • A forgotten PIN would make the data unrecoverable.

Instead, a random per-install key lives in the iOS Keychain or Android Keystore (expo-secure-store). The PIN and biometrics gate access to it. The Vault re-locks the moment you leave it, and it never shows ads.

5. Backup without a server: Google Drive's appDataFolder

The obvious objection to local-first is "what if I lose my phone?". My answer is Google Drive's appDataFolder, a hidden, app-scoped space in the user's own Drive (drive.appdata scope). The app can't see your other files, and I never see your backup.

When a user deletes their account, the app also deletes the Drive backup and revokes the OAuth grant. That's important for both the store policies and user trust.

6. An AI assistant that writes into SQLite

The feature I'm proudest of: you type or say "Lunch 12 dollars" or "Remind me to call mom tomorrow at 7", and the right record lands in the right module.

How it works:

  • The model returns exactly one JSON object that must match a strict schema.
  • The prompt includes the user's real category list and today's date, so "next Tuesday" resolves correctly.
  • If a required field is missing, the AI asks instead of guessing. A short reply like "12k" is merged into the pending request rather than starting a new one.
  • Every result shows as an action card with "View" and "Edit" buttons, so the user always sees what was written.

Validating everything before it touches the database mattered more than prompt tricks.

7. Paying for it without wrecking UX

LifeDesk is free and ad-supported. My rules:

  • Banners only on the main tab screens. Never in the Vault or on PIN screens.
  • Every rewarded ad is opt-in, in exchange for points the user can spend (unlocking PDF/CSV export, freezing a habit streak).
  • App-open ads are rate-limited, so you don't get an ad every time you switch apps.

8. What I'd do differently

  • Turn on foreign keys on day one.
  • Treat backup and restore as a core feature from the first sprint, not a late add-on.
  • Scope smaller. Nine modules is a lot to polish at once.

Try it

LifeDesk is free on Google Play and the App Store. I'd especially love feedback from people who care about local-first software: what would make you trust an app like this with your data?

Top comments (1)

Collapse
 
alexshev profile image
Alex Shev •

Local-first storage is a meaningful privacy choice, but the product still needs a recovery story. Documenting encryption boundaries, export/delete behavior, and what happens when a device is lost makes “zero servers” a verifiable user promise rather than just an architecture label.