Where your data lives
Every domain (workouts, exercises, routines, runs, sleep, nutrition, body measurements, goals, plans) is stored in one local SQLite database on the device. Rows use client-generated identifiers and cascading deletes, so deleting a workout takes its sets with it and never leaves orphans.
The schema is versioned. Upgrades are incremental migrations that add tables and columns rather than rewriting your data, and code that reads newer tables checks that they exist first, so a device that has not migrated yet degrades a screen instead of crashing on it.
The flip side of owning your data is being responsible for it. There is no cloud copy: if you lose the device without a backup, the journal is gone. Take a backup.
What touches the network
| Feature | Talks to | When |
|---|---|---|
| Map tiles | A tile provider | While a map is on screen during or after a run |
| Food lookup | Open Food Facts, anonymously | When you search a product you do not already have, or scan a barcode |
| AI Coach | The provider you configured | When you send a message or a label photo |
That is the whole list. There is no analytics SDK, no crash reporter phoning home, and no background sync. Logging, timers, routines, progress, manual sleep entries, the body tracker and periodization all work with the radio off.
Backups
Settings → Backup writes a JSON file containing the rows of every table plus a version marker and an explicit manifest of the collections that must be present. That manifest exists so a truncated or half-written file cannot be mistaken for a legitimately empty database on restore.
What you can do
- Save a backup through the system save dialog, named by date. On Android it goes to the public Downloads folder when that is accessible, and to the app's own documents directory otherwise.
- Share a backup straight to another app.
- List local backups, newest first, and delete ones you no longer want.
- Restore from a file, from picked bytes, or from a JSON string you paste in, useful when the file is stuck in a chat app.
How a restore behaves
- The envelope and every collection are validated before anything is deleted.
- Only then is the current data cleared and the backup inserted, all inside one transaction.
- Any error rolls the whole thing back, leaving your existing data untouched.
- The number of restored rows is reported when it finishes.
Restoring replaces your current journal with the contents of the backup. There is no merge, so take a backup of what you have now before restoring something older.
What a backup deliberately excludes
| Excluded | Why |
|---|---|
| AI provider tokens | They live in secure storage, not the database. A backup can be shared without leaking a key |
| AI conversations and proposals | Transient by design |
| Detailed sleep-stage epochs | Restored sessions keep their summary metrics and mark the per-epoch detail as unavailable rather than fabricating it |
| Remote food-search cache | Disposable and refetchable. Your manual, favourite and previously used foods, along with their variants and servings, are kept, so meal history and saved meals stay intact |
| Permissions and live sessions | Device state, not data. You grant permissions again on the new device |
Other exports
- Workouts as CSV: save or share for a spreadsheet.
- Nutrition meal log as CSV: the same for food history.
- A shareable session summary: for when you just want to send someone what you did today.
File references stored in the database are made portable when a backup is produced, so progress photos survive a move to a new device.
Deleting data
Individual records can be deleted from their own screens. Settings also exposes bulk resets (wiping all workout data or all nutrition data) as part of a full app reset.
There is no undo and no server-side copy. Export a backup first if there is any chance you will want the history back.
Privacy specifics worth knowing
- Sleep analysis reads aggregates, not audio. The Android service reduces each window to numeric features on the device, and the stage engine only ever sees those. No recording is retained.
- A wake-up mission stores a salted hash of your chosen barcode and its format, never the code itself.
- AI provider tokens stay in secure storage, and are kept out of the database, logs, prompts and backups.
- The AI snapshot is counts, not content. Specific facts have to be fetched by an explicit tool call. See AI Coach.
- Food lookups are anonymous. No account, no key, no identifier attached to a query.
Permissions
Each permission maps to exactly one capability, and refusing one never disables anything else. The full list, with what happens if you decline, is in Getting started.
Four Android foreground services are declared: one for the sleep monitor (microphone), one for run tracking (location and media playback), and two for run voice and alarms (media playback). Each one only runs while the feature it belongs to is active, and each shows its own notification while it does.
Platform support
| Capability | Android | Other targets |
|---|---|---|
| Workouts, exercises, routines, timers, history | Full | Full |
| Progress, goals, body measurements | Full | Full |
| Nutrition diary, food library, targets | Full | Full |
| Periodization, plans, check-ins | Full | Full |
| AI Coach | Full | Full |
| Stationary bike sessions | Full | Full |
| Background GPS run tracking | Full | Not available |
| Sleep monitoring and stage estimates | Full | Manual entries only |
| Alarms and wake-up missions | Full | Not available |
| Barcode scanning | Full | Search or create foods manually |
| Native voice coaching | Full | In-app speech fallback |
Every native bridge checks the platform before calling and reports itself as unsupported elsewhere, including when the platform channel is missing entirely. That is why the app runs in tests and on desktop without the Android services present.
The app is MIT licensed and the whole repository is public. If any claim on this page matters to you, the code that implements it is one clone away: github.com/RafaelGoulartB/workout-notes.