Data Model
Tables, entities, channels
A table is a set of channels (columns) observed per entity (device) over time:
entity— string device/row key, implicit on every tablets— Unix milliseconds, implicit on every table- every other column is a channel with a declared type and fill policy
The table key is (entity, ts): it gives ordering, fragment coalescing and scan locality in one stroke.
The frozen type system
Channels are f64 or string. Nothing else, ever. Booleans are 0/1, enums are strings (the engine dictionary-compresses them), binary blobs live in external object storage as reference strings. This is a load-bearing constraint: the core stays small because the answer to every new-type request is code, not engine.
Sparse by construction
Devices send fragments. Whatever channels a fragment carries get appended to those channels' streams; the others receive nothing. There is no "row waiting to be completed" — a row is something reads reconstruct, not something storage keeps. See Forward-Fill.
Mutability classes
| Kind | Declared with | Behavior |
|---|---|---|
| IoT data (default) | — | immutable, append-only; duplicates allowed; retention applies |
| Metadata | WITH (MUTABLE) |
last-write-wins per entity; DELETE supported |
Mutable tables never mutate in place: an update is a versioned append (same entity, newer ts), a delete is a tombstone. The merge worker resolves both — the newest version survives, tombstoned entities vanish. The API feels like a document store; the files behave like an LSM tree.