mirror of
https://github.com/kvcache-ai/AgentENV.git
synced 2026-11-05 01:17:36 +00:00
[GH-ISSUE #441] Restore a sandbox from a snapshot with its original sandbox ID #106
Labels
No labels
bug
documentation
enhancement
enhancement
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
kvcache-ai/AgentENV#106
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @aniket-sarvam on GitHub (Oct 27, 2026).
Original GitHub issue: https://github.com/kvcache-ai/AgentENV/issues/441
Affected component
Orchestrator / sandbox lifecycle
Problem statement
As an operator running AgentENV as a multi-node sandbox platform,
I need a sandbox restored from a snapshot on another node to keep its original sandbox ID,
because every create, including a create from a snapshot, gets a new ID, so a sandbox cannot be moved between nodes and still look like the same sandbox to clients.
In E2B, a sandbox keeps its ID across pause and resume, and clients build data-plane addresses from it (
{port}-{sandboxID}.{domain}, theE2b-Sandbox-Idheader). In AgentENV, pause and resume keep the ID but are node-local (the paused state lives underpersisted_sandbox_store_pathon that node). The only way to resume a sandbox on a different node, or after its node is gone, is to snapshot it and create a new sandbox from the snapshot, which assigns a new ID.Use case
We run AgentENV on Kubernetes with several runtime nodes and a shared S3 snapshot repository. Agent sessions sit idle for hours, so we free their compute by snapshotting the sandbox and deleting it ("hibernate"), and restore it from the snapshot when the session continues. Nodes are added and removed by an autoscaler, so the restore usually lands on a different node from the original.
This happens for most sessions, many times a day per session, across hundreds of concurrent sandboxes.
Current behavior and workarounds
OrchestratorService::create_sandboxalways generates the ID:github.com/kvcache-ai/AgentENV@abc027b/src/orchestrator/service.rs (L425-L435)so a sandbox restored from a snapshot comes back with a new ID. Consequences for clients:
sbx.connect()on an existingSandbox) stop working.{port}-{sandboxID}.{domain}URLs from it, stop working.SandboxAccessTokenGenerator::generate/generate_traffic,github.com/kvcache-ai/AgentENV@abc027b/src/sandbox/access.rs (L71-L77)), so tokens that clients already hold become invalid too.Workarounds, and why they fall short:
Sandbox.connect(id), which adopts the ID and tokens the server returns, and keep an ID mapping outside AgentENV. Breaks E2B semantics for any client that holds aSandboxobject or a stored ID.Desired behavior
X, and sandboxXno longer running anywhere,X,X, the gateway routesXto its new node, and its envd and traffic access tokens are the same as before (given the sameaccess_token_hash_seedacross nodes).Xis still live on the target node, the request fails with409 Conflict.Proposed approach
The orchestrator already supports creating with a caller-chosen ID:
create_sandbox_inner(sandbox_id, request, template_builder)takes the ID as a parameter, andcreate_template_builderpasses its own (github.com/kvcache-ai/AgentENV@abc027b/src/orchestrator/service.rs (L437-L456)). The change would be:sandboxIDto the create request (NewSandbox//v2/sandboxes), accepted only from an admin-scoped caller and only when creating from a snapshot.CreateSandboxRequestintocreate_sandbox_inner, keepingSandboxId::new()as the default.409if the ID is live on the node, using the orchestrator's existing store.RecordAssignment), and node heartbeats refresh bindings, so the binding moves to the new node without scheduler changes. Worth a test that a stale binding on the old node cannot win.Cluster-wide uniqueness (that
Xis not live on another node) would stay the caller's responsibility, which suits a control plane that already serialises lifecycle operations per sandbox. A gateway-side check throughLookupNodecould be added if you prefer it enforced inside AgentENV.Compatibility and operational impact
API/config changes: one optional request field, admin-only. No change for existing callers.
Snapshot or storage format changes: none.
New host/runtime requirements: none.
Upgrade and rollback considerations: older servers don't know the field, so clients should only send it to servers that support it. Rolling back removes the ability, nothing else.
Alternatives considered
Non-goals
Contribution
I can implement it
Pre-submission checklist
@yingdi-shan commented on GitHub (Oct 28, 2026):
Thanks, this is a valid need, but we want to be careful with the API design. Creating a sandbox from a snapshot naturally gives it a new sandbox ID, because the original sandbox keeps running after the snapshot is taken. If a restored sandbox could reuse the source ID, nothing would stop the source, or a second restore of the same snapshot, from being live under that ID at the same time.
We think the underlying problem is a different one: how to remove a node and move its paused and running sandboxes to other nodes while keeping their sandbox IDs. Pause/resume already keeps the ID. The gap is that paused state is stored only on the node that paused it. So we'd prefer to solve this by migrating paused sandboxes, as part of VM migration (#11): the source node hands the sandbox off, and the target node resumes it under the same ID. Your hibernate workflow would then become pause → migrate → resume.
Would this solve your problem? Full VM migration for running sandboxes may take a while, but migrating paused sandboxes should be a much smaller effort. That said, v0.3 includes a significant refactor of pause, so we plan to implement this after the v0.3 release.