Summary
Substrate creates a durable checkpoint whenever it suspends an Actor. Make each successful checkpoint a first-class, immutable ActorSnapshot that callers can get, list, tag, and use to initialize another Actor.
The design changed during implementation. Superseded decisions remain crossed out so the history is visible:
A caller explicitly promotes a suspended Actor's private checkpoint. Every successful SuspendActor automatically creates an ActorSnapshot and exposes it through Actor.latest_snapshot.
Tags are globally unique aliases. Tags are owned and addressed by Atespace as atespace/name; the same name may exist in different Atespaces.
Visibility is mutable state on the snapshot. Reuse policy belongs to each tag: ATESPACE permits local reuse and PUBLISHED permits reuse from any Atespace without changing the tag's address.
Tags are embedded in ActorSnapshot. ActorSnapshotTag is a separate resource and O(1) index entry.
Deleting an Atespace cascades to its tags. Atespace deletion is blocked while any Actors or ActorSnapshotTags remain, matching existing non-empty Atespace behavior.
Expose explicit snapshot deletion and protect tagged snapshots from it. Explicit snapshot deletion and automatic retention are deferred until the GC policy is designed.
CreateActor(source_snapshot) accepts a canonical snapshot reference or a tag. Explicit reuse accepts only a tag reference, ensuring reuse always has an explicit policy.
Motivation
Control planes need to preserve an Actor at a known state and later create an independent Actor from it. Substrate must own the storage layout, compatibility checks, authorization boundary, and stable references rather than requiring callers to copy private storage fields.
Proposed API
Snapshots remain immutable resources:
message ActorSnapshot {
ResourceMetadata metadata = 1;
ObjectRef source_actor = 2;
string source_actor_uid = 3;
int64 source_actor_version = 4;
string actor_template_namespace = 5;
string actor_template_name = 6;
string actor_template_uid = 7;
SnapshotContentScope content_scope = 8;
}
Tags are Atespace-owned aliases and retention pins. ATESPACE is intentionally the zero/default scope:
enum ActorSnapshotTagScope {
ACTOR_SNAPSHOT_TAG_SCOPE_ATESPACE = 0;
ACTOR_SNAPSHOT_TAG_SCOPE_PUBLISHED = 1;
}
message ActorSnapshotTag {
ResourceMetadata metadata = 1;
ObjectRef snapshot = 2;
ActorSnapshotTagScope scope = 3;
}
message ActorSnapshotRef {
oneof reference {
ObjectRef snapshot = 1;
ObjectRef tag = 2;
}
}
TagActorSnapshot creates a tag in the source snapshot's Atespace. UpdateActorSnapshotTag publishes or unpublishes it without changing its atespace/name identity. DeleteActorSnapshotTag removes the tag. Tag lookup uses actor-snapshot-tag:<atespace>:<name> and remains O(1).
Publishing changes who may reuse a tag, not who owns it. There is no separate global tag namespace. A higher-level grouping or tiered Atespaces can be considered later if concrete use cases require it.
Suspend and reuse semantics
- Each successful suspension creates exactly one immutable snapshot for that suspension.
- The source Actor version is captured when suspension starts, so snapshot metadata describes the state actually checkpointed.
CreateActor(source_snapshot) only accepts a tag reference.
- An
ATESPACE tag may initialize Actors only in its owning Atespace.
- A
PUBLISHED tag may initialize Actors in any Atespace while retaining its original atespace/name address.
- Full and data snapshots currently require the exact source
ActorTemplate UID.
- Cloning a template with external volumes is rejected until CSI/provider snapshot support exists; ordinary suspend/resume behavior is unchanged.
Snapshot data is stored under:
<ActorTemplate snapshotsConfig.location>/snapshots/<opaque-id>
The physical path carries no Actor, Atespace, tag, or reuse-policy semantics. Its flat manifest.json records actor identity and snapshot metadata.
Lifecycle and retention
- Deleting a tag removes its retention pin.
- Deleting an Atespace is blocked while any Actors or ActorSnapshotTags remain.
- Atespace deletion does not delete snapshot metadata or bytes in this change.
- Snapshot metadata is retained so a future GC/retention policy can make deletion decisions.
- There is no explicit
DeleteActorSnapshot RPC in this version.
Non-goals
- Automatic snapshot retention or garbage collection.
- Tiered Atespaces or another higher-level grouping concept.
- In-place rollback of an existing Actor.
- Portable reuse across different ActorTemplate UIDs.
- Cloning external volumes; future support should use CSI/provider snapshot APIs.
- Cloning node-local pause snapshots.
Expected errors
| Condition |
gRPC status |
| Actor, snapshot, or tag does not exist |
NOT_FOUND |
| Target Actor name or requested tag already exists in its Atespace |
ALREADY_EXISTS |
| Atespace still contains Actors or ActorSnapshotTags |
FAILED_PRECONDITION |
Canonical snapshot reference is supplied to CreateActor |
FAILED_PRECONDITION |
| Tag is created outside the source snapshot's Atespace |
FAILED_PRECONDITION |
| Tag scope does not permit the target Atespace |
FAILED_PRECONDITION |
| Snapshot uses a different ActorTemplate UID |
FAILED_PRECONDITION |
| Target template has external volumes |
FAILED_PRECONDITION |
| Snapshot data is missing or corrupt |
DATA_LOSS |
Acceptance criteria
References
Summary
Substrate creates a durable checkpoint whenever it suspends an Actor. Make each successful checkpoint a first-class, immutable
ActorSnapshotthat callers can get, list, tag, and use to initialize another Actor.The design changed during implementation. Superseded decisions remain crossed out so the history is visible:
A caller explicitly promotes a suspended Actor's private checkpoint.Every successfulSuspendActorautomatically creates anActorSnapshotand exposes it throughActor.latest_snapshot.Tags are globally unique aliases.Tags are owned and addressed by Atespace asatespace/name; the same name may exist in different Atespaces.Visibility is mutable state on the snapshot.Reuse policy belongs to each tag:ATESPACEpermits local reuse andPUBLISHEDpermits reuse from any Atespace without changing the tag's address.Tags are embedded inActorSnapshot.ActorSnapshotTagis a separate resource and O(1) index entry.Deleting an Atespace cascades to its tags.Atespace deletion is blocked while any Actors or ActorSnapshotTags remain, matching existing non-empty Atespace behavior.Expose explicit snapshot deletion and protect tagged snapshots from it.Explicit snapshot deletion and automatic retention are deferred until the GC policy is designed.Explicit reuse accepts only a tag reference, ensuring reuse always has an explicit policy.CreateActor(source_snapshot)accepts a canonical snapshot reference or a tag.Motivation
Control planes need to preserve an Actor at a known state and later create an independent Actor from it. Substrate must own the storage layout, compatibility checks, authorization boundary, and stable references rather than requiring callers to copy private storage fields.
Proposed API
Snapshots remain immutable resources:
Tags are Atespace-owned aliases and retention pins.
ATESPACEis intentionally the zero/default scope:TagActorSnapshotcreates a tag in the source snapshot's Atespace.UpdateActorSnapshotTagpublishes or unpublishes it without changing itsatespace/nameidentity.DeleteActorSnapshotTagremoves the tag. Tag lookup usesactor-snapshot-tag:<atespace>:<name>and remains O(1).Publishing changes who may reuse a tag, not who owns it. There is no separate global tag namespace. A higher-level grouping or tiered Atespaces can be considered later if concrete use cases require it.
Suspend and reuse semantics
CreateActor(source_snapshot)only accepts a tag reference.ATESPACEtag may initialize Actors only in its owning Atespace.PUBLISHEDtag may initialize Actors in any Atespace while retaining its originalatespace/nameaddress.ActorTemplateUID.Snapshot data is stored under:
The physical path carries no Actor, Atespace, tag, or reuse-policy semantics. Its flat
manifest.jsonrecords actor identity and snapshot metadata.Lifecycle and retention
DeleteActorSnapshotRPC in this version.Non-goals
Expected errors
NOT_FOUNDALREADY_EXISTSFAILED_PRECONDITIONCreateActorFAILED_PRECONDITIONFAILED_PRECONDITIONFAILED_PRECONDITIONFAILED_PRECONDITIONFAILED_PRECONDITIONDATA_LOSSAcceptance criteria
ActorSnapshotand updatesActor.latest_snapshot.atespace/namewith O(1) lookup.CreateActoraccepts only tag references and enforces tag scope and ActorTemplate UID compatibility.References
ateapi.proto