[PR #416] feat(compose): add the guest Compose runtime image #420

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

📋 Pull Request Information

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

Base: main ← Head: feat/compose-guest-runtime


📝 Commits (1)

  • bc95e36 feat(compose): add the guest Compose runtime image

📊 Changes

5 files changed (+104 additions, -0 deletions)

View changed files

➕ 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)

📄 Description

What

Add compose-image/Dockerfile, the guest base image for Compose sandboxes.

Why

Run Docker Compose services on separately mounted service root filesystems inside a sandbox.

Guest-image part of the #349 split. Independent of planner #415; #349 integrates both and is the only consumer of this image.

Scope and non-goals

Only compose-image/ (Dockerfile, init, containerd.toml, daemon.json, Dockerfile.dockerignore); no Rust or Go code. The server drives startup through envd in #349. Publishing the image is out of scope: operators build it and set compose.base_image.

Design and behavior changes

  • The AgentENV tools drive executes /init as PID 1. /init re-executes under tini (docker-init), then starts plain-snapshotter, containerd and dockerd in order, waiting for each socket. Daemon logs go to /var/log/agentenv-compose/.
  • Daemons are never restarted: the snapshotter keeps active snapshots in memory, and VM snapshots preserve the running processes. /init stays alive if a daemon fails, so the guest does not panic; the server reports readiness failures.
  • /init exits unless it runs as tini's child, so a second run (for example as an image start command) cannot replace the live snapshotter socket.
  • Contract with the server (#349): /usr/bin/env, /usr/local/bin/docker, /usr/local/bin/plain-snapshotter, and Docker's plain storage driver.

Compatibility and operations

  • Public API or generated protocol: N/A.
  • Configuration or defaults: N/A; #349 adds compose.base_image.
  • Snapshot manifest, artifact layout, or storage format: N/A.
  • Upgrade and rollback: new image only; nothing references it until #349.
  • Host requirements, permissions, ports, or dependencies: build from the repository root with docker build -f compose-image/Dockerfile .; Dockerfile.dockerignore limits the context to these files and the snapshotter sources; the build downloads Debian packages, Docker static binaries and the Compose plugin (both pinned by SHA-256). linux/amd64 only; runs on the default AgentENV guest kernel. Docker's raw-table direct-access filter is disabled via allow-direct-routing: sandbox ingress always targets the guest address, so the filter adds no isolation and would otherwise require CONFIG_IP_NF_RAW in the guest kernel.

Validation

On the designated Ubuntu test host. Its Docker daemon runs with --bridge none, so the build uses --network=host and container checks use --network=none.

  • docker build --network=host -f compose-image/Dockerfile . — passed (40.0 kB build context).
  • Image contents: sh -n /init passes; all binaries the server and /init use are present; /init, /etc/containerd/config.toml and /etc/docker/daemon.json match the repository files byte for byte; Docker 28.4.0, Compose v2.39.4, iptables-legacy.
  • Guard: running /init outside tini's child prints agentenv-compose: /init must run as the guest init and exits 1.
  • python3 compose-image/test_e2e.py -v (integrated in #349) — 3 passed in 87.2 s on the default guest kernel (vmlinux-6.1.175). Guest logs show the three daemons started in order and storage-driver=plain.

Skipped checks and reasons: Rust and Go checks do not apply; this PR changes no Rust or Go code.

Risks and reviewer notes

A daemon crash after startup is not reported; the sandbox stays Running with Docker unavailable. Restarting is unsafe because the snapshotter's active snapshots are in memory.

Checklist

  • One coherent image change.
  • 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/416 **Author:** [@Primary33](https://github.com/Primary33) **Created:** 10/22/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feat/compose-guest-runtime` --- ### 📝 Commits (1) - [`bc95e36`](https://github.com/kvcache-ai/AgentENV/commit/bc95e369ba74052ffe1f3f6d8f918202293f9bca) feat(compose): add the guest Compose runtime image ### 📊 Changes **5 files changed** (+104 additions, -0 deletions) <details> <summary>View changed files</summary> ➕ `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) </details> ### 📄 Description ## What Add `compose-image/Dockerfile`, the guest base image for Compose sandboxes. ## Why Run Docker Compose services on separately mounted service root filesystems inside a sandbox. ## Related issue Guest-image part of the #349 split. Independent of planner #415; #349 integrates both and is the only consumer of this image. ## Scope and non-goals Only `compose-image/` (`Dockerfile`, `init`, `containerd.toml`, `daemon.json`, `Dockerfile.dockerignore`); no Rust or Go code. The server drives startup through envd in #349. Publishing the image is out of scope: operators build it and set `compose.base_image`. ## Design and behavior changes - The AgentENV tools drive executes `/init` as PID 1. `/init` re-executes under tini (`docker-init`), then starts plain-snapshotter, containerd and dockerd in order, waiting for each socket. Daemon logs go to `/var/log/agentenv-compose/`. - Daemons are never restarted: the snapshotter keeps active snapshots in memory, and VM snapshots preserve the running processes. `/init` stays alive if a daemon fails, so the guest does not panic; the server reports readiness failures. - `/init` exits unless it runs as tini's child, so a second run (for example as an image start command) cannot replace the live snapshotter socket. - Contract with the server (#349): `/usr/bin/env`, `/usr/local/bin/docker`, `/usr/local/bin/plain-snapshotter`, and Docker's `plain` storage driver. ## Compatibility and operations - Public API or generated protocol: N/A. - Configuration or defaults: N/A; #349 adds `compose.base_image`. - Snapshot manifest, artifact layout, or storage format: N/A. - Upgrade and rollback: new image only; nothing references it until #349. - Host requirements, permissions, ports, or dependencies: build from the repository root with `docker build -f compose-image/Dockerfile .`; `Dockerfile.dockerignore` limits the context to these files and the snapshotter sources; the build downloads Debian packages, Docker static binaries and the Compose plugin (both pinned by SHA-256). linux/amd64 only; runs on the default AgentENV guest kernel. Docker's raw-table direct-access filter is disabled via `allow-direct-routing`: sandbox ingress always targets the guest address, so the filter adds no isolation and would otherwise require `CONFIG_IP_NF_RAW` in the guest kernel. ## Validation On the designated Ubuntu test host. Its Docker daemon runs with `--bridge none`, so the build uses `--network=host` and container checks use `--network=none`. - [x] `docker build --network=host -f compose-image/Dockerfile .` — passed (40.0 kB build context). - [x] Image contents: `sh -n /init` passes; all binaries the server and `/init` use are present; `/init`, `/etc/containerd/config.toml` and `/etc/docker/daemon.json` match the repository files byte for byte; Docker 28.4.0, Compose v2.39.4, iptables-legacy. - [x] Guard: running `/init` outside tini's child prints `agentenv-compose: /init must run as the guest init` and exits 1. - [x] `python3 compose-image/test_e2e.py -v` (integrated in #349) — 3 passed in 87.2 s on the default guest kernel (`vmlinux-6.1.175`). Guest logs show the three daemons started in order and `storage-driver=plain`. Skipped checks and reasons: Rust and Go checks do not apply; this PR changes no Rust or Go code. ## Risks and reviewer notes A daemon crash after startup is not reported; the sandbox stays Running with Docker unavailable. Restarting is unsafe because the snapshotter's active snapshots are in memory. ## Checklist - [x] One coherent image change. - [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#420
No description provided.