[PR #214] [MERGED] feat: add persistent volumes #257

Closed
opened 2026-10-29 14:21:32 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/kvcache-ai/AgentENV/pull/214
Author: @yingdi-shan
Created: 8/25/2026
Status: ✅ Merged
Merged: 9/1/2026
Merged by: @yingdi-shan

Base: main ← Head: feat/issue-211-volumes


📝 Commits (8)

  • 2afc0ae feat(volume): add repository-backed volume storage
  • 0eae48b feat(sandbox): preserve volume drives across lifecycle
  • 3439d56 feat(api): implement persistent volume workflows
  • 40182f5 feat(cli): add persistent volume commands
  • 89fdac0 test(volume): cover lifecycle persistence and compose
  • b3d376f fix(volume): harden shared catalog and lifecycle state
  • 82e5140 fix(volume): harden restore paths and E2E checks
  • 4a2c1c0 fix(volume): address lifecycle and catalog review findings

📊 Changes

72 files changed (+7470 additions, -296 deletions)

View changed files

📝 config/default.toml (+5 -0)
📝 crates/aenv/src/client/mod.rs (+1 -0)
📝 crates/aenv/src/client/sandboxes.rs (+12 -0)
➕ crates/aenv/src/client/volumes.rs (+116 -0)
📝 crates/aenv/src/commands/mod.rs (+1 -0)
📝 crates/aenv/src/commands/start.rs (+111 -3)
➕ crates/aenv/src/commands/volume.rs (+129 -0)
📝 crates/aenv/src/main.rs (+3 -0)
📝 crates/e2e-tests/tests/snapshot_oss_e2e_test.rs (+4 -0)
📝 docs/src/SUMMARY.md (+1 -0)
➕ docs/src/concepts/volumes.md (+220 -0)
📝 docs/src/configuration/reference.md (+7 -0)
📝 docs/src/getting-started/aenv-cli.md (+46 -0)
📝 scripts/tests/e2e/docker-compose.e2e.yml (+5 -1)
📝 scripts/tests/e2e/lib/helpers.sh (+14 -0)
➕ scripts/tests/e2e/suites/15_volume.sh (+149 -0)
➕ scripts/tests/e2e/suites/16_volume_randomized.sh (+476 -0)
📝 services/gateway/internal/server.go (+42 -4)
📝 services/gateway/internal/server_test.go (+8 -1)
📝 src/api/generated/src/apis/mod.rs (+1 -0)

...and 52 more files

📄 Description

What

Add E2B-compatible persistent volumes backed by the configured snapshot repository, with volume CRUD, sandbox mounts, lifecycle persistence, and native CLI commands.

Volumes are visible from every AgentENV node that shares the POSIX or OSS repository. New volumes default to 64 GiB, can be created with an explicit size, expose uploading while publication is incomplete, and support exclusive writable or shared read-only mounts.

Why

Closes the persistent-volume gap tracked in issue #211. Previously, attached storage was node-local and could not be listed or reused reliably from another runtime node. Users also lacked an E2B-aligned API and CLI flow for creating, sizing, mounting, and deleting durable volumes.

Closes #211

Scope and non-goals

Included:

  • Repository-authoritative volume catalogs for POSIX and OSS backends.
  • Volume create, get, list, and delete APIs, plus sandbox volumeMounts.
  • A 64 GiB default size and explicit size support in the API and CLI.
  • ready and uploading status semantics.
  • Exclusive writable reservations and shared read-only mounts.
  • Volume data preservation across pause, resume, sandbox fork, sandbox snapshots, deletion, and multi-node reuse.
  • Native aenv volume commands and aenv start --volume mounting.
  • Deterministic and randomized multi-node Compose coverage.

Not included:

  • Scheduler changes; repository storage is the cross-node source of truth.
  • Separate fork, snapshot, or restore endpoints for volumes. Volume snapshots follow sandbox lifecycle operations.
  • Dynamic changes between read-only and writable mode for an existing reserved drive.

Design and behavior changes

The configured snapshot repository owns the authoritative volume catalog and backing layers. Each node reloads this shared catalog, so creating a volume through node A makes it listable and mountable through node B without scheduler state.

Creating an empty volume provisions an OverlayBD-compatible filesystem image. Creating from another volume publishes an independent child backing. A volume is uploading while its backing is being published and cannot be mounted until it becomes ready.

Writable volumes use an exclusive repository reservation. Read-only volumes can be mounted by multiple sandboxes and are exposed read-only to the guest, so guest writes fail at the block device/filesystem boundary. Sandbox mount paths are supplied as the E2B-compatible path-to-volume map.

Mounted drives are remounted through envd's /patch/drives flow after resume. Pause and deletion seal and publish recent writable data before releasing ownership. Sandbox fork and sandbox snapshot operations create logical volume snapshots with independent backing references. Expired sandboxes with mounted writable volumes are atomically claimed, paused to flush their drives, and then deleted.

After the durable catalog entry is deleted, node-local backing cleanup is best-effort. A local cleanup failure is logged and does not turn a successful durable deletion into a misleading HTTP 500.

Compatibility and operations

  • Public API or generated protocol: Adds /volumes CRUD operations, volume models/status fields, and volumeMounts on sandbox creation. Generated Rust server bindings are updated from src/api/openapi.yml.
  • Configuration or defaults: No new configuration key. Omitted volume size defaults to 65,536 MiB (64 GiB).
  • Snapshot manifest, artifact layout, or storage format: Adds logical volume snapshot metadata and repository volume catalog/backing paths. POSIX and OSS repositories implement the same catalog contract.
  • Upgrade and rollback: Existing snapshots remain readable through serde defaults. Rollback leaves the new repository volume artifacts unused; operators should avoid creating new volumes during a mixed-version rollback window.
  • Host requirements, permissions, ports, or dependencies: No new port or daemon. Uses the existing OverlayBD/ublk runtime and repository credentials; empty-volume creation requires the existing mkfs.ext4 runtime package.

Validation

  • make fmt
  • make clippy
  • make test-unit
  • Relevant Rust integration tests
  • make -C services test (required when services/ changes)
  • Generated clients/server regenerated with the documented make target
  • Documentation updated
  • Benchmarks or performance comparison completed

Commands and results:

make agentenv-server
  PASS — server bindings regenerated from OpenAPI

make fmt
  PASS

make clippy
  PASS — workspace, all targets/features, warnings denied

make test-unit
  PASS — AgentENV/envd/linux-cap, privileged ignored tests, ublk/daemon, and script verifiers

make test-agent-integration PROFILE=debug
  PASS — 20 integration, 2 orchestrator integration, and 4 OSS repository tests

make -C services test
  PASS — gateway and scheduler packages

E2E_MODE=compose SUITE_FILTER='1[56]_volume*.sh' \
  AENV_VOLUME_RANDOM_SEED=21106 AENV_VOLUME_RANDOM_STEPS=100 \
  scripts/tests/e2e/run_e2e.sh
  PASS — deterministic suite 59/59; randomized multi-node suite 2078/2078

Skipped checks and reasons:

  • Documentation: the API schema, CLI help, and executable test suites describe the user-facing behavior; no existing documentation page covered volumes.
  • Benchmarks: this change is lifecycle and correctness focused, with no established volume benchmark baseline.

Checklist

  • The PR contains one coherent change and no unrelated formatting or refactoring.
  • New behavior is covered by tests, or I explained why testing is impractical.
  • Logs and examples contain no credentials, tokens, or private registry information.
  • I did not manually edit generated code without updating its source and regenerating it.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/kvcache-ai/AgentENV/pull/214 **Author:** [@yingdi-shan](https://github.com/yingdi-shan) **Created:** 8/25/2026 **Status:** ✅ Merged **Merged:** 9/1/2026 **Merged by:** [@yingdi-shan](https://github.com/yingdi-shan) **Base:** `main` ← **Head:** `feat/issue-211-volumes` --- ### 📝 Commits (8) - [`2afc0ae`](https://github.com/kvcache-ai/AgentENV/commit/2afc0aeb1ed723dbf9ca243065dedb6bd25014fd) feat(volume): add repository-backed volume storage - [`0eae48b`](https://github.com/kvcache-ai/AgentENV/commit/0eae48b0539bc9cda889448adf7890448694840c) feat(sandbox): preserve volume drives across lifecycle - [`3439d56`](https://github.com/kvcache-ai/AgentENV/commit/3439d56dabe7ce192b7fcfed9abf38846ad8233b) feat(api): implement persistent volume workflows - [`40182f5`](https://github.com/kvcache-ai/AgentENV/commit/40182f5adae5aa4ca4c7f35784b4cd205ef96b4a) feat(cli): add persistent volume commands - [`89fdac0`](https://github.com/kvcache-ai/AgentENV/commit/89fdac0e1b1f4945259c09ee0a84f356be2c23e1) test(volume): cover lifecycle persistence and compose - [`b3d376f`](https://github.com/kvcache-ai/AgentENV/commit/b3d376f817ce7578494bd0d57f889a5361bba238) fix(volume): harden shared catalog and lifecycle state - [`82e5140`](https://github.com/kvcache-ai/AgentENV/commit/82e5140174f51ec4de9990f74df82f074458d491) fix(volume): harden restore paths and E2E checks - [`4a2c1c0`](https://github.com/kvcache-ai/AgentENV/commit/4a2c1c07c8f9e3a0029a2c36aa4f622fb06ff20c) fix(volume): address lifecycle and catalog review findings ### 📊 Changes **72 files changed** (+7470 additions, -296 deletions) <details> <summary>View changed files</summary> 📝 `config/default.toml` (+5 -0) 📝 `crates/aenv/src/client/mod.rs` (+1 -0) 📝 `crates/aenv/src/client/sandboxes.rs` (+12 -0) ➕ `crates/aenv/src/client/volumes.rs` (+116 -0) 📝 `crates/aenv/src/commands/mod.rs` (+1 -0) 📝 `crates/aenv/src/commands/start.rs` (+111 -3) ➕ `crates/aenv/src/commands/volume.rs` (+129 -0) 📝 `crates/aenv/src/main.rs` (+3 -0) 📝 `crates/e2e-tests/tests/snapshot_oss_e2e_test.rs` (+4 -0) 📝 `docs/src/SUMMARY.md` (+1 -0) ➕ `docs/src/concepts/volumes.md` (+220 -0) 📝 `docs/src/configuration/reference.md` (+7 -0) 📝 `docs/src/getting-started/aenv-cli.md` (+46 -0) 📝 `scripts/tests/e2e/docker-compose.e2e.yml` (+5 -1) 📝 `scripts/tests/e2e/lib/helpers.sh` (+14 -0) ➕ `scripts/tests/e2e/suites/15_volume.sh` (+149 -0) ➕ `scripts/tests/e2e/suites/16_volume_randomized.sh` (+476 -0) 📝 `services/gateway/internal/server.go` (+42 -4) 📝 `services/gateway/internal/server_test.go` (+8 -1) 📝 `src/api/generated/src/apis/mod.rs` (+1 -0) _...and 52 more files_ </details> ### 📄 Description ## What Add E2B-compatible persistent volumes backed by the configured snapshot repository, with volume CRUD, sandbox mounts, lifecycle persistence, and native CLI commands. Volumes are visible from every AgentENV node that shares the POSIX or OSS repository. New volumes default to 64 GiB, can be created with an explicit size, expose `uploading` while publication is incomplete, and support exclusive writable or shared read-only mounts. ## Why Closes the persistent-volume gap tracked in issue #211. Previously, attached storage was node-local and could not be listed or reused reliably from another runtime node. Users also lacked an E2B-aligned API and CLI flow for creating, sizing, mounting, and deleting durable volumes. ## Related issue Closes #211 ## Scope and non-goals Included: - Repository-authoritative volume catalogs for POSIX and OSS backends. - Volume create, get, list, and delete APIs, plus sandbox `volumeMounts`. - A 64 GiB default size and explicit size support in the API and CLI. - `ready` and `uploading` status semantics. - Exclusive writable reservations and shared read-only mounts. - Volume data preservation across pause, resume, sandbox fork, sandbox snapshots, deletion, and multi-node reuse. - Native `aenv volume` commands and `aenv start --volume` mounting. - Deterministic and randomized multi-node Compose coverage. Not included: - Scheduler changes; repository storage is the cross-node source of truth. - Separate fork, snapshot, or restore endpoints for volumes. Volume snapshots follow sandbox lifecycle operations. - Dynamic changes between read-only and writable mode for an existing reserved drive. ## Design and behavior changes The configured snapshot repository owns the authoritative volume catalog and backing layers. Each node reloads this shared catalog, so creating a volume through node A makes it listable and mountable through node B without scheduler state. Creating an empty volume provisions an OverlayBD-compatible filesystem image. Creating from another volume publishes an independent child backing. A volume is `uploading` while its backing is being published and cannot be mounted until it becomes `ready`. Writable volumes use an exclusive repository reservation. Read-only volumes can be mounted by multiple sandboxes and are exposed read-only to the guest, so guest writes fail at the block device/filesystem boundary. Sandbox mount paths are supplied as the E2B-compatible path-to-volume map. Mounted drives are remounted through envd's `/patch/drives` flow after resume. Pause and deletion seal and publish recent writable data before releasing ownership. Sandbox fork and sandbox snapshot operations create logical volume snapshots with independent backing references. Expired sandboxes with mounted writable volumes are atomically claimed, paused to flush their drives, and then deleted. After the durable catalog entry is deleted, node-local backing cleanup is best-effort. A local cleanup failure is logged and does not turn a successful durable deletion into a misleading HTTP 500. ## Compatibility and operations - Public API or generated protocol: Adds `/volumes` CRUD operations, volume models/status fields, and `volumeMounts` on sandbox creation. Generated Rust server bindings are updated from `src/api/openapi.yml`. - Configuration or defaults: No new configuration key. Omitted volume size defaults to 65,536 MiB (64 GiB). - Snapshot manifest, artifact layout, or storage format: Adds logical volume snapshot metadata and repository volume catalog/backing paths. POSIX and OSS repositories implement the same catalog contract. - Upgrade and rollback: Existing snapshots remain readable through serde defaults. Rollback leaves the new repository volume artifacts unused; operators should avoid creating new volumes during a mixed-version rollback window. - Host requirements, permissions, ports, or dependencies: No new port or daemon. Uses the existing OverlayBD/ublk runtime and repository credentials; empty-volume creation requires the existing `mkfs.ext4` runtime package. ## Validation - [x] `make fmt` - [x] `make clippy` - [x] `make test-unit` - [x] Relevant Rust integration tests - [x] `make -C services test` (required when `services/` changes) - [x] Generated clients/server regenerated with the documented `make` target - [ ] Documentation updated - [ ] Benchmarks or performance comparison completed Commands and results: ```text make agentenv-server PASS — server bindings regenerated from OpenAPI make fmt PASS make clippy PASS — workspace, all targets/features, warnings denied make test-unit PASS — AgentENV/envd/linux-cap, privileged ignored tests, ublk/daemon, and script verifiers make test-agent-integration PROFILE=debug PASS — 20 integration, 2 orchestrator integration, and 4 OSS repository tests make -C services test PASS — gateway and scheduler packages E2E_MODE=compose SUITE_FILTER='1[56]_volume*.sh' \ AENV_VOLUME_RANDOM_SEED=21106 AENV_VOLUME_RANDOM_STEPS=100 \ scripts/tests/e2e/run_e2e.sh PASS — deterministic suite 59/59; randomized multi-node suite 2078/2078 ``` Skipped checks and reasons: - Documentation: the API schema, CLI help, and executable test suites describe the user-facing behavior; no existing documentation page covered volumes. - Benchmarks: this change is lifecycle and correctness focused, with no established volume benchmark baseline. ## Checklist - [x] The PR contains one coherent change and no unrelated formatting or refactoring. - [x] New behavior is covered by tests, or I explained why testing is impractical. - [x] Logs and examples contain no credentials, tokens, or private registry information. - [x] I did not manually edit generated code without updating its source and regenerating it. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-29 14:21:32 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kvcache-ai/AgentENV#257
No description provided.