Services

Tables

Store your application's rows in versioned Tables that live in your Project's bucket.

Available

A Table is where your application keeps its data: one row per camera frame, robot episode, or log record, with columns for metadata, embeddings, and media. The Table's files live in your Project's bucket, and your application reads and writes them directly through the SDK. Every write publishes a new version, so a job that pinned last week's version keeps reading exactly those rows while new data lands.

What a Table holds

A Table has a name, a schema, and rows. The running example in these docs is frames, a Table of camera frames from a robot:

frame_idcameracaptured_atimageembedding
f-000184front2026-10-12 09:14:03.120JPEG bytes[0.12, -0.40, …]
f-000185left2026-10-12 09:14:03.120JPEG bytes[0.08, 0.31, …]

A column can hold a number, a string, a timestamp, binary data, a list, a struct, or a fixed-size vector, so a frame's metadata, its image, and its embedding sit in the same row. Each field inside a struct or list is stored as its own column; see Structs and lists.

Tables do not enforce primary keys or unique constraints. Keep your own identifier, such as frame_id, in an ordinary column. Duplicate values are allowed.

Every write is a new version

A Table version never changes after it is written. An append, an update, a schema change, or a new Table writes new files and then publishes a new version in a commit:

frames
  v1  create, 10,000 frames
  v2  append 25,000 frames
  v3  append 18,000 frames

A reader always sees one complete version, never a half-finished write. Readers that pinned v2 keep reading v2 after v3 lands, which is what makes a training run or an evaluation repeatable. To pin a whole Project at once, select a commit or a tag; see Select an exact state.

Concurrent writers do not lose data. Each commit names the version that it started from. If another writer published first, the commit reports a conflict and publishes nothing, and you repeat the write from the new head.

What you can do

You want toUse
Create a Table from Arrow batchesCreate a Table
Add, update, merge, or delete rowsWrite rows
Read rows as Arrow batches, or query with SQLRead rows
Add, rename, or drop columns without rewriting dataChange the schema
Give existing rows a new column of valuesAdd columns
Read only the rows that changed since an earlier versionRead changes
Keep scans fast after many small appendsMaintain a Table
Add a derived value, such as is_blurry, to every rowEnrichments
Find the rows with the nearest embeddingsVector Indexes

How Tables are stored

Tables use a storage format built for data that grows by appending and gains new columns over time. Files are never edited: an append adds new files, and an update or a delete adds a small change record. Every row has a hidden row ID that stays the same when maintenance reorganizes the files. How Tables are stored explains what this means for cost, pruning, and the files in your bucket.

Where it runs

Your application writes and reads Table files directly in Project storage, with short-lived storage access for one Table. Spiral's control plane checks permissions, allocates storage, and publishes versions; the rows never pass through it. See Architecture.

The Tables HTTP reference documents the storage and version operations.

HTTP API

The HTTP reference documents the contract at /api/tables. It uses camelCase fields, tagged union payloads, and decimal strings for 64-bit counts and timestamps. Existing Rust and CLI clients keep using the compatible resource endpoints.

Found a problem? Edit this page or report an issue.

On this page