substrate

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 22, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package substrate implements the agentsessions api.Runtime compute SPI on top of agent-substrate (the "actors on pre-warmed workers" runtime that memory-snapshots RAM+disk). It is a CLIENT of substrate's control plane, not a K8s/pod manager.

Neutrality by design: the backend depends only on the ControlClient interface DEFINED HERE — a small subset of substrate's ateapi.Control gRPC. A real deployment provides a thin adapter implementing ControlClient over the generated substrate proto client; agentsessions does not import the (still largely aspirational) substrate module, so the core stays vendor-neutral and substrate is just one Runtime backend beside pod/Kata/CLH.

Mapping: Create→CreateActor+ResumeActor; Snapshot(EXTERNAL)→SuspendActor; Restore→ResumeActor; Stop→SuspendActor+DeleteActor; Status→GetActor; Fork→replay-fork for a stateless harness, or CreateTag{source_actor}+CreateActor{source_tag}+ResumeActor to clone the parent's RAM for a REQUIRES_MEMORY_SNAPSHOT harness.

Transport: the harness is reached through substrate's atenet-router, the only supported ingress to an actor. Every incarnation addresses the router and carries the ate-target-actor metadata that selects the actor; the router resumes the actor if needed and proxies the h2c gRPC stream to it. Capabilities report MemorySnapshot=true by default; WithVolumesOnly disables memory admission for files-only templates. Snapshot reports the observed fidelity, not the configured preference.

Index

Constants

View Source
const DefaultRouterAddress = "atenet-router.ate-system.svc:80"

DefaultRouterAddress is the atenet-router Service a stock substrate install creates. The router accepts h2c on port 80 and carries gRPC unary and bidi streams, trailers included, to the actor's HarnessPort.

View Source
const HarnessPort = "80"

HarnessPort is the TCP port the in-sandbox harness (cmd/harnessnode) serves harnesswire on. The router's default ingress forwards to port 80 in the actor, so this is not configurable per incarnation. Keep in sync with cmd/harnessnode's HARNESS_ADDR default.

View Source
const TargetActorHeader = "ate-target-actor"

TargetActorHeader is the request header the atenet-router reads to pick the actor, with the value "<atespace>/<actor>". The router ignores the host and authority, so without it a call reaches no actor. It mirrors substrate internal/atenet.TargetActorHeader, which is not importable.

Variables

View Source
var ErrActorBusy = fmt.Errorf("substrate: actor is busy with another operation: %w", api.ErrComputeBusy)

ErrActorBusy is what a ControlClient must return (or wrap) when substrate refuses an operation because another one holds the actor (substrate reports ABORTED: another host is resuming or suspending it). The backend also returns it for an actor it finds mid-transition, and for one replaced or deleted while Create was attaching to it. It wraps api.ErrComputeBusy, which the session service reports as ABORTED, so the caller retries once the actor settles.

View Source
var ErrActorExists = errors.New("substrate: actor already exists")

ErrActorExists is what a ControlClient must return (or wrap) from CreateActor when an actor of that name already exists. Create treats it as another host having placed the session first, and attaches to that actor instead of failing the turn.

View Source
var ErrActorNotFound = errors.New("substrate: actor not found")

ErrActorNotFound is what a ControlClient must return (or wrap) from GetActor and SuspendActor when the actor does not exist, and should from ResumeActor. After a resume that reports it, Restore asks GetActor and reports a memory-tier session's state lost unless the actor still holds the snapshot being restored, so a replacement created between the two calls does not hide the loss. It is a sentinel rather than a transport code so the seam stays neutral: the backend decides "create it" vs "attach to it", and whether a memory-tier session's in-RAM state is gone, without knowing that the client underneath speaks gRPC.

View Source
var ErrMemoryStateLost = fmt.Errorf("substrate: refusing to cold-create an actor: %w", api.ErrMemoryStateLost)

ErrMemoryStateLost is returned by Create when a REQUIRES_MEMORY_SNAPSHOT session that already has a started turn on this harness has no actor to attach to or resume. Its state existed only in that actor's RAM, so a new actor from the template would continue the journal with a harness that remembers none of it. Restore returns it for every such session whose actor is gone, whatever its journal records (an interrupted first turn included), since the snapshot went with the actor. It wraps api.ErrMemoryStateLost, which the session service reports as FAILED_PRECONDITION.

View Source
var ErrNoSnapshotToClone = errors.New("substrate: fork of a memory-snapshot harness needs a parent snapshot")

ErrNoSnapshotToClone is returned when a REQUIRES_MEMORY_SNAPSHOT harness is forked without a parent memory snapshot to clone. Such a harness's live state exists only in RAM — the journal cannot reconstruct it (I4) — so cold-booting the child would silently produce a divergent branch. Failing loudly is the honest-degradation counterpart to the CanPlace gate.

View Source
var ErrSnapshotNotReady = fmt.Errorf("%w: newest snapshot has no completed durable copy", ErrSnapshotNotRestorable)

ErrSnapshotNotReady is returned by Snapshot, Restore or Create's suspended-actor attachment when the newest snapshot has no completed durable copy. It wraps ErrSnapshotNotRestorable. Snapshot returns no ref and Restore does not resume. Pending uploads may complete; local-only checkpoints require inspection and an explicit durable checkpoint, not merely waiting. For a REQUIRES_MEMORY_SNAPSHOT harness, Restore, Create's attach and Snapshot's check before suspending return it for a suspended actor only while its upload is pending: any other snapshot that is not ready was never recorded, so they return api.ErrMemoryStateLost (see stuckNotReady). A Snapshot whose own suspend leaves the capture not ready still returns it.

View Source
var ErrSnapshotNotRestorable = errors.New("substrate: snapshot is not restorable")

ErrSnapshotNotRestorable is returned by Restore when a held checkpoint is missing or no longer matches a suspended actor's durable snapshot. VOLUMES-only backends and memory refs also require a URI (default stateless backends ignore the legacy memory bit). Invalid checkpoint evidence wraps api.ErrInvalidCheckpoint; control-read failures retain their causes. A running actor keeps ResumeActor's no-op behavior, subject to the memory-tier recorded-UID check. This error alone does not imply permanent memory loss; wrapped causes retain their classifications.

View Source
var ErrSnapshotSuperseded = fmt.Errorf("substrate: parent snapshot was superseded before the fork could tag it: %w", api.ErrSnapshotSuperseded)

ErrSnapshotSuperseded is returned when a stateful fork finds that the parent no longer holds the snapshot the fork took. Substrate tags whatever snapshot an actor holds at tag time, and the router resumes an actor on any request addressed to it, so a parent woken and suspended again between the checkpoint and the tag would hand the children RAM that does not match their copied journal prefix. It wraps api.ErrSnapshotSuperseded, which the session service reports as ABORTED.

View Source
var ErrTagExists = errors.New("substrate: tag already exists")

ErrTagExists is what a SnapshotCloner must return (or wrap) from TagActor when a tag of that name already exists, finished or left pending by an earlier attempt. It tells the backend the tag is not this attempt's to clean up.

Functions

This section is empty.

Types

type ActorInfo

type ActorInfo struct {
	Status ActorStatus
	// Worker names the worker hosting the actor while it holds one, for diagnostics only. The backend
	// never dials it: the harness is reached through the router, which resolves the worker per call.
	Worker string
	// Snapshot is the actor's current external snapshot handle, or "" before its first suspend. It
	// changes on every suspend, so comparing it against a recorded handle tells whether the actor
	// still holds the snapshot a caller took.
	Snapshot string
	// SnapshotFidelity is the observed fidelity of Snapshot, not the template's preferred fidelity.
	// A missing, unspecified or unrecognized value must be reported as SnapshotFidelityUnknown.
	SnapshotFidelity SnapshotFidelity
	// SnapshotNotReady reports that the newest snapshot has no completed durable copy. Clients
	// must not expose an older completed generation instead. This is durable readiness evidence,
	// not proof of the effective restore source (which may be node-local or template-downgraded).
	SnapshotNotReady bool
	// SnapshotPending distinguishes an explicit in-progress durable upload from local-only,
	// failed or unknown durable evidence. It implies SnapshotNotReady; only pending uploads
	// can become ready by waiting, without another checkpoint operation.
	SnapshotPending bool
	// NoSnapshotHistory is positive control-plane evidence that no snapshot generation has ever
	// been assigned and the snapshot list is empty. Only the adapter should set it from authoritative
	// metadata. False means history is present or unknown, never permission to boot without a
	// checkpoint. It permits initial attachment between CreateActor and the first ResumeActor;
	// captures and actors seeded from a fork tag do not receive this exemption.
	NoSnapshotHistory bool
	// MemoryNotRestored is positive control-plane evidence that the actor holds a worker that did
	// not restore the process memory of the MEMORY snapshot its resume restored from, when that
	// snapshot carries session state (its own capture, or a fork tag's), so its RAM is not that
	// snapshot's. Substrate since v0.5.0 does this when no worker matching the snapshot's recorded
	// sandbox runtime has room: it restores the snapshot's volumes only on another worker. A
	// template's golden snapshot holds no session state and never sets it. It stays set while the
	// actor runs on that worker. False is not proof that memory was restored (see the
	// effective-source limits in sessions issue #192).
	MemoryNotRestored bool
	// CaptureLineage identifies the in-RAM state a suspend of the actor captures. For an actor on a
	// worker it is the snapshot generation the next capture will be assigned and the sandbox runtime
	// that capture will be stamped with; for one mid-capture or without a worker it is the newest
	// snapshot's generation and stamp, which a suspend completes or leaves as it is. So a suspend
	// that captures the state an earlier read saw reports the value that read did. A capture or
	// pause by another caller in between followed by a resume changes it (the next capture gets a
	// newer generation), as does a resume onto a worker of another sandbox runtime. A single capture
	// or pause with no resume after it is the capture that read predicted and leaves it as it was,
	// as does a crash and resume onto a worker of the same runtime. The value is opaque: compare it
	// only for equality. "" means the client cannot tell, and a memory-tier Snapshot refuses it (see
	// Backend.Snapshot).
	CaptureLineage string
	// NoCapture is positive control-plane evidence that the actor holds no snapshot captured from it:
	// it has no snapshot, or its newest is the one it was created with, borrowed from a tag (the
	// template's golden snapshot or a fork's source tag), and no later generation was assigned. A
	// suspend or pause of the actor, by any caller, ends it. False means a capture exists or the
	// client cannot tell. A memory-tier session whose journal records no checkpoint is attached to
	// or captured only with it set (see Backend.Create).
	NoCapture bool
	// UID is the actor's server-assigned identity (substrate ResourceMetadata.uid). It is fixed for the
	// actor's lifetime, suspends and resumes included, and differs for an actor created later under the
	// same name, which is how the backend tells a replacement apart. A REQUIRES_MEMORY_SNAPSHOT harness
	// is placed only on an actor that reports one.
	UID string
}

ActorInfo is the subset of an actor's state the backend needs.

type ActorRef

type ActorRef struct {
	Atespace string
	Name     string
}

ActorRef identifies an actor within an atespace (substrate's tenant/namespace).

type ActorStatus

type ActorStatus int

ActorStatus is substrate's actor lifecycle, narrowed to what the backend maps.

const (
	StatusUnknown ActorStatus = iota
	StatusRunning
	StatusSuspended
	StatusTerminated
	// StatusTransitioning is an actor on its way between states (resuming, suspending, pausing),
	// usually because another host or the router is acting on it. It settles on its own, so the
	// backend reports it as ErrActorBusy rather than as a permanent failure.
	StatusTransitioning
)

func (ActorStatus) String added in v0.3.0

func (s ActorStatus) String() string

String names the lifecycle state without exposing control-plane payloads.

type Backend

type Backend struct {
	// contains filtered or unexported fields
}

Backend implements api.Runtime over a substrate ControlClient.

func New

func New(ctl ControlClient, atespace string, template ObjectRef, descriptor api.Descriptor, opts ...Option) *Backend

New builds the backend targeting one atespace and harness ActorTemplate. descriptor is the harness's declared contract, used by the placement gate (see Describe).

func (*Backend) Capabilities

func (b *Backend) Capabilities() api.RuntimeCapabilities

Capabilities reports what the configured backend supports. MemorySnapshot is true by default, but false with WithVolumesOnly. CoWFork stays false deliberately: substrate can clone an actor from a tagged snapshot, but the tag copies the snapshot and each restore materializes a private per-actor copy, so a fork is a full copy and restore, not a copy-on-write share. Attestation and GPU-state capture are not surfaced by the control API today (spike §3/§7).

func (*Backend) Create

func (b *Backend) Create(ctx context.Context, s *api.SessionSpec) (inc api.Incarnation, err error)

Create places the session's actor and returns a live incarnation, whether or not the actor already exists. It is deliberately IDEMPOTENT, because the Placer calls it on every Exec and substrate's CreateActor rejects a repeat with AlreadyExists:

  • absent — create it, then resume it. A new actor holds no snapshot of its own, so substrate starts it from the template's golden snapshot (a harness captured right after it became ready, before any session touched it) or, if that is not built yet, cold-boots the template spec. Either way the harness carries no session state, which is what the first placement needs. For a REQUIRES_MEMORY_SNAPSHOT harness whose session already has started turns on this harness (SessionSpec.StartedTurns, counted after GetActor reports the actor missing), that is exactly wrong: the state those turns built lived only in the missing actor's RAM, so Create refuses with ErrMemoryStateLost and creates nothing.
  • RUNNING — attach. Return the actor's incarnation without touching it. This covers the second and later turns of a session, and the first turn of a FORKED CHILD, whose actor was created from the parent's snapshot and resumed before it was ever handed out.
  • SUSPENDED — resume, which restores the actor's own snapshot, so its RAM is what comes back.

When CreateActor finds the name taken (ErrActorExists), another host placed the session between this call's GetActor and its CreateActor, so Create attaches to or resumes that actor as above.

For a REQUIRES_MEMORY_SNAPSHOT harness the incarnation carries the actor's UID as ComputeInstance, and the host refuses the turn if an earlier turn recorded another one (api.ErrMemoryStateLost). That catches an actor created under the session's name after the actor its turns ran on was lost: a host that counted no started turns can create one before another host's first turn is journaled, and the count above cannot see that turn. A fork child's FORK marker records the UID of the clone Fork returns, so an actor created under the child's name before its first turn is refused too.

For a REQUIRES_MEMORY_SNAPSHOT harness, an existing actor, RUNNING or SUSPENDED, must hold the checkpoint SessionSpec.RecordedCheckpoint reads, read after GetActor answers: the snapshot the session's latest SUSPEND names or, with none recorded, no capture of its own. Otherwise it holds a capture the journal never accepted (one a Snapshot refused stays on the actor) and Create refuses with api.ErrMemoryStateLost before resuming or handing it out. A SUSPENDED actor is checked again on what its resume reports (see attach).

So is the actor Create itself creates. Between CreateActor and ResumeActor it is SUSPENDED under the session's name, where another host's attach or the router can resume it and any caller can then capture it, and ResumeActor restores whatever it holds by then. Create checks the actor the resume reports against SessionSpec.RecordedCheckpoint read after the resume (normally none, so it must hold no capture of its own) and refuses a mismatch with api.ErrMemoryStateLost, leaving the actor running and not handed out (resumedNotHandedOut), as attach refuses the same actor. Unlike a refused fork clone, whose name no other host knows, it is not torn down and the refusal is not ErrActorBusy: the session's name is shared, so the capture can be another host's whose SUSPEND is not appended yet, and Stop deletes by name with no UID precondition.

The distinction matters most for a REQUIRES_MEMORY_SNAPSHOT harness: recreating an actor that already holds live state would silently discard exactly the state fork and suspend exist to carry, and the harness never rebuilds it from Start.History (I4).

func (*Backend) Describe

func (b *Backend) Describe(ctx context.Context) (api.Descriptor, error)

Describe returns the harness's declared contract. Substrate's harness runs REMOTELY inside the actor, so — unlike runtime/local which reads it in-process — the descriptor is CONFIGURED at construction (from the ActorTemplate/registry). This is the M0 form of the spike's registry-resolution: the placement gate reads it BEFORE Create, without provisioning compute.

func (*Backend) Fork

func (b *Backend) Fork(ctx context.Context, ref api.SnapshotRef, opts api.ForkOpts) (inc api.Incarnation, err error)

Fork branches a session's compute into a child incarnation.

Two realizations, chosen by the harness's declared resumability:

  • STATELESS_REPLAY — a replay-fork: the child is a fresh cold actor and the host replays the copied journal prefix into it. The parent's RAM holds nothing the journal lacks, so cloning a snapshot would only add a restore for no gain.
  • REQUIRES_MEMORY_SNAPSHOT — a snapshot clone: the parent's in-RAM state is NOT in the journal, so the child is created from the parent's external snapshot (tag the suspended parent, CreateActor from the tag, then ResumeActor to restore the cloned RAM). The clone the resume reports must hold no capture of its own, as the child's journal records no checkpoint; one that does, or that the client cannot show holds none, is ErrActorBusy (forkCloneCaptured) and is torn down. A retry clones anew, though not past a client that cannot tell.

Cloning is NOT copy-on-write: each tag copies the snapshot in object storage and each child restores into a private per-actor directory, so an N-way fan-out costs N copies and N restores. Capabilities() reports CoWFork=false accordingly.

func (*Backend) Restore

func (b *Backend) Restore(ctx context.Context, ref api.SnapshotRef) (api.Incarnation, error)

Restore brings a suspended actor back from its held durable checkpoint, or reattaches to a running one. Without a held checkpoint, default stateless backends and memory-tier Memory:false refs support live recovery as-is. SnapshotRef.Memory is legacy evidence, not the descriptor's required fidelity.

func (*Backend) RestoreInstance added in v0.3.0

func (b *Backend) RestoreInstance(ctx context.Context, ref api.SnapshotRef, recorded func() (string, error)) (inc api.Incarnation, err error)

RestoreInstance is Restore with a reader for the UID of the actor the session's journal last recorded (api.InstanceRestorer); nil reads none. After a failed resume, that UID is what tells the session's actor, still there, from a replacement (see memoryActorGone).

For a REQUIRES_MEMORY_SNAPSHOT harness the actor ResumeActor reports must still hold ref, the checkpoint the journal last recorded (requireRecordedCheckpoint), as it had to before the resume: another operation can leave a capture the journal never accepted (one a Snapshot refused) between the look and the resume, and ResumeActor restores whatever the actor holds by then, on the runtime that capture is stamped with, so MemoryNotRestored does not catch it. A mismatch is api.ErrMemoryStateLost wrapped in ErrSnapshotNotRestorable, and the actor is left running and not handed out (see resumedNotHandedOut). This sees only what the response reports. It is not a precondition on the resume itself, which v0.5.0's ResumeActorRequest (the actor reference only) cannot carry.

func (*Backend) Snapshot

func (b *Backend) Snapshot(ctx context.Context, in api.Incarnation, kind api.SnapshotKind) (snapshot api.SnapshotRef, err error)

Snapshot suspends the actor to external storage, reporting Memory only for observed MEMORY fidelity. WithVolumesOnly requires observed VOLUMES; a memory-required descriptor or recorded compute instance requires MEMORY. Both require a durable handle and refuse missing evidence. Unknown fidelity remains accepted as Memory:false for default stateless replay. Only the external (cold/suspend) kind is supported; substrate has no node-local warm checkpoint API today.

Whenever the checkpoint must hold memory (a recorded compute instance or a REQUIRES_MEMORY_SNAPSHOT descriptor), Snapshot reads the actor before suspending, and its first check refuses a resuming or suspending actor (StatusTransitioning) with ErrActorBusy, as Restore and Create's attach refuse it: the operation moving it, such as another host's Suspend, may still record the capture it takes. The checks below apply once the actor has settled.

When in.ComputeInstance is set (Placer.Suspend and a stateful fork pass the UID the journal last recorded), the actor under the name must be that one. Snapshot checks it before suspending, so a settled replacement is refused with api.ErrMemoryStateLost untouched, and again on the actor the suspend reports, since the actor can be replaced between the look and the suspend; substrate's SuspendActor has no UID precondition to close that window. In the second case the replacement is left suspended, which costs nothing: it holds none of the session's state and the host refuses it on every later turn. An actor that is gone is refused with api.ErrMemoryStateLost as well, since the state it held went with it. An actor that reports no UID is refused with a plain error: it cannot be compared, and that is a ControlClient fault, not evidence of a replacement. A REQUIRES_MEMORY_SNAPSHOT backend refuses a suspend that reports no UID even when no instance is recorded (a fork child whose FORK marker records no clone, before its first turn). Whenever the checkpoint must hold memory, recorded instance or not, an actor that reports MemoryNotRestored is refused with api.ErrMemoryStateLost before suspending, so its RAM-less state is never recorded as the session's checkpoint.

That check and the suspend are separate calls, and substrate v0.5.0's SuspendActor takes no precondition (no UID, generation or worker), so another caller can suspend the checked actor and resume it on a worker that cannot restore its memory before this suspend captures it. The capture then holds RAM that is not the session's, and the evidence is gone: a suspended actor holds no worker to compare. So whenever the checkpoint must hold memory, Snapshot also requires the suspend to report the UID and CaptureLineage the check saw, and refuses any other with api.ErrMemoryStateLost and no ref; the actor is left suspended. That fails closed: a capture or pause by another caller followed by a resume in the window is refused even when the memory survived. A single capture or pause with no resume after it is the capture this suspend would have taken: the suspend reports the lineage checked and it is recorded. A lineage missing from either read is a plain error, before suspending when the check reports none. A crash and resume onto a worker of the same runtime keeps the lineage, so that window remains; only a precondition on SuspendActor itself would close it.

The refused capture stays on the suspended actor, and nothing on the actor marks it. The journal does: Snapshot also refuses, before suspending, an actor that does not hold in.RecordedCheckpoint (requireRecordedCheckpoint), as Create's attach and Restore do, so that capture is never resumed or recorded as the session's, whether the actor is still suspended on it or the router woke it. A suspended actor whose newest upload is pending is refused as not ready before that check.

func (*Backend) Status

func (b *Backend) Status(ctx context.Context, in api.Incarnation) (state api.ComputeState, err error)

Status maps the actor's substrate status onto the agentsessions compute-lifecycle axis.

func (*Backend) Stop

func (b *Backend) Stop(ctx context.Context, in api.Incarnation) (err error)

Stop suspends then deletes the actor (substrate requires SUSPENDED before delete), then deletes the tag the actor's own fork took of its parent. The tag name is derived from the session UID, so teardown is the last moment it can be named. The actor is deleted first because an actor that never suspended still borrows the tag's snapshot. A session that was never forked simply has no such tag, which is why the delete is best-effort.

type ControlClient

type ControlClient interface {
	// CreateActor creates a SUSPENDED actor, borrowing the template's current golden tag as its
	// first snapshot when one exists. A name that is already taken must be reported as (or wrap)
	// ErrActorExists.
	CreateActor(ctx context.Context, actor ActorRef, template ObjectRef) error
	// ResumeActor schedules the actor onto a worker from its own snapshot, or cold-boots the
	// template spec if it has none. It does not look up the template's golden tag at resume time.
	// There is no per-call boot flag. It returns the actor as the resume left it, with every field
	// GetActor reports: a memory-tier Restore, Create (an attach, or the actor it created) and
	// stateful Fork compare its Snapshot (or NoCapture) with the checkpoint the session's journal
	// recorded, none for a fork child, before handing the actor out.
	ResumeActor(ctx context.Context, actor ActorRef) (ActorInfo, error)
	// SuspendActor snapshots the actor to durable storage and frees the worker, returning the actor
	// as the suspend left it: ActorInfo.Snapshot is the external snapshot it now holds, paired with
	// its observed SnapshotFidelity, and ActorInfo.UID tells which actor under the name was suspended.
	// UID must be set: a memory-tier Snapshot refuses a suspend that reports none, since it cannot
	// tell which actor it captured. So must CaptureLineage, which a memory-tier Snapshot compares
	// with the one GetActor reported when it checked the actor.
	// An actor that does not exist must be reported as (or wrap) ErrActorNotFound.
	SuspendActor(ctx context.Context, actor ActorRef) (ActorInfo, error)
	DeleteActor(ctx context.Context, actor ActorRef) error
	GetActor(ctx context.Context, actor ActorRef) (ActorInfo, error)
}

ControlClient is the subset of agent-substrate's ateapi.Control gRPC that the backend uses. A real substrate deployment implements this over pkg/proto/ateapipb; keeping it an interface here is what keeps agentsessions free of a substrate dependency.

type ObjectRef

type ObjectRef struct {
	Atespace string
	Name     string
}

ObjectRef is an atespace-scoped substrate object other than an actor (e.g. an ActorTemplate).

type Option

type Option func(*Backend)

Option configures a substrate Backend.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger enables structured operational logs.

func WithRouter

func WithRouter(address string) Option

WithRouter overrides the atenet-router address (host:port) incarnations point at. The default, DefaultRouterAddress, is the in-cluster Service a stock substrate install creates.

func WithVolumesOnly added in v0.3.0

func WithVolumesOnly() Option

WithVolumesOnly requires Snapshot to observe a completed VOLUMES snapshot with a durable URI and disables memory-snapshot admission. It validates results, not template configuration: set the template's preferred fidelity to VOLUMES, configure durable directories and disable goldens. Combining it with a memory-required descriptor is unplaceable and Snapshot refuses directly too.

type SnapshotCloner

type SnapshotCloner interface {
	// TagActor tags the external snapshot the SUSPENDED source actor holds right now. The tag gets
	// its own copy of that snapshot, so suspending or deleting the source afterwards cannot collect
	// it. The tag lives in the source actor's atespace. Substrate reserves the tag's name before it
	// copies, so a call that fails may still leave a pending tag behind; a name that was already
	// taken fails with ErrTagExists.
	TagActor(ctx context.Context, source ActorRef, tag SnapshotID) error
	// CreateActorFromTag creates actor seeded from the snapshot behind tag and reports the actor it
	// created, UID included. Substrate requires the actor's template to be the one the snapshot was
	// taken under. Fork compares the reported UID with the one ResumeActor reports, so a clone
	// deleted and replaced under its name between the two calls is refused rather than recorded.
	CreateActorFromTag(ctx context.Context, actor ActorRef, template ObjectRef, tag SnapshotID) (ActorInfo, error)
	// DeleteTag removes a tag and collects its copy of the snapshot. An actor seeded from the tag
	// borrows that copy until its own first suspend, so a tag must outlive its unsuspended clones.
	DeleteTag(ctx context.Context, tag SnapshotID) error
}

SnapshotCloner is the OPTIONAL clone-from-snapshot extension of ControlClient, backed by substrate's Tag APIs. Keeping it separate from ControlClient means a control client that does not implement tags still satisfies the base interface: stateless sessions keep forking (they never need a clone), and a stateful fork refuses loudly instead of silently losing the in-RAM state it exists to carry.

Substrate's CreateActor seeds an actor only from a Tag, and a Tag names a SUSPENDED source actor rather than a snapshot, so cloning is always actor → tag → actor.

type SnapshotFidelity added in v0.3.0

type SnapshotFidelity int

SnapshotFidelity describes what a completed snapshot captured, as reported by the control plane. Unknown is not evidence of files-only capture.

const (
	SnapshotFidelityUnknown SnapshotFidelity = iota
	SnapshotFidelityMemory
	SnapshotFidelityVolumes
	SnapshotFidelityRootFS
)

func (SnapshotFidelity) String added in v0.3.0

func (f SnapshotFidelity) String() string

String names the observed fidelity without exposing a snapshot's storage URI.

type SnapshotID

type SnapshotID struct {
	Atespace string
	Name     string
}

SnapshotID addresses an atespace-owned substrate Tag.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL