Storage Schema
getbased stores normal app data in the browser.localStorage holds app data and preferences, and IndexedDB holds auto-backup snapshots plus larger per-device datasets. Opt-in features may use hosted encrypted storage outside this browser; profile sharing is documented separately below because the hosted record contains only ciphertext and share metadata.
localStorage key reference
Keys are namespaced by profile ID where data is per-profile.{profileId} defaults to "default" for the first profile.
Global keys (not profile-specific)
Per-profile keys
importedData structure
Stored as JSON atlabcharts-{profileId}-imported. Full database and folder backups preserve this blob, while curated single-profile exports select user-facing fields from it.
Marker identity and placement compatibility
markerPlacements is an additive display map keyed by immutable marker ID. It does not replace the dot keys in entries, refOverrides, markerNotes, markerValueNotes, manualValues, import snapshots, or provenance maps. Missing or invalid assignments fall back to the marker’s native category, while unknown assignments remain stored so a newer runtime can resolve them later.
Custom marker markerId is also additive. New definitions receive an opaque ID independent of category and name; legacy profiles receive deterministic IDs during normal profile migration. Profiles without either field remain valid and require no value migration or re-import.
Curated single-profile exports, full database bundles, folder backups, automatic snapshots, encrypted shares, and cross-device sync preserve these fields. See Marker model and category placement for placement validation and the mutation boundary.
Lab import and range compatibility
importSnapshots[] is the historical per-file source of truth; entries[] is the current live projection. Active adopted lab ranges are chosen by collection date and then import time. Collection-context ownership is tracked per field so deleting or re-reviewing one same-date report can restore another report’s value instead of clearing unrelated context.
Marker moves must migrate both plain dotKeys and date-scoped keys in manualValues, markerValueNotes, markerLabels, and refOverrides. See Lab markers and range internals for precedence, revert, and migration contracts.
genetics — curated Genome data (null if not imported)
source metadata. When a raw file and an explicit manual/report override contain the same rsID, the explicit override remains authoritative. catalogVersion is preserved when a manual call is added to an older raw import so the app can still prompt for a genuine catalog refresh.
IndexedDB — auto-backup
Database:labcharts-backups
Object store: snapshots
Key path: id (auto-increment)
Each snapshot record:
chatDraft_* records are intentionally absent from full database, auto, folder, and single-profile backups. Sent conversations and custom personas are included.
Encryption
When encryption is enabled (labcharts-encryption-enabled = 'true'), sensitive localStorage keys are stored as AES-256-GCM ciphertext (base64-encoded) instead of plaintext JSON.
Encrypted key patterns (SENSITIVE_PATTERNS in crypto.js):
*-imported— all importedData*-chat— legacy chat history*-chat-threads— thread index*-chat-t_*— per-thread messages*-chatDraft_*— device-local unsent drafts*-chatPersonalityCustom— custom persona records*-chatPersonalityDeleted— custom persona tombstones
labcharts-{profileId}-enc-salt).
Hosted encrypted share records
Profile sharing is opt-in and uses hosted storage only after the user confirms upload in the Share Profile modal. The browser creates a normal v2 single-profile export, strips credential surfaces throughbuildClientExportObject(), compresses it when supported, and encrypts it locally before upload. The share password never leaves the browser and is not stored in the share link.
Production records are stored by api/share.js in private Vercel Blob objects under profile-shares/v1/{id}.json. Production also stores small per-client fixed-slot rate-limit marker records under profile-share-rate/v1/{sha256-client}/{windowStart}/{slot}.json to add friction to anonymous share creation without relying on a read-then-write counter. Local development stores the same logical share record in the dev-server.js in-memory map.