Trust Updated 2026-08-01 View as Markdown

Data model

What is stored, in what shape, and why. Useful if you are reasoning about the platform from the outside — or if you are an agent trying to understand what a tool result actually represents.

The shape of a day

The atomic unit is one user, one date:

{
  userId: "…",
  dateKey: "2026-08-01",        // YYYY-MM-DD, the natural key
  date: ISODate("…"),
  data: {
    wakeAt7am: true,
    takeVitamins: true,
    weight: 178.4,
    pushUps: 60,
    breakfast: "eggs, oats",
    whoopRecovery: 71,
    whoopHrv: 88.2,
    // …any custom column, keyed by its id
  },
  lastUpdated: "…",

  // Retrieval
  embedding: [ /* vector */ ],
  embeddingText: "…",           // the human-readable text that was embedded
  embeddedAt: ISODate("…"),
  needsEmbedding: false,
  embeddingStatus: "embedded"
}

Three things are worth drawing out.

Keys inside data are canonical metric ids. whoopHrv, not "HRV". They come from the metric registry, which is the same source of truth the API, the correlation engine and the AI tools read. WHOOP fields sit in the same object as fields you typed — there is no separate wearable record to join.

Custom columns live in the same object. A custom column is not a second-class citizen stored elsewhere; it is a key in data with an entry in your column settings describing it.

The embedding lives on the document. Each day carries its own vector and the exact text that produced it, so retrieval and the audit of what was retrieved are the same object.

Collections

Collection Holds
routineData Tracked days, one document per user per date, with embeddings
routineSummaries Narrative rollups: weekly, monthly, quarterly, yearly, lab and methylation interpretations, mission analysis, correlation narratives
users Accounts, hashed passwords, and the profile — sensitive fields encrypted
userLearnings Insights extracted from conversation, embedded and scored
columnSettings Column definitions, order, visibility, custom column metadata
columnWidths Per-column display widths
dailyFeatures The feature store — normalized values and z-scores per day
apiTokens Hashed machine credentials with scopes and revocation state
labResults Bloodwork panels and extracted markers
methylationResults The one-time genetic profile

Derived artifacts

Several things are computed rather than entered, and stored so they can be read cheaply.

Feature store. Every metric normalized to a number plus its z-score against a trailing 30-day baseline. It is what the correlation engine reads and what get_daily_features returns. Stamped with the registry version, so a change to a metric's definition can trigger a targeted rebuild rather than a silent stale read.

Correlation edges. Relationships as graph edges with coefficient, partial coefficient, lag, p-value, sample size, tier and provenance. See analytics.

User facts. All-time aggregates per metric — extremes, totals, streaks, rolling averages — derived from the registry rather than a hand-maintained field list.

Insights. The precomputed statistical models: readiness, goal ETA, changepoints, dose-response, and the rest.

Summaries. Narrative rollups, embedded alongside days so retrieval can find a period as a unit.

What is encrypted

Sensitive profile fields in users are encrypted with AES-256-GCM. Ledger data is not. The reasoning is in security and privacy.

Retrieval indexes

Alongside conventional indexes on userId and dateKey, the database maintains a vector index over day and summary embeddings and full-text indexes over the same content. A question runs both, the results are fused, and the survivors are reranked. Neither alone is sufficient: vector search misses exact terms, text search misses paraphrase.

Consistency

The database is the single source of truth. The browser keeps essentially no cache of your data, every edit is written immediately, and every read comes from the database.

Server-side caching exists for expensive computations, with a bounded TTL, and is invalidated for your account whenever you write. So a figure the coach quotes is either fresh or was computed against the full history by a background worker — never a stale value from a browser that has been open since yesterday.

Reading it from outside

The MCP server exposes most of this read-only. get_day_details returns a single day, get_daily_features the feature matrix, get_correlation_edges the graph, get_user_facts the aggregates, get_insights the models, and get_summaries the narratives. Full schemas in the tool reference.

Not exposed: raw account records, password or token hashes, and progress pictures.