ServicesLocations

Change the Root

Move a Location to a new URI or Connection with a new version, and keep its inventory.

Available

When recordings move to a new prefix, point the Location at it without changing its name. Readers keep using recordings, and pinned commits keep the old root and the old file list. The change creates a new Location version and keeps the inventory until the next refresh.

A revision can point a Location at a new root URI, name another Connection, or both. Every version names the Connection that reads its root.

Before you start

  • The Location ID and the version ID at the branch head. Read the Resources at the head commit and find the Resource with the kind location. The Resource ID is the Location ID. See Select an exact state.
  • The branch ID and its head commit ID.
  • location:update on the Location, and connection:bind on the Connection.
  • The ID of the Connection whose URI contains the new root.
  • A new UUIDv7 for the version ID, and another UUIDv7 for the transaction ID.

Create the new version

Name the base version, the new root and its Connection. The version ID is yours, so a retry of the same request is safe:

request = LocationsCreateVersionRequest(
    base_version_id=base_version_id,
    version_id=new_version_id,
    change=LocationsVersionChange(
        revise=LocationsLocationBacking(uri="s3://example-robotics/recordings-2026/", connection_id=connection_id)
    ),
)
created = client.locations.create_version(location_id, request)
print(f"created version {created.version.version_id}")

The root must lie inside the URI of the Connection that change.revise names.

The new version is not visible until a commit selects it.

Publish the new version

A commit makes the new version current. The publication names the version that you started from and the new one, and Spiral swaps them only if the Location still has the base version:

request = CatalogPublishRequest(
    branch_id=branch_id,
    base_commit_id=head_commit_id,
    transaction_id=transaction_id,
    idempotency_key="revise-recordings-2026",
    message="Move recordings to recordings-2026",
    resources=[
        CatalogResourceChange(
            advance=CatalogAdvanceResourceChange(
                resource_id=location_id, expected=base_version_id, replacement=new_version_id
            )
        )
    ],
    namespace=[],
)
outcome = client.catalog.publish("demo", request)
if outcome.published is not None:
    print(f"published commit {outcome.published.commit_id}")
elif outcome.conflict is not None:
    print(f"conflict: the head is now {outcome.conflict.current_commit_id}")

The response is {"type": "published", ...} with the new commit, or {"type": "conflict", ...}. A conflict means that another change, such as a refresh, advanced the Location first. Nothing is published. Read the new head and create the version again from the new base.

The Rust SDK publishes Table changes only. Publication of other Resource kinds from Rust is planned.

The inventory

The revision keeps the previous inventory until a refresh reconciles it with the new root. A refresh updates the URI of a path that both roots contain, even when its size and ETag match, because the same ETag under a different root can be a different object.

Verify the result

Read the new version:

curl "https://api.spiraldb.dev/api/locations/$LOCATION_ID/versions/$NEW_VERSION_ID" \
  -H "Authorization: Bearer $SPIRAL_TOKEN"

version.definition.backing shows the new root, and version.parentVersionId is the base version.

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

On this page