Services

Locations

Give a folder in an existing bucket a name in your Project, and keep a versioned list of its files.

Available

Most robotics data already sits in a bucket: thousands of MCAP logs and videos under a prefix such as s3://example-robotics/recordings/. A Location gives that folder a name in your Project, recordings, and keeps a table of its files. Your team finds and reads the files through that name, Spiral never copies the bytes, and a pinned commit always shows the same file list.

HTTP API

The HTTP API is available under /api/locations. Requests use camelCase fields and tagged unions with matching discriminator and payload names. Counts and timestamps that use 64-bit integers are returned as decimal strings. Existing Rust and CLI clients continue to use the established endpoints.

An example

After a refresh, the Location recordings holds a file inventory like this:

pathsizelast_modifiedetag
2026-10-12/run-0041.mcap2.1 GB2026-10-12 18:02:119b2cf5…
2026-10-12/run-0041-front.mp4640 MB2026-10-12 18:02:4041d0a8…

Each row also has the file's uri, which a reader uses to fetch the bytes. In Studio, the Preview tab of the Location shows the inventory and previews images, video, and text in place.

Locations are separate from Project storage. Project storage holds the data that Spiral manages, such as Tables. A Location names data that already exists somewhere else, and only reads it.

How readers reach the bucket

A Location records a storage root URI, such as s3://example-robotics/recordings/, and the Connection that authorizes reads of it. The Connection holds a key, or an identity that your IAM role trusts, and the root lies inside the Connection URI. A reader with access to the Location gets a short-lived, read-only credential for that root and reads the bucket directly. Connect Amazon S3, Cloudflare R2, Ceph, or Hugging Face.

Spiral reads no storage without credentials, so a public bucket or repository also needs a Connection.

Existing Location versions without a Connection remain stored, along with their history. Reading or using one returns an error identifying the version and its missing Connection. Create a Location with a Connection to read that root. A connected version remains usable even if its history includes a version without a Connection.

Every Location needs Project storage for its inventory. The inventory stores object metadata only, never copies of the bytes.

The file inventory

A new Location has an empty inventory. A refresh lists the storage root and reconciles the inventory: it adds new paths, removes missing ones, and updates a path whose URI, size, modification time, or ETag changed. It then publishes the inventory and a new Location version in one commit, and records how many rows changed. Zero means that the bucket matched the inventory.

Because a commit selects both the Location version and its inventory, a pinned commit keeps its file list after later refreshes, and after a change of root. That makes a training run's input list reproducible.

The inventory records what the provider returned at the time of the refresh. A later read can return newer bytes, or fail if the object was deleted. The ETag is the provider's metadata; it is not checked when you read.

Each refresh adds small files to the inventory, and Table maintenance compacts them like those of any Table.

Where next

Availability

Reading a gs:// root through a Google Cloud Storage Connection is planned.

Reference

The Connection

A Location version stores its root URI and its Connection's Resource ID. Reading costs one credential exchange per hour, not one per object. A Location stores its Connection's Resource ID, not its name, so renaming the Connection breaks nothing. If the Connection is dropped, reads of the Location fail with an error that says who dropped it and when.

Object URIs

A Location returns the provider URI of each file:

s3://example-robotics/recordings/2026-10-12/run-0041.mcap

Anyone whose client can reach the bucket can read this URI. For a private bucket behind a Connection, that means a reader to whom Spiral gave a credential. A reader who keeps the URI can still read the object after the Location is dropped, if their own access allows it.

A stored URI does not change when the Location root changes.

Permissions

OperationPermission
Create a Locationlocation:create
Name a Connection from a Locationconnection:bind on the Connection
Revise the root URI or the Connectionlocation:update
Refresh the inventorylocation:read, location:list, and location:update
Drop a Locationlocation:read and location:drop
Read the inventorylocation:read; also location:list for a Connection Location

The inventory is a Table, so it also needs Table permissions: table:create and table:write to create, table:read to read, and table:write to refresh, maintain, or drop. The standard viewer, editor, and admin roles can read a Location.

For a URI Location, these permissions control who can find the Location and learn its root, not who can read the bucket. The bucket's own access rules decide that.

The Locations HTTP reference documents the resource operations.

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

On this page