Container images for the Cella sandbox platform.
- sandbox-base: shared base image with OS packages, Go, Go tools, Node.js, Python, and a non-root
agentuserghcr.io/latere-ai/sandbox-base - sandbox-gui: base + GUI/VNC stack for computer-use workflows
ghcr.io/latere-ai/sandbox-gui - sandbox-harness: base + agent CLIs (Claude Code, Codex) for agent workloads
ghcr.io/latere-ai/sandbox-harness
The image inventory lives in catalog.yaml; the CI build matrix, the Makefile targets, test.sh, and the published catalog all derive from it. To add an image: create a context directory with a Dockerfile, add one entry to catalog.yaml, done. catalog.yaml documents every field inline; ./catalog.sh lint validates it (and bash catalog_test.sh tests the tooling itself).
The base image (Ubuntu 24.04, multi-arch amd64/arm64) provides:
- OS: Ubuntu 24.04 with
build-essential,git,curl,wget,vim,jq,ripgrep,openssh-client - Go: pinned via the
GO_VERSIONARG inbase/Dockerfile, plus tooling (gopls, goimports, delve, golangci-lint, staticcheck, gosec, and more) - Node.js: 22 LTS
- Python: 3 with pip and venv
- Non-root user:
agent(UID 1000), passwordless sudo
Image-specific additions:
- sandbox-gui
- Xvfb display, XFCE desktop session, x11vnc, websockify/noVNC, xdotool, ImageMagick, and Chrome for Testing
- Exposes noVNC on port
6080and defaults toSCREEN_GEOMETRY=1280x800x24
- sandbox-harness
- Claude Code and Codex CLIs, installed system-wide (versions pinned via ARGs in
harness/Dockerfile) - Pre-created
~/.claudeand~/.codexso a single credential file can be bind-mounted
- Claude Code and Codex CLIs, installed system-wide (versions pinned via ARGs in
Pre-built images are published to GHCR on every release:
# Pull the base image
podman pull ghcr.io/latere-ai/sandbox-base:latest
# Pull a specific version
podman pull ghcr.io/latere-ai/sandbox-base:v0.0.1
# Pull the GUI variant
podman pull ghcr.io/latere-ai/sandbox-gui:latestReplace podman with docker if using Docker.
git clone https://github.com/latere-ai/images.git
cd images
make # Build all images in catalog order
make base # Build base image only
make gui # Build GUI/VNC sandbox (builds base first)
make clean # Remove all imagesTargets are the context directories from catalog.yaml. Building requires yq and jq. Override the container runtime (default: podman):
make RUNTIME=dockerBuilt images are tagged as both sandbox-base:latest (local) and ghcr.io/latere-ai/sandbox-base:latest (registry name).
You can run these images directly. Mount a workspace directory into the container under /workspace.
docker run --rm -it \
-v "$(pwd)":/workspace/myproject \
-w /workspace/myproject \
ghcr.io/latere-ai/sandbox-base:latest \
bashThe container runs as the non-root agent user with /home/agent as $HOME. Install any agent CLI you need inside the sandbox (e.g. npm install -g <cli>).
sandbox-gui is an image for computer-use workflows. It starts an Xvfb display, a lightweight XFCE desktop, x11vnc, and a noVNC websocket bridge.
The GUI image is published for linux/amd64 because Chrome for Testing does not ship a Linux arm64 archive. On arm64 hosts, run it with --platform linux/amd64.
Run it locally and open noVNC:
docker run --rm -it \
--platform linux/amd64 \
-p 6080:6080 \
-v "$(pwd)":/workspace/myproject \
-w /workspace/myproject \
ghcr.io/latere-ai/sandbox-gui:latestThen visit http://localhost:6080/vnc.html. The entrypoint creates ~/.vncpass (mode 0600) on first boot. Read the generated password with docker exec <container> cat ~/.vncpass; it is never written to the container logs. Set VNC_PASSWORD or replace that file when orchestration owns attach credentials.
Launch Chromium directly inside the display:
docker run --rm -it \
--platform linux/amd64 \
-p 6080:6080 \
ghcr.io/latere-ai/sandbox-gui:latest \
chromium-launch https://example.com- Replace
dockerwithpodmanif preferred. - Mount additional project directories as needed under
/workspace/. - To limit resources:
--cpus 2 --memory 4g.
Releases are tag-driven through the central latere-ai/ci images-release pipeline (the same reusable-pipeline approach every latere.ai project uses; this repo keeps only the thin caller in release.yml). Pushing a vX.Y.Z tag builds and pushes every cataloged image in dependency order, publishes the catalog, smokes the published images with test.sh, and publishes a GitHub release whose notes carry the digest table and the full smoke output as evidence. Branch pushes run ci.yml instead: catalog lint, tooling tests, and smoke builds of changed images, nothing pushed.
The pipeline composes catalog.json from catalog.yaml plus the built image digests and uploads it to S3-compatible object storage:
${PREFIX}/catalog.json: the current catalog, overwritten each release${PREFIX}/history/<tag>.json: an immutable copy per release
Consumers use this to discover published images. The contract (top-level version: 1, bumped on breaking changes):
{
"version": 1,
"source": { "repo": "latere-ai/images", "commit": "<sha>", "tag": "v0.0.13" },
"images": [
{
"name": "sandbox-gui",
"ref": "ghcr.io/latere-ai/sandbox-gui:v0.0.13",
"digest": "sha256:...",
"platforms": ["linux/amd64"],
"label": "GUI",
"description": "...",
"defaults": { "cpu_milli": 500, "memory_mb": 1024, "width": 1280, "height": 800 }
}
]
}ref is always the immutable release tag, never latest. defaults is optional advisory resource hints.
Publication is configured through repository secrets: CATALOG_S3_ENDPOINT, CATALOG_S3_REGION, CATALOG_S3_BUCKET, CATALOG_S3_PREFIX, CATALOG_S3_ACCESS_KEY, CATALOG_S3_SECRET_KEY. A release fails if they are unset.
These details are relevant if you are building custom images on top of the sandboxes or integrating them into your own orchestration.
- Working directory:
/workspace(workspaces are mounted as subdirectories) - User: non-root
agent(UID 1000), passwordless sudo,$HOME=/home/agent - Prompt: the shell prompt prefers the
CELLA_HOSTenv var (the runtime-injected instance name) and falls back to the kernel hostname when unset - GUI mode:
sandbox-guistarts its own XFCE desktop onDISPLAY=:0and serves noVNC from port6080; overrideSCREEN_GEOMETRYto change the boot-time display size