Encryption-at-rest
The SQLite database is encrypted using SQLCipher viasqlcipher_flutter_libs, which replaces sqlite3_flutter_libs in pubspec.yaml. All data written to disk - tasks, check-ins, events, mana state - is encrypted at rest. The feature flag table is not sensitive and is also encrypted as part of the same database.
The app bundle itself is not encrypted.
Key management
On first launch, a 256-bit key is generated using the platform’s cryptographically secure random number generator and stored in the platform keystore:| Platform | Storage | Setting |
|---|---|---|
| iOS | Keychain via flutter_secure_storage | accessibility: afterFirstUnlock |
| Android | Android Keystore | Default flutter_secure_storage settings |
afterFirstUnlock means the key is accessible after the user’s first unlock since boot, supporting background tasks and notifications without requiring biometric authentication on every access.
The key is never written to iCloud, Google Drive, or any cloud backup. This is enforced by the storage accessibility settings.
Device restore
If a user restores a device from a cloud backup, the database file may be restored but the encryption key will not be. The database is unreadable without the key. Users are informed during onboarding:“Your data is stored only on this device. It isn’t backed up to iCloud or Google Drive. Use the export feature to keep a copy.”
Lost key / biometrics change
If the key is lost (device wipe, Keychain reset, failed migration), the database cannot be recovered. The user must wipe and start fresh. This is mitigated by the Export/Import feature.Corruption detection
If the database fails to open or a query returns an integrity error, classify it ascorrupted_local_state per the Error Taxonomy.
Surface a calm message:
“Something went wrong with your data. If this keeps happening, contact support and we’ll help.”Attempt a full rebuild from the event log before surfacing the error to the user. If the rebuild also fails, surface the message and offer the delete-all-data flow as a recovery path.
Delete all data
Accessible from Settings. Deletes everything stored on the device. There is no network call - all data is local.Flow
- User taps “Delete all data” in Settings
- Confirmation screen appears with clear, explicit copy (see below)
- User taps the confirmation button
- Wipe executes atomically
What is wiped
- SQLite database file
- Encryption key in the platform keystore
- All in-memory cached state
What is not wiped
shared_preferences(settings and feature flags) - these intentionally survive. Notification preferences, theme, and accessibility settings are not health data and should not reset with the database.
Copy rules
The confirmation screen uses calm, factual language. No guilt, no urgency, no “are you sure?” framing. Primary copy:“This will permanently delete all your data on this device. Your app settings will be kept. This cannot be undone.”Confirmation button:
“Delete everything”Cancel:
“Go back”Avoid:
- Exclamation marks
- Questions (“Are you sure?”)
- Conditional urgency (“Warning:”, “Caution:”)
- Blame framing (“You are about to…”)