[PR #349] feat(compose): run Compose projects in isolated sandboxes #368

Open
opened 2026-10-29 14:22:42 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/kvcache-ai/AgentENV/pull/349
Author: @Primary33
Created: 10/9/2026
Status: 🔄 Open

Base: main ← Head: feat/compose-runtime


📝 Commits (3)

  • f5d5f49 feat(compose): add the Compose planner
  • 87a326e feat(compose): add the guest Compose runtime image
  • b4010e5 feat(compose): run Compose projects in isolated sandboxes

📊 Changes

34 files changed (+2478 additions, -24 deletions)

View changed files

📝 .github/workflows/release.yml (+10 -0)
➕ compose-image/Dockerfile (+44 -0)
➕ compose-image/Dockerfile.dockerignore (+7 -0)
➕ compose-image/containerd.toml (+11 -0)
➕ compose-image/daemon.json (+10 -0)
➕ compose-image/init (+32 -0)
➕ compose-image/test_e2e.py (+201 -0)
📝 deploy/docker/Dockerfile.agentenv (+7 -0)
📝 docs/src/configuration/reference.md (+9 -0)
📝 scripts/install.sh (+3 -0)
📝 services/Makefile (+6 -0)
📝 services/README.md (+7 -2)
➕ services/compose/cmd/main.go (+46 -0)
➕ services/compose/plan.go (+451 -0)
➕ services/compose/plan_test.go (+271 -0)
➕ services/gateway/internal/compose_test.go (+36 -0)
📝 services/gateway/internal/metrics.go (+1 -1)
📝 services/gateway/internal/schedule_hint.go (+1 -1)
📝 services/gateway/internal/server.go (+13 -2)
📝 services/go.mod (+11 -0)

...and 14 more files

📄 Description

What

Add POST /sandboxes-compose to run image-only Compose services in one sandbox, with a separate writable drive per service.

Why

Provide Compose startup and preserve it through pause/resume, fork, snapshots and deletion.

Depends on planner #415 and guest runtime #416. This PR temporarily includes their commits; review the last commit only. Rebase after both dependencies merge. The CLI follows in #417; Compose builds remain in #348.

Scope and non-goals

API/schema, server and gateway integration, lifecycle startup/rollback, runtime configuration, release packaging and API E2E coverage.

Design and behavior changes

  • Limit planning to 30 seconds, resolve images and assign unique drive IDs/mount paths on the server. Planner exit code 65 maps to HTTP 400 and any other failure to 500. The configured tools.control_plane_port is passed to the planner as envdPort. Check the final startup plan, including image configuration, against 4 MiB before VM allocation.
  • Start services from the server through EnvdApi::run_with_stdin with a clean environment and one shared 300-second budget: wait for Docker with the plain driver, register each service image with plain-snapshotter, then run docker compose up --wait. Inputs travel on stdin; nothing is written to the guest disk. Running is published only after Compose readiness, and startup failures use existing lifecycle cleanup.
  • Route the endpoint through the gateway with resource hints, assignment recording and a minimum 600-second request timeout.

Compatibility and operations

Adds /sandboxes-compose, compose.base_image and compose.planner_binary. Requires the Linux amd64/KVM guest image from #416 and the packaged planner; runs on the default AgentENV guest kernel without a [kernel].image_path override. Service image drives use shared read-only ublk devices, so [pool.block] enabled (the default) is required. Request timeouts are not a total startup deadline; creation can finish after a disconnected request.

Validation

On the designated Ubuntu test host as a non-root user with isolated state, on this revision:

  • make fmt
  • make clippy
  • cargo test -p agentenv --lib compose / api::impls::sandbox / module_cycles — 3 / 26 / 4 passed.
  • make -C services build test fmt-check vet — passed; the three RedisBindingStore integration tests were skipped because redis-server is not installed.
  • make agentenv-server — the only schema-driven change is the NewComposeSandbox.compose description. Regeneration with the current stable rustfmt also rewraps one unrelated, pre-existing line in the Node model; that drift is not included.

Carried over from the previous revision (not re-run after the planner protocol change):

  • make test-unit — agentenv 1020 passed plus 5 capability tests; aenv 115; linux-cap 3; uvm-ublk 18; uvm-ublk-daemon 49. Run on 845614f.
  • Server, CLI and ublk daemon builds.
  • python3 compose-image/test_e2e.py -v — all 3 API/lifecycle cases passed in 87.2 s with the #416 image on the default guest kernel (vmlinux-6.1.175). Its rejection case now uses env_file because absolute guest bind mounts are allowed.

Risks and reviewer notes

Lifecycle changes are concentrated in this commit. Review drive ownership, readiness publication and rollback; API E2E coverage exercises the full lifecycle. Guest daemons are not restarted; a daemon crash after startup is not reported and the sandbox stays Running.

Checklist

  • One integration change with focused tests.
  • Schema and generated code updated together.
  • Logs and examples contain no credentials or private settings.

🔄 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/349 **Author:** [@Primary33](https://github.com/Primary33) **Created:** 10/9/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feat/compose-runtime` --- ### 📝 Commits (3) - [`f5d5f49`](https://github.com/kvcache-ai/AgentENV/commit/f5d5f491d6762a1aed8597f5f5c2d7b5cfce99fb) feat(compose): add the Compose planner - [`87a326e`](https://github.com/kvcache-ai/AgentENV/commit/87a326ecf14b29a823a6c8ac2039bd9c67cc1533) feat(compose): add the guest Compose runtime image - [`b4010e5`](https://github.com/kvcache-ai/AgentENV/commit/b4010e5359318799226383b64e522945a3478576) feat(compose): run Compose projects in isolated sandboxes ### 📊 Changes **34 files changed** (+2478 additions, -24 deletions) <details> <summary>View changed files</summary> 📝 `.github/workflows/release.yml` (+10 -0) ➕ `compose-image/Dockerfile` (+44 -0) ➕ `compose-image/Dockerfile.dockerignore` (+7 -0) ➕ `compose-image/containerd.toml` (+11 -0) ➕ `compose-image/daemon.json` (+10 -0) ➕ `compose-image/init` (+32 -0) ➕ `compose-image/test_e2e.py` (+201 -0) 📝 `deploy/docker/Dockerfile.agentenv` (+7 -0) 📝 `docs/src/configuration/reference.md` (+9 -0) 📝 `scripts/install.sh` (+3 -0) 📝 `services/Makefile` (+6 -0) 📝 `services/README.md` (+7 -2) ➕ `services/compose/cmd/main.go` (+46 -0) ➕ `services/compose/plan.go` (+451 -0) ➕ `services/compose/plan_test.go` (+271 -0) ➕ `services/gateway/internal/compose_test.go` (+36 -0) 📝 `services/gateway/internal/metrics.go` (+1 -1) 📝 `services/gateway/internal/schedule_hint.go` (+1 -1) 📝 `services/gateway/internal/server.go` (+13 -2) 📝 `services/go.mod` (+11 -0) _...and 14 more files_ </details> ### 📄 Description ## What Add `POST /sandboxes-compose` to run image-only Compose services in one sandbox, with a separate writable drive per service. ## Why Provide Compose startup and preserve it through pause/resume, fork, snapshots and deletion. ## Related issue Depends on planner #415 and guest runtime #416. This PR temporarily includes their commits; [review the last commit only](https://github.com/Primary33/AgentENV/compare/87a326e...b4010e5). Rebase after both dependencies merge. The CLI follows in #417; Compose builds remain in #348. ## Scope and non-goals API/schema, server and gateway integration, lifecycle startup/rollback, runtime configuration, release packaging and API E2E coverage. ## Design and behavior changes - Limit planning to 30 seconds, resolve images and assign unique drive IDs/mount paths on the server. Planner exit code 65 maps to HTTP 400 and any other failure to 500. The configured `tools.control_plane_port` is passed to the planner as `envdPort`. Check the final startup plan, including image configuration, against 4 MiB before VM allocation. - Start services from the server through `EnvdApi::run_with_stdin` with a clean environment and one shared 300-second budget: wait for Docker with the `plain` driver, register each service image with `plain-snapshotter`, then run `docker compose up --wait`. Inputs travel on stdin; nothing is written to the guest disk. Running is published only after Compose readiness, and startup failures use existing lifecycle cleanup. - Route the endpoint through the gateway with resource hints, assignment recording and a minimum 600-second request timeout. ## Compatibility and operations Adds `/sandboxes-compose`, `compose.base_image` and `compose.planner_binary`. Requires the Linux amd64/KVM guest image from #416 and the packaged planner; runs on the default AgentENV guest kernel without a `[kernel].image_path` override. Service image drives use shared read-only ublk devices, so `[pool.block] enabled` (the default) is required. Request timeouts are not a total startup deadline; creation can finish after a disconnected request. ## Validation On the designated Ubuntu test host as a non-root user with isolated state, on this revision: - [x] `make fmt` - [x] `make clippy` - [x] `cargo test -p agentenv --lib compose` / `api::impls::sandbox` / `module_cycles` — 3 / 26 / 4 passed. - [x] `make -C services build test fmt-check vet` — passed; the three `RedisBindingStore` integration tests were skipped because `redis-server` is not installed. - [x] `make agentenv-server` — the only schema-driven change is the `NewComposeSandbox.compose` description. Regeneration with the current stable rustfmt also rewraps one unrelated, pre-existing line in the `Node` model; that drift is not included. Carried over from the previous revision (not re-run after the planner protocol change): - [x] `make test-unit` — agentenv 1020 passed plus 5 capability tests; aenv 115; linux-cap 3; uvm-ublk 18; uvm-ublk-daemon 49. Run on 845614f. - [x] Server, CLI and ublk daemon builds. - [x] `python3 compose-image/test_e2e.py -v` — all 3 API/lifecycle cases passed in 87.2 s with the #416 image on the default guest kernel (`vmlinux-6.1.175`). Its rejection case now uses `env_file` because absolute guest bind mounts are allowed. ## Risks and reviewer notes Lifecycle changes are concentrated in this commit. Review drive ownership, readiness publication and rollback; API E2E coverage exercises the full lifecycle. Guest daemons are not restarted; a daemon crash after startup is not reported and the sandbox stays Running. ## Checklist - [x] One integration change with focused tests. - [x] Schema and generated code updated together. - [x] Logs and examples contain no credentials or private settings. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
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#368
No description provided.