There are a few things about snapshots which I would like to revisit before we commit to it.
Terminology and concepts
When we suspend, we generate a snapshot ID, which encodes the timestamp at which the snapshot is created plus a random suffix. They look like this: 2026-08-05T16:47:28-04:00-N7GBFQ2XKJ4ZLWMR6TYHPVCD3A.
When the snapshot is uploaded to GCS, we construct a snapshot URL, which consist of the base path on the ActorTemplate and the snapshot ID. It looks like this:
gs://my-bucket/snapshots/secret-agent/snapshots/2026-08-05T16:47:28-04:00-N7GBFQ2XKJ4ZLWMR6TYHPVCD3A
Note that the ID goes into that URI raw — those colons end up verbatim in the GCS object names. The sanitizing happens somewhere else entirely: after the upload, when we finalize the suspend, we parse the ID back out of the URI and rewrite it (:→-, +→-, plus lowercasing) to get a valid Substrate resource name, because ObjectRef.name is validated as a DNS-1123 label.
So one snapshot ends up with two IDs:
|
|
| in GCS |
2026-08-05T16:47:28-04:00-N7GBFQ2XKJ4ZLWMR6TYHPVCD3A |
in the API (ActorSnapshot.metadata.name) |
2026-08-05t16-47-28-04-00-n7gbfq2xkj4zlwmr6typvcd3a |
Note that the rewrite is one-way: : and + both become -, so you can't get from the API name back to the path (t16-47-28-04-00 — was that -04:00 or +04:00?).
I think we need to simplify the concepts and use consistent terminology throughout the codebase.
-
There is no snapshot ID. When we create a new snapshot, we allocate a new snapshot name, which is used for both the final snapshot URI and the ActorSnapshot name.
-
We don't encode semantic information into the name. Today we try to encode a timestamp, but it's not really needed. The ActorSnapshot resource has a create_time timestamp already.
-
We have three concepts:
- Snapshot location: This is the base path which is dictated by
ActorTemplate.spec.snapshotsConfig.Location.
- Snapshot name: The identifier of the snapshot. A single UUID. Opaque.
- Snapshot URI: The address of a given snapshot. This is
<location>/snapshots/<atespace>/<snapshot-name>.
This removes the confusion about snapshot name, snapshot id (sanitized vs not sanitized), etc.
ActorSnapshot has an internal version in storage
The ActorSnapshot representation in ValKey today is a json wrapper of the proto, which includes the (not sanitized!) snapshot ID.
We should just add a snapshot_uri to the ActorSnapshot proto.
Bookkeeping during operations
Today the suspend and pause workflows are reusing the in_progress_snapshot field in the Actor proto for storing two different concepts. We should split them.
There are a few things about snapshots which I would like to revisit before we commit to it.
Terminology and concepts
When we suspend, we generate a snapshot ID, which encodes the timestamp at which the snapshot is created plus a random suffix. They look like this:
2026-08-05T16:47:28-04:00-N7GBFQ2XKJ4ZLWMR6TYHPVCD3A.When the snapshot is uploaded to GCS, we construct a snapshot URL, which consist of the base path on the ActorTemplate and the snapshot ID. It looks like this:
Note that the ID goes into that URI raw — those colons end up verbatim in the GCS object names. The sanitizing happens somewhere else entirely: after the upload, when we finalize the suspend, we parse the ID back out of the URI and rewrite it (
:→-,+→-, plus lowercasing) to get a valid Substrate resource name, becauseObjectRef.nameis validated as a DNS-1123 label.So one snapshot ends up with two IDs:
2026-08-05T16:47:28-04:00-N7GBFQ2XKJ4ZLWMR6TYHPVCD3AActorSnapshot.metadata.name)2026-08-05t16-47-28-04-00-n7gbfq2xkj4zlwmr6typvcd3aNote that the rewrite is one-way:
:and+both become-, so you can't get from the API name back to the path (t16-47-28-04-00— was that-04:00or+04:00?).I think we need to simplify the concepts and use consistent terminology throughout the codebase.
There is no snapshot ID. When we create a new snapshot, we allocate a new snapshot name, which is used for both the final snapshot URI and the ActorSnapshot name.
We don't encode semantic information into the name. Today we try to encode a timestamp, but it's not really needed. The
ActorSnapshotresource has acreate_timetimestamp already.We have three concepts:
ActorTemplate.spec.snapshotsConfig.Location.<location>/snapshots/<atespace>/<snapshot-name>.This removes the confusion about snapshot name, snapshot id (sanitized vs not sanitized), etc.
ActorSnapshot has an internal version in storage
The ActorSnapshot representation in ValKey today is a json wrapper of the proto, which includes the (not sanitized!) snapshot ID.
We should just add a
snapshot_urito theActorSnapshotproto.Bookkeeping during operations
Today the suspend and pause workflows are reusing the
in_progress_snapshotfield in theActorproto for storing two different concepts. We should split them.