Skip to content

Migrations

There are no hand-written migration files. The schema is code, versioned with @vN. Deploy a new schema head; each tenant applies it lazily on first touch.

Fingerprint apply-on-touch (Slice A)

Every tenant records the fingerprint of the last schema IR it applied (SchemaLog). On a data-touch path the engine compares that fingerprint to the deployed registry:

  1. Match → no-op (catalog-only check; no data-page reads).
  2. Differ → metadata-only apply: ensure @index B-trees exist, write the new fingerprint, persist. Studio's pending_migration clears after that first touch.

Idle tenants never pay. Adding a field with a default, growing an enum, or adding @index does not rewrite pages — storage is dynamic; values are interpreted through the schema at read time.

Metadata vs @breaking

Most diffs are metadata-only. Destructive or narrowing changes require an explicit annotation plus a transform — the compiler refuses to infer data loss:

eel
schema Orders @v4 @breaking(from: @v3) {
  id: Id
  total: Money<usd>
} transform { drop region }
  • @breaking without transform { … } is a parse error.
  • Today transform supports drop <field> (old values stay in page history; reads ignore the dropped field because it is absent from the schema shape).
  • Slice A still treats the drop as metadata: fingerprint bumps, indexes are ensured, pages are not rewritten for the drop.

What compacting does / doesn't today

OperationToday
Lazy apply-on-touch (fingerprint + indexes)Yes — Slice A
eelden compact / Studio compactYes — rewrites live rows for a tenant (space reclaim)
History-compacting rewrite that purges dropped fields from page historyNot yet — see Status

Until history compaction for @breaking lands, dropped field bytes can remain in versioned page history even after the live schema no longer exposes them.

Drift vs migration

MigrationDrift
What movesThe declared schema (fingerprint)Stored values disagreeing with the declared type
How you see itpending_migration until first touchValues typed unknown at stage boundaries
Rewrite?Slice A never rewrites values to force conformanceNarrow with is / asTypes

The two are deliberately independent: migrating the schema does not coerce stored data into the new shape.

Pre-alpha. Local-first. Stdlib-only Rust engine. Tenant concerns shifted left into the database.