← All posts
Privacy & Product

Adding a Paid Tier Without Betraying Local-First

Toldari keeps all your data on your device, with no account and no backend. Adding a paid cloud tier meant storing data off-device for the first time, which is a one-way door for a privacy-first product. Here is the model I used to walk through it without breaking the promise.

In building Toldari, I knew early I wanted to go local-first in the strict sense. Everything lives on the device, there is no account, there is no server, and the app works completely offline. That is a deliberate privacy stance, and it is part of why people trust the product. So when I decided to add a paid tier with genuine cloud value, like backup and sharing, I had to confront the fact that I was about to store a user’s data off their device for the first time. For a privacy-first brand, that is a one-way door. You cannot quietly walk it back later.

I did not want the paid tier to compromise the free experience, trap a user’s own data behind a paywall, or create a large pile of personal data I would then be responsible for securing and regulating. So I wrote down a small set of rules before building anything, and they did most of the hard thinking for me.

Premium is additive and reversible, never a gate

The first rule is that the local-first app stays completely intact. It launches, works, and stores everything locally with no account and no network. Premium lights up only after sign-in and an active subscription, and it only ever adds capability on top.

The second rule is the one that protects users most: the local store remains the source of truth. The backend is a synced mirror and a transport, never the only copy of a user’s own data and never the thing standing between them and it. That single decision is what makes leaving premium safe. When a subscription lapses, the premium interface hides and syncing stops, but the local data is untouched. Cloud copies enter a retention window and then purge. Re-subscribing restores from the local store that was authoritative the whole time. Going on and off premium never destroys anything, because the cloud was always a convenience layer, not the home of the data.

The remaining rules keep the data footprint small and honest. Sharing is opt-in and layered, so the user chooses what leaves the device, starting from local-only even for subscribers. A non-subscriber’s data may exist on the server only for the duration of a sharing transaction and is then purged, so I never build a persistent record of someone who never subscribed. And there is no directory and no discovery, no user search or “people you may know,” because every share is one user sending to one recipient they already have the contact details for.

That last guarantee, that non-subscriber data never outlives a transaction, is the easiest one to violate by accident. I treat deletion as the default, delete in the same transaction that claims the data, add a time-to-live backstop in case something slips through, and write tests that fail if data outlives the transaction it belonged to. A privacy guarantee you cannot test is a privacy hope.

The two supporting decisions

Two smaller choices fall out of this stance. For authentication, I went password-free, using only the native Google, Facebook, and Apple sign-in sheets, with the backend validating the provider token and minting its own short-lived app token. No password store means no password breach surface and no reset machinery to maintain. The detail worth flagging to anyone doing this: once you offer Google or Facebook sign-in on iOS, Apple’s guidelines require you to also offer Sign in with Apple, and Apple hands you the user’s name only on the very first authorization, so you have to capture it then or lose it.

For the subscription itself, I used a subscription management layer over the native store billing rather than integrating each store directly. Apple and Google require their own in-app purchase systems for digital subscriptions, so the only real choice is whether to handle receipt validation and renewals yourself or let something abstract it. The pattern that worked is to read entitlement on the client for responsive interface gating, but to re-validate on the server through a webhook so a tampered client cannot reach paid endpoints. The entitlement rides in the app token for the fast path, and sensitive operations re-check the authoritative row.

What the rules bought me

Writing these rules down before building was the highest-leverage time in the whole project. Because the local store stayed authoritative, no design discussion ever ended with “but then the user loses their data.” Because non-subscriber data is transactional, the security and regulatory surface stayed tiny. And because I decided the privacy posture up front rather than discovering it feature by feature, I never had to walk back a promise I had already shipped. If you are taking a privacy-first product to a paid tier, the cloud architecture is downstream of one question: when someone stops paying, what happens to the data they already had? Answer that one first, and most of the rest decides itself.