docs/wiki/3.06-User-Data.md
The User Data concept applies only to the desktop (Electron) application. The web app stores data in browser storage (IndexedDB and localStorage) instead. See [[3.05-Web-App-vs-Desktop]] for web vs desktop differences. For an overview of how import, export, backups, and sync work from a user perspective, see [[4.23-Managing-Your-Data]].
A full backup or export is a restorable snapshot of the application model: tasks (active and archived), projects, tags, time-tracking state, global configuration, notes, reminders, metrics, simple counters, planner and board state, task repeat configurations, issue-provider settings, plugin user data, and both archive tiers (recent and old). It is not a byte-for-byte copy of every local database.
Device-local support data is deliberately outside that snapshot. Examples include the sync conflict journal, operation-log transport metadata, the internal import-recovery slot, and sync-provider private configuration stored in the separate sup-sync credential database (credentials and encryption key material). Conversely, issue-provider settings inside the application model can contain API credentials or tokens.
Normal backup and export files are plaintext JSON, even when sync encryption is enabled. Treat them as sensitive: protect them like the work data and credentials they may contain. The anonymized/privacy export masks known field names, but it is not a credential-safety boundary: inspect it before sharing and remove any remaining provider or plugin credentials, tokens, and private content.
The User Data Folder is the application data directory where the desktop app stores:
styles.css)| Platform | Location |
|---|---|
| macOS | ~/Library/Application Support/superProductivity/ |
| Windows | C:\Users\<YOUR_USER_NAME>\AppData\Roaming\superProductivity/ or %APPDATA%\superProductivity |
| Linux | ~/.config/superProductivity/ |
The backup path is shown in the UI under Settings → Automatic Backups (including the backups/ subfolder) and is printed when the app is started from the command line.
You can specify a custom user data directory using the --user-data-dir command-line parameter:
superproductivity --user-data-dir=/path/to/my/data
Trailing slashes are stripped. The custom path is used instead of the default for the session.
For Snap packages on Linux, the app uses the SNAP_USER_COMMON directory so that data is not accessed by the Snap update process. The effective path is $SNAP_USER_COMMON/.config/superProductivity (or the app name equivalent).
The Windows Store build may use a different path that includes the Windows Store package identifier (under Local\Packages\...\LocalCache\Roaming instead of Roaming). This depends on whether Windows virtualizes the package: virtualized packages have their AppData writes redirected into LocalCache, while full-trust ones write to the plain Roaming path. Settings shows whichever location actually exists on your machine, so check there rather than assuming either one.
{userData}/backups/.YYYY-MM-DD_HHmmss.json.YYYY-MM-DD_HHmmss.json for desktop automatic backups.2025-01-15_143022.json).styles.css (exact name required).src/app/op-log/persistence/op-log-db-schema.ts and src/app/op-log/persistence/db-upgrade.ts; access is owned by OperationLogStoreService and its persistence adapters.src/app/op-log/sync-providers/credential-store.service.ts. This database is not part of application-model backups or exports./sync-conflicts. The store and review UI exist, but normal production remote-conflict processing currently has journal emission disabled, so the journal is dormant/incomplete and is not a complete record of resolutions. Existing entries are deliberately separate from SUP_OPS, never synced or uploaded (they can capture the discarded side of conflicts verbatim), pruned to 14 days / newest 200 entries — on app start, and mid-session once the store crosses a soft cap of 220 entries — and cleared whenever the full dataset is replaced (backup import/restore, profile switch).UI and app state stored with keys such as:
SUP_UI_HELPER, SUP_ACTION_LOG, SUP_LAST_ERROR_ACTION_LOGSUP_IS_PROJECT_LIST_EXPANDED, SUP_IS_TAG_LIST_EXPANDEDDARK_MODE, SUP_NAV_SIDEBAR_EXPANDED, SUP_NAV_SIDEBAR_WIDTH, SUP_RIGHT_PANEL_WIDTHSUP_IS_ADD_TO_BOTTOM, and others (see storage-keys.const.ts for the full set).Automatic backups: When enabled in settings, the app creates a backup on a schedule (default: every 5 minutes). The interval and backup subdirectory are not configurable in the UI. Storage location depends on platform:
| Platform | Where automatic backups are stored |
|---|---|
| Electron (desktop) | {userData}/backups/ as timestamped JSON files (YYYY-MM-DD_HHmmss.json) |
| Android | A native app-private SQLite store (SupKeyValStore, keys backup / backup_prev for the latest and previous slots), separate from the WebView IndexedDB so it survives IndexedDB eviction |
| iOS | Capacitor Filesystem API: latest and previous JSON files in Directory.Data |
| Web | No automatic file backups; use "Export data" to download a file manually |
On desktop, the Maximum backup files setting caps the number of timestamped automatic backup files kept in the backups/ folder. Older files are deleted when the cap is exceeded. At the default cap, cleanup keeps the 30 most recent backup files plus up to one newest backup per day for the current day and previous 20 days, when files for those days exist. The exact file count can be lower because the daily backups often overlap with the 30 most recent files.
On mobile (Android/iOS), if the app launches with no local data but a usable on-device backup exists — the typical "blank app after the system evicted WebView storage" case — the newest usable backup is restored automatically on startup, without a prompt, with a snackbar reporting how many tasks and projects were restored. This only happens when the live store is genuinely empty; a deliberate in-app "delete all data" leaves a valid (empty) state and is not auto-restored. Restore latest automatic backup also remains available manually in Settings -> Sync & Backup -> Automatic Backups, which shows a Last backup timestamp so you can confirm you are protected; the manual restore replaces the current local data after confirmation. If sync is configured, restored data (auto or manual) is treated as a backup import: on the next sync it propagates to and overwrites your other synced devices, so use it as a recovery action rather than a routine one.
Manual backups: (1) Create manual backup in Settings → Sync & Export creates a backup using the same mechanism as automatic backups (platform-dependent location). (2) Safety backups are created automatically before certain sync operations; the app keeps a limited number of slots (e.g. two most recent, one first from today, one first from day before). (3) Export data downloads a full application-model snapshot as JSON to a path you choose (or the browser download folder on web).
What is included: Backups and exports contain the restorable application model described above, including both archive tiers when archives are requested. They do not copy the retained operation log, causal/transport metadata, device-local conflict journal, internal import-recovery slot, or separately stored sync-provider credentials and encryption key material. A separate, single-slot import backup is stored locally in IndexedDB (import_backup). The app captures it before replacing local state through a file import or the sync conflict action Use Server Data. A successful Use Server Data replacement shows a persistent Undo action that restores this backup; interrupted replacements offer the same recovery action when sync resumes. The Undo remains available after an app reload while that same backup is still present. The slot is internal recovery state and is not included in exports or the user-visible automatic/manual backup lists.
The application-model snapshot can still contain private work content and issue-provider API credentials or tokens. Its JSON serialization is not encrypted by sync encryption; protect normal backup/export files. The anonymized/privacy export is best-effort, so inspect it and remove any remaining credentials, tokens, or private content before sharing it for diagnostics.
Export: Export produces a snapshot of the current state at the time of export. The file is plaintext JSON containing (or wrapping) the restorable application-model state. Relationships are preserved by ID references (e.g. tasks reference projectId and tagIds; subtasks reference parentId). Export is available via Settings → Sync & Export → Import/Export (e.g. "Export data" / download backup).
Import: The app accepts a full backup in the same format (complete application-model state, optionally wrapped with timestamp and version). Supported structures: (1) the raw complete state object, or (2) a wrapper object with data, timestamp, and crossModelVersion where data is the complete state. Legacy V1 format (e.g. config + tasks array) is detected but no longer supported; the user is shown an alert that migration is not supported. Before applying import, the app validates the data (structure and cross-entity relationships). If validation fails, automatic repair may run when possible (e.g. fixing orphaned tasks, invalid project/tag references, missing archive structures). If the snapshot records a different sync-encryption state from the current app, the app may show a confirmation dialog before importing. That warning is about how the imported state would configure subsequent sync; it does not mean the backup file itself is encrypted. After import, current state is replaced; archive data is written to IndexedDB. See [[2.02-Restore-Data-From-Backup]] for how to perform a restore.
Strategy: The app is local-first and records changes as operations, then adapts that log to different transports. SuperSync sends operations through an ordered server API. File providers use the default v2 monolithic format, which rebuilds and uploads the current snapshot, both archive tiers, and retained operations on an operation-bearing upload. The opt-in v3 split format stores those parts separately, so ordinary operation uploads do not rewrite snapshots between compactions. See the maintainer field guide's transport comparison.
Conflict resolution: Conflicts are detected using vector clocks (same entity changed concurrently in two places). Resolution first applies explicit archive/delete precedence and safe disjoint-field merging. Remaining overlaps use last-write-wins (LWW): the newer operation timestamp wins; an exact tie uses stable client-ID ordering. When the local or merged result must dominate both histories, the app emits the appropriate update, archive-win, or delete-win operation for the next upload. Identical conflicts are auto-resolved without user action. In first-sync situations where both local and remote already have data, the app may show a dialog to choose use local or use remote. After resolution, state is validated and repaired as needed.
User-editable:
System-managed (do not edit manually):
QuotaExceededError occurs, the app triggers emergency compaction (24-hour retention instead of 7 days) and uses a circuit breaker to avoid infinite retries.nodeExecution permission and explicit user consent in the Electron desktop app. Main-process grants are currently issued only for packaged built-in plugins whose manifests can be verified by the app; uploaded/community plugins declaring nodeExecution are rejected for now.REPAIR operations for the log.SUP_OPS) is missing or corrupted, the app attempts to load from the legacy pf database and run genesis migration.backups/ via Settings → Sync & Export → Import/Export → Import from file. See [[2.02-Restore-Data-From-Backup]].SUP_OPS; local UI settings also use localStorage (SUP_* and related keys). Sync-provider private configuration and key material live separately in the sup-sync credential database.src/app/op-log/persistence/op-log-db-schema.ts and src/app/op-log/persistence/db-upgrade.ts as the executable source of truth; do not rely on a copied version number or store inventory in documentation.src/environments/versions.ts), not stored in the User Data Folder.app.getPath('userData'); custom path set with app.setPath('userData', path) when --user-data-dir or Snap logic is used.getUserDataPath()).fs APIs (e.g. readFileSync, writeFileSync, readdirSync, statSync, unlinkSync) are used for backup and local file sync.