Haven Docs

Data storage

Everything on this page lives on the device. Nothing here is synchronised, backed up to a server, or transmitted. allowBackup is false, so Android's own backup does not carry it off the phone either.

The database#

One Room database, currently at schema version 5, with five tables:

TableHoldsWritten by
Block rulesEvery rule the matcher uses: synced operator domains, wildcards, keyword tiers, encrypted-DNS resolvers, the allowlist, and the user's own filtersThe bundled seed, the sync worker, the user, the content watch
Block eventsThe transparency log: which host was blocked, when, by which layer, and the matched ruleThe DNS layer and the content watch
Accountability eventsThe history of protection changesThe accountability manager
Financial pause eventsWhich guarded app was paused, when, and whether the user proceededThe financial pause screen
Learned sitesDomains the risk engine taught itself, with the score, the reasons, a structural fingerprint, first/last seen and a hit countThe content watch

Two design points#

User rules and synced rules share one table but not one authority. The user's personal filters sit under their own rule kinds, and the sync path maps incoming deltas through an explicit allow-list of kinds that does not include them. A remote update therefore cannot write or delete a rule the user made.

Migrations are real, not destructive. The version-4-to-5 upgrade adds the learned-sites table with a written migration rather than falling back to dropping the database, so an app update keeps the user's block log, financial pause history and personal rules intact. For an app someone has been using for months to stay quit, wiping that history on an update would be a real loss.

Preference stores#

Fourteen separate DataStore files rather than one. The separation is deliberate: it keeps concerns that must never influence each other physically apart, and it means account data can be erased without touching protection settings.

StoreHolds
ProtectionWhether protection is on, the cooldown length, a pending disable request and its reason, the applied blocklist version, the last security check, first-run markers
LockThe salted PIN hash and whether biometric unlock is enabled
Financial protectionWhether the layer is on, the guarded packages, the hard-lock date, the recurring schedule, the emergency timestamp
AccountabilityThe partner's name and email, the pairing code, and whether they confirmed
App detectionPackages the user released as false positives
Learned siteHosts the user overturned, which the content watch then leaves alone entirely
RiskWhether the content watch is on, its thresholds, and per-source toggles
BudgetThe monthly plan and the balance ledger, as JSON
BuddySam's treat jar, growth, equipped cosmetics and chosen trick, as JSON
ProgressionThe cached streak and best streak
SubscriptionThe cached Pro entitlement and since-when
PersonalThe onboarding goal and chosen start profile
LanguageThe chosen interface language, plus a synchronous mirror read at startup
AppearanceLight, dark or follow-system
AuthThe session and, for local accounts, the credential record

A note on the "personal" store: it exists specifically so that soft, personal choices can never end up in the same place as anything that decides whether blocking runs.

Deliberately not persisted#

Some state is held in memory only, and would be wrong to persist:

StateWhy it must not survive a restart
That the user just proceeded through a financial pauseA grace period should not survive a reboot
That the user just asked to uninstall a flagged appThe same; after a restart a still-installed gambling app is simply blocked again
Queued partner emailsA message about something that happened last week is noise
Warned-host memory in the content watchEscalation should reflect this browsing session
Skipped setup stepsAndroid silently revokes the accessibility permission on every app update, so a persisted "don't ask again" would turn the strongest layers off for good

What is never stored anywhere#

  • Page text. Read in memory during a scan and discarded. Only a matched domain name is written down.
  • Raw PINs or passwords. Only salted hashes.
  • A browsing history. The block log records what was blocked, not what was visited.
  • Financial account data. Haven holds no banking credentials and connects to no bank.

Erasing it#

ActionEffect
Delete a personal filterRemoves that rule
Release a learned siteRemoves the rule; the audit entry can be forgotten too
Release an app in App-checkStops flagging it
Remove the accountability partnerClears the partner store
Delete the accountErases the account store locally and the server-side row
Uninstall HavenRemoves every table and every preference store with the app