Skip to content

Data model

Data model

Logical stores

erDiagram
    SECTIONS ||--o{ FIELDS : contains
    ENTRIES ||--o{ ENTRY_VALUES : owns
    FIELDS ||--o{ ENTRY_VALUES : defines
    CATALOG_ITEMS ||--o{ EVENTS : classifies
    EVENTS ||--o{ RESETS : may_trigger
    PROFILES ||--o{ COUNTERS : owns
    PROFILES }o--o{ EVENTS : concerns
Store Content Key idea
meta Runtime configuration, language, UI state, privacy key/value metadata
sections Visible sections of the daily check-in order, label, Life Wheel axis
fields Versioned field definitions stable IDs and logicalId
entries One daily record per date sparse values map
catalogItems People, sports, meals, triggers reusable catalog objects
events Repeatable events social, exercise, self-care, meal, urge, trigger
resets Counter start points and relapses history instead of overwrite
todos Tasks state, priority, deadline
inventoryEntries Recovery program-context reflections retained for schema compatibility; hidden in General Audience
chats, chatMessages Companion conversations separate from tracking data

Daily entry

{
  "id": "2026-07-21",
  "date": "2026-07-21",
  "values": {
    "sleep_quality": 4,
    "stress": 4,
    "day_management": 3
  },
  "createdAt": "...",
  "updatedAt": "...",
  "source": "app"
}

The values map is sparse. A missing key means unknown. This is semantically different from 0, false or "no".

Dynamic events

Repeatable things are not forced into the daily entry. Three social contacts on one day are three events, but they can reference the same person from catalogItems.

{
  "id": "social-...",
  "kind": "social",
  "date": "2026-07-21",
  "catalogItemId": "person-anna",
  "connection": 4,
  "note": "short conversation"
}

Runtime configuration

loadAppData() combines built-in default configuration with locally stored changes. This gives the UI a consistent object containing data, configuration and preferences.

For a fresh general-audience database, only the current sections and fields are created. ensureConfiguration() adds missing current definitions in this variant but does not inject absent legacy fields. Historical field definitions that are actually present are still normalized and retained together with their daily values. The same rule applies after a validated backup import.

Sources