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§
- Control
Plane Minter - 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 toSandbox::adopt(or the prelude’sSandbox::adopt_in). - RunRequest
- Everything a launch needs, with the defaults the Python client measured.
- Sandbox
- One MicroVM’s whole life.
- Teardown
Opts - What a teardown should delete beyond the VM itself.
- Teardown
Report - 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’sterminate --wait). - DEFAULT_
READY_ TIMEOUT Deprecated - 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 iscrate::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
WaitOptsreuses the interval. Sandbox launches poll faster during their initial retries.