Skip to main content

Principles

Data belongs to the user. Export must be complete, portable, and not require the app to read it back. See the Product Contract for the user data ownership commitment.
  • Export produces a self-contained file - no external dependencies to restore it
  • Import is additive by default - it does not silently overwrite existing data
  • No proprietary encoding or binary formats - everything is readable in a text editor
  • Schema version is embedded in every export so future versions can handle old files

Formats

JSON (full fidelity)

Includes the complete event log, all tasks, and all check-ins. Use this for:
  • Transferring to a new device
  • Backup before a wipe
  • Debugging and support

CSV (human-readable summary)

Two sheets: tasks and check-ins. Does not include the event log. Use this for:
  • Sharing with a doctor or care coordinator
  • Personal analysis in a spreadsheet
  • Printing a history
CSV is export-only. It cannot be imported back into the app.

Export schema (JSON)

{
  "version": 1,
  "exportedAt": "2025-09-01T09:00:00Z",
  "events": [
    {
      "id": "uuid",
      "type": "check_in_recorded",
      "occurredAt": "2025-08-01T08:30:00Z",
      "payload": { "body": 3, "mind": 3, "mood": 3, "dayType": "normal" }
    }
  ],
  "tasks": [
    {
      "id": "uuid",
      "title": "Shower",
      "netMet": 2.5,
      "durationMinutes": 15,
      "bodyWeight": 0.8,
      "createdAt": "2025-07-01T10:00:00Z"
    }
  ],
  "checkIns": [
    {
      "id": "uuid",
      "body": 3,
      "mind": 3,
      "mood": 3,
      "recordedAt": "2025-08-01T08:30:00Z",
      "dayType": "normal"
    }
  ]
}
All timestamps are ISO 8601 UTC strings. IDs are UUIDs. Derived mana state (pool, EWMA, confidence) is not exported - it is always recomputed from the event log on import.

Import rules

ID handling

Original IDs are preserved on import. If an event or task with the same ID already exists in the database:
  • Events: skip the duplicate (events are immutable; same ID = same event)
  • Tasks: prompt the user to choose: keep existing, replace with imported, or keep both (new ID assigned)

Conflict resolution

For event log merges (importing into a non-empty database): events are ordered by occurredAt. No deduplication beyond ID matching. The combined log is replayed in chronological order to recompute mana state.

Validation

Before importing, validate:
  1. Schema version - version field must be present and an integer the app knows how to handle
  2. Required fields - events, tasks, and checkIns arrays must be present (can be empty)
  3. Date format - all timestamp strings must parse as valid ISO 8601
  4. Event types - unknown type values are rejected with a clear message (not silently dropped)
If validation fails, the import is aborted entirely. No partial imports.

Handling unknown event types

If an event’s type is not in the known set, surface an error and stop:
“This export contains event types this version of the app doesn’t recognise. Update the app and try again.”
Do not silently drop unknown events - they may be load-bearing for EWMA history.

User-safe error messages

All import errors use neutral, non-judgemental language. No blame, no urgency.
“This file doesn’t look like a Canthus export. Make sure you’re selecting the right file.”
“This export is from a newer version of the app. Update Canthus and try importing again.”
“This file couldn’t be read - it may be incomplete or corrupted. Try exporting again from your other device.”
“This export contains data this version of the app doesn’t recognise. Update Canthus and try importing again.”
No error message exposes internal field names, schema version numbers, or stack traces to the user.