Skip to main content

Module sandbox

Module sandbox 

Source
Expand description

The Sandbox lifecycle: one VM’s whole life, with the state machine the symspec model describes made true of the code.

Composes crate::control and crate::session into the surface the CLI and the bindings use: build, run, suspend, resume, terminate, a resume of a VM that ended before it (STATE-12), and teardown in the order that does not leak.

§The lifecycle is a field, and every transition is guarded

Lifecycle is the symspec’s vm_state verbatim, and Sandbox carries the other four variables beside it — token_installed, image_exists, was_terminated, bootstrap_count. The Z3 proofs over that model (bootstrap at most once, suspend from non-RUNNING unreachable, TERMINATED never returns to RUNNING) are proofs about this struct’s reachable states, which is only worth something if the transitions here are the only way to move it. Those fields are private and transitions are guarded here. Adoption initializes an existing VM from service-observed state and is covered by the adoption tests; it does not replay the launch bootstrap.

§Runtime-checked rather than typestate, deliberately

The packet offered Sandbox<Running> returning a Suspended handle, which would make STATE-5’s wrong call a compile error rather than a local refusal — strictly stronger on the ladder in crate. It is not what landed, for the reason the packet names as acceptable: T-W3-8 wraps one object for PyO3 and napi-rs, and a type whose Rust identity changes on every transition cannot be one #[pyclass]. A typestate sandbox would be re-erased into a runtime-checked enum at the binding boundary, so the check would exist twice with the binding’s copy being the one most callers actually hit.

What is kept from the typestate idea is the part that costs nothing: the check happens before the wire call, so a suspend from SUSPENDED is refused with zero control-plane calls rather than answered by AWS. The test asserts the call count, which is the observable that distinguishes the two.

§The service decides whether a suspended VM resumes (STATE-12)

The launch-time idlePolicy terminates a VM the idle policy suspended once suspendedDurationSeconds passes, and the service doesn’t hold an explicit SuspendMicrovm to that window: measured 2026-10-12 in us-east-1, a VM this client suspended resumed 1283 s into a 60 s window (#597). So a resume is never refused here for the time it has spent suspended. It goes to the service, and the service’s refusal for a VM that ended, whatever ended it, comes back from ControlPlane::resume as ErrorKind::WindowClosed, which this sandbox records as a termination (STATE-11).

A sandbox held as RUNNING reads GetMicrovm before deciding whether it can resume. This observes a suspend or termination made by the platform idle policy. A service state of SUSPENDED follows the normal resume path; an ended VM is WindowClosed.

§Teardown never raises, and the log group is last

Sandbox::terminate returns a TeardownReport rather than a Result. It runs where a caller’s finally would, and an error raised there replaces the real failure with a teardown failure — the real one being the one worth reading.

The order is VM, then image (retrying), then the log group last, because the service can recreate a group deleted before its image. See TeardownReport::undeleted for what this crate can and cannot delete.

§There is no Drop that tears down

Rust has no context manager and Drop cannot await. A Drop that blocked on a runtime would deadlock inside one; a Drop that spawned would race the process exit. So Sandbox’s Drop only warns about a live VM, naming the id, and the rule is that a caller calls Sandbox::terminate explicitly.

Structs§

ControlPlaneMinter
Mints proxy tokens for one MicroVM through the control plane.
Detached
What another process needs to adopt a VM this sandbox handed off with Sandbox::detach: pass the fields to Sandbox::adopt (or the prelude’s Sandbox::adopt_in).
RunRequest
Everything a launch needs, with the defaults the Python client measured.
Sandbox
One MicroVM’s whole life.
TeardownOpts
What a teardown should delete beyond the VM itself.
TeardownReport
What a teardown did, and what it left behind.

Enums§

Lifecycle
The symspec’s vm_state, verbatim.

Constants§

DEFAULT_DELETE_ATTEMPTS
How many times a teardown retries the image delete.
DEFAULT_DELETE_BACKOFF
The gap between image-delete attempts.
DEFAULT_LIFECYCLE_TIMEOUT
The default lifecycle wait for suspend, resume, and terminate, and for a state wait the caller gives no deadline (the bindings’ wait_for_state, the CLI’s terminate --wait).
DEFAULT_READY_TIMEOUTDeprecated
The old name of DEFAULT_RUNNING_TIMEOUT, kept for one release.
DEFAULT_RUNNING_TIMEOUT
The default wait for a launch to reach RUNNING: five minutes, matching the Python client’s ready_timeout_sec. The daemon wait after it is crate::session::DEFAULT_BOOTSTRAP_TIMEOUT.
DENY_EGRESS_ENV_KEYS
The environment variables the advisory deny sets, in both spellings clients read.
DENY_EGRESS_PROXY_URL
Where the advisory deny points a well-behaved client: a loopback port nothing serves.
LIFECYCLE_POLL_INTERVAL
Default lifecycle polling interval. Public so an adapter that builds its own WaitOpts reuses the interval. Sandbox launches poll faster during their initial retries.