mirror of
https://github.com/kvcache-ai/AgentENV.git
synced 2026-11-05 01:17:36 +00:00
[GH-ISSUE #31] Support attached drives when creating a sandbox from a template #14
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#14
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 @jiaoqsh on GitHub (Jul 28, 2026).
Original GitHub issue: https://github.com/kvcache-ai/AgentENV/issues/31
Summary
attachedDrivesis currently accepted only byPOST /sandboxes-cold. Once a drive isattached during a cold start, its
driveID,mountPath,subPathandreadOnlyarefrozen into the snapshot manifest, and every sandbox later created from that snapshot
inherits them verbatim — there is no way to add, remove, or reconfigure a drive at launch
time.
In effect, "attach a drive" and "start fast from a snapshot" are mutually exclusive at the
API level. I'd like to propose adding
attachedDrivestoNewSandbox(POST /sandboxes),and I'm happy to implement it — but the merge semantics need to be agreed first, which is
why this is an issue rather than a PR.
Current behavior
attachedDrivesPOST /sandboxes-coldPOST /sandboxesRelevant code:
NewColdSandbox.attachedDrives—src/api/openapi.yml:605;NewSandboxhas noequivalent field —
src/api/openapi.yml:510-548SandboxLaunchSource::Image { .., extra_drives, .. }carries drives, whileSandboxLaunchSource::Snapshot(Box<RunnableSnapshot>)has nowhere to put them —src/orchestrator/types.rs:13-25src/sandbox/firecracker/manifest.rs:52-64(FirecrackerAttachedDriveArtifactsstoresdrive_id,read_only,mount_path,sub_path,virtual_size)src/snapshot/types/snapshot.rs:472andFirecrackerSnapshotManifest::extra_drives()The runtime is already capable of this: snapshot-based launches prepare inherited drives
through the same
prepare_extra_drivespath as cold starts (viaExtraDrivePrepareMode::Resume), and read-only and writable drives already share a singledevice lifecycle (
docs/src/internals/architecture.md:124). What is missing is the APIentry point, not the machinery.
Why this is limiting
Any workload where the environment and the data vary independently runs into this. With
N template environments and M versions of a shared dataset or config, you currently need
N×M templates, because the only way to get a drive onto a fast-starting sandbox is to bake
it in at cold-start time and snapshot it.
Updating a shared read-only dataset likewise forces a template rebuild, even though the
drive content is just an OCI image reference that could be selected at launch.
Prior art
Both Kubernetes and E2B separate what the data is from how it is mounted — exactly the
separation AgentENV currently collapses.
Kubernetes declares the volume source in
spec.volumesand its mount configurationper-container in
containers[].volumeMounts(mountPath,subPath,readOnly). The samevolume can be mounted at different paths, with different
readOnlysettings, in differentcontainers. Kubernetes also has an
imagevolume source that mounts an OCI image referenceas a read-only volume — essentially the same primitive as
AttachedDriveSource.image, butwith the mount configuration supplied at pod creation rather than frozen into the artifact:
E2B mounts volumes at sandbox creation time, so the mount path is a property of the
creation call rather than of the volume:
In AgentENV today, both the source and the mount configuration are fixed at cold-start time
and inherited immutably thereafter.
Proposal (sketch)
attachedDrivesfield toNewSandbox.SandboxLaunchSource::Snapshota place to carry launch-provided drives and threadit through
LaunchPlan::for_create_from_snapshot.RunnableSnapshot::attached_drives()under agreedrules (see open questions).
MAX_EXTRA_DRIVES(24,src/sandbox/firecracker/config.rs:52) against themerged set.
attachedDrivesis currently not mentioned indocs/src/concepts/sandboxes.mdat all.Open questions
Was the cold-start-only restriction deliberate? I suspect it follows from the
latency contract (see below) rather than being an intentional design constraint, but I'd
like to confirm before implementing.
Merge conflict rules. When a launch-provided drive collides with a snapshot-inherited
one by
driveIDor bymountPath— reject with 400, or allow override? My inclinationis to always reject on
mountPathcollision, and to allowdriveIDoverride only if wedecide to support question 3.
Should launch-time overrides of inherited drives be supported? That is, re-mounting an
inherited drive at a different
mountPath/subPath/readOnly, the way Kuberneteslets each container mount a shared volume differently. This is arguably the more valuable
half of the feature, but it is also the larger API commitment.
Should launch-provided read-only drives be captured into snapshots taken from that
sandbox? Current behavior captures all attached drive state. For a shared read-only
reference drive, that makes every downstream snapshot carry a dependency on that image.
Excluding read-only launch-provided drives from capture may be preferable, but it makes
snapshot content depend on how the sandbox was launched.
diskSizeMBsemantics on this path.docs/src/concepts/sandboxes.mdstates thatresizing applies only when creating a fresh writable root filesystem — not to read-only
images, images with an existing upper, or snapshot resume. A drive newly attached on a
snapshot-based launch is none of those exactly. Reject
diskSizeMBhere, or honor it?Device naming stability. Guest device letters are assigned by attachment order
(
vd(c+i),src/sandbox/firecracker/sandbox.rs:971), not bydriveID. Appendinglaunch-provided drives after inherited ones keeps existing devices stable, but any future
support for removing an inherited drive would shift every subsequent letter. Worth
fixing the ordering rule explicitly as part of this change.
Note on start latency
The most likely objection:
resolve_attached_drivescallsImageResolver::resolve(
src/api/impls/attached_drives.rs:95), which on a cache miss performs a full OCI pull andconversion. That is presumably why the field lives on
/sandboxes-cold, whose descriptionalready warns about tens-of-seconds latency. Adding it to
POST /sandboxesas-is wouldimport unbounded latency into the fast path.
Two mitigations, which I'd propose combining:
overlaybd-native images (which skip blob download entirely and are read on demand through
registryfs_v2). Reject anything else with a 400 pointing at/sandboxes-coldor at apre-warm step.
the latency.
This keeps the default latency characteristics of
POST /sandboxesunchanged.#9 wrestles with the same tension from the other direction (a synchronous timeout versus
202 Acceptedwith polling for large images), so whatever convention is settled on therewould be good to follow here.
Related issues
capability that today exists only on the cold path should be reachable from the regular
creation API. That issue covers the rootfs; this one covers attached drives. They are
independent but should probably agree on how OCI resolution latency is surfaced.
volumeMountsonPOST /sandboxesand the
/volumesCRUD endpoints as not implemented. This issue is deliberately nota proposal to implement E2B Volumes — attached drives are OCI-image-backed and have no
independent lifecycle — but making mount configuration a property of the creation call
is a prerequisite for any future volume-style feature, and would close part of the
volumeMountsgap tracked there.Willing to implement
Happy to send a PR once the semantics above are settled.
tests/integration/snapshot_attached_drive.rslooks like the natural place to extend coverage.
@yingdi-shan commented on GitHub (Jul 28, 2026):
Thanks for putting this together. I agree with the underlying goal: users should be able to select and configure attached drives at launch time.
I’m hesitant to add this directly to
POST /sandboxes. That endpoint is intended to remain a minimal, predictable fast path for restoring an existing snapshot. Supporting launch-time drives there could make its behavior and latency less predictable, particularly when a snapshot already contains attached drives.POST /sandboxes-cold, on the other hand, always starts from a fresh OCI image and is expected to be slower.Related to the direction discussed in #9, I’m considering a separate image-oriented endpoint, tentatively
/from-images. It could dynamically choose whether to use a compatible snapshot for a warm start or fall back to a cold start. It would also be a better place for richer launch-time operations such as mounting drives, hot-plugging memory, and resizing disks, allowing more efficient use of automatically created templates.This would keep the contract of
POST /sandboxessimple while providing a flexible interface for both the rootfs use case in #9 and the attached-drive use case described here.@jiaoqsh commented on GitHub (Jul 28, 2026):
That makes sense — keeping
POST /sandboxesas a predictable snapshot-restore path is abetter contract than what I proposed, and it removes the need for the cache-only
restriction I suggested, since
/from-imageswouldn't be promising a fast path in thefirst place.
Two things I'd want to pin down about
/from-images, both about whether it fully coversthe case in this issue:
1. Does the drive set participate in snapshot compatibility?
If the auto-template key in #9 (
{IMAGE_DIGEST}-{CPU}c-{MEM}mb-{DISK}gb) is extended toinclude attached drives, then every new (rootfs image, dataset version) combination
requires a cold start to materialize its template — which is the cost this issue is trying
to avoid, just relocated.
If instead drives are attached at launch onto a rootfs-only snapshot, the endpoint stays
clean and the template namespace stays small — but the runtime still has to attach drives
on a snapshot-backed launch, so the merge and device-ordering questions above still need
answers; they'd just live behind
/from-imagesinstead ofPOST /sandboxes.I'd advocate for the second. Worth deciding early, since it determines whether the
underlying work is "resolve drives and attach on resume" or "bake drives into the template
key".
2. What about templates that aren't derived from a single image?
/from-imagesis image-oriented, but templates produced by the template builder(RUN / ENV / WORKDIR steps) don't correspond to one OCI image reference. If a user has such
a template and wants to mount a dataset drive into it, is that in scope for
/from-images,or would it need something else? This is also where the "re-mount an inherited drive at a
different path" case from open question 3 would live, if that's wanted at all.
@yingdi-shan commented on GitHub (Jul 28, 2026):
We should avoid including attached drives in the template key, as this could cause a combinatorial explosion of templates. Instead, we should keep template key rootfs-only, attach drives via Firecracker's PATCH /drives API, and mount them by running a command inside the sandbox.
Templates not derived from a single image are also a valid use case. To support this, we can build a general-purpose interface that combines PATCH /drives with drive mounting, then use that interface to implement the /from-images behavior described above.
@jiaoqsh commented on GitHub (Jul 29, 2026):
Sounds good. Two points that might be worth noting:
1.
PATCH /drivescannot change read-only-ness. The vendored Firecracker 1.15.1 spec(
thirdparty/firecracker-client/firecracker.yaml) definesPartialDriveas{drive_id, path_on_host?, rate_limiter?}— there is nois_read_only. So whichever stepcreates a drive fixes whether it is read-only, and a later
PATCHcan only re-pointpath_on_host. Noting it since it affects which of the currentAttachedDrivefields canvary per launch.
2. Mounting currently lives in the guest init, and
subPathwith it. Todaytools-image/initparses theagentenv_drives=vd<letter>:<mountPath>[:<subPath>]boot argand mounts before
pivot_root. ForsubPathit stages a mount, bind-mounts the requestedsubdirectory onto the guest mount path, then lazy-detaches the staging mount. If mounting
moves to a command run inside the sandbox, that
subPathbehavior would need to be handledsomewhere else, and mount failures would surface after the sandbox is already running rather
than during boot.
Looking forward to seeing where this lands.
@yingdi-shan commented on GitHub (Sep 1, 2026):
Resolved in PR #214.