Skip to content

Data model

Content items

Every authored thing is a ContentItem, a discriminated union on kind with nine variants. The content schema reference is generated from the schema package and lists every field.

The current SCHEMA_VERSION is 2.

Lifecycle

flowchart LR
  d["Draft<br/>editable"] -->|"publish(changeNote)"| v["Version n<br/>immutable"]
  v --> h["Head<br/>points at n"]
  d -->|"archive"| x["Archived"]
Node Meaning
Draft The single working copy per item. Saved with an etag; a stale etag raises ConflictError
Version n A published snapshot. The next publish writes n+1; earlier versions stay for history
Head The pointer the manifest and the player read
Archived A flag on the head. Hidden from the manifest, not deleted

IndexedDB

Database guidepost, version 1. An install made before the product rename is copied in on first start and the original is left in place (see upgrading). Stores:

Store Key Holds
apps appKey Application settings, origins, theme
segments id Audience segments (in the schema, not evaluated)
drafts id, index by-app Working copies
versions key, index by-id Published snapshots
heads id, index by-app Latest version and archived flag
progress contentId Per-item progress for the local user
assets sha256 Uploaded files, content addressed

Settings live in storage.local. The frame registry and picker sessions live in storage.session.

Migrations

migrateItem upgrades older items when they are read. Index n in the migration table upgrades schema n to n+1. The only step today is 1 to 2, which adds tags and locale (default en-CA). The comment in the code states the rule: add a step, never edit one.

Bundle format

.guidepost.zip, read and written by BundleCodec:

Entry Content
bundle.json The Bundle: schemaVersion, exportedAt, sourceMode, apps, segments, items
assets/<sha256> Raw bytes of each referenced asset
signature.json Optional. Read and returned; nothing verifies it today

Items in an older bundle are migrated on read, not rejected. A zip without bundle.json is rejected. Import takes a conflict mode (skip, overwrite or new-version) and returns a report of what was imported, skipped, overwritten, versioned, plus missing references and the asset count.

Gotchas

  • The progress store is keyed by content id only. There is one record per item per browser profile; version handling is in the engine's eligibility check, not the key.
  • Assets are content addressed, so the same file is stored once.
  • A change to the database layout needs an IndexedDB version bump. None has been made yet.