mirror of
https://github.com/wirenboard/agent-vm.git
synced 2026-07-12 09:21:39 +00:00
rename ghcr image to agent-vm-template (mirror from rewrite-microsandbox)
This commit is contained in:
parent
c87d9bfb99
commit
ca6a2a3d2b
2 changed files with 198 additions and 1 deletions
2
.github/workflows/build-image.yml
vendored
2
.github/workflows/build-image.yml
vendored
|
|
@ -36,7 +36,7 @@ concurrency:
|
|||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
IMAGE: ghcr.io/${{ github.repository_owner }}/agent-vm
|
||||
IMAGE: ghcr.io/${{ github.repository_owner }}/agent-vm-template
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
|
|||
197
images/Dockerfile
Normal file
197
images/Dockerfile
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
# agent-vm GUEST TEMPLATE image — the OCI image that the agent-vm
|
||||
# tool boots *inside* its per-project microVM. NOT the agent-vm
|
||||
# tool itself (that's the Rust binary published as
|
||||
# @wirenboard/agent-vm on npm).
|
||||
#
|
||||
# Contents: Debian 13 slim + the three AI coding agents (Claude
|
||||
# Code, OpenCode, Codex) and the minimum dev tooling they need to be
|
||||
# useful. Heavier extras (Docker-in-VM, LSPs, additional MCP
|
||||
# servers) are deliberately deferred.
|
||||
#
|
||||
# Published to ghcr.io/wirenboard/agent-vm-template:latest by the
|
||||
# hourly CI workflow (.github/workflows/build-image.yml). Source-
|
||||
# checkout users can also build locally and push to a host-local
|
||||
# registry via images/build.sh.
|
||||
|
||||
FROM debian:13-slim
|
||||
|
||||
LABEL org.opencontainers.image.title="agent-vm guest template"
|
||||
LABEL org.opencontainers.image.description="Guest OCI image that the agent-vm tool (npm: @wirenboard/agent-vm) boots inside each per-project microVM. Contains Debian 13 + Claude Code + OpenCode + Codex + minimal dev tooling. Rebuilt hourly to pick up new agent releases. NOT the agent-vm runtime itself — install that via `npm install -g @wirenboard/agent-vm`."
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
HOME=/root \
|
||||
PATH=/root/.local/bin:/root/.claude/local/bin:/root/.opencode/bin:/usr/local/bin:/usr/bin:/bin
|
||||
|
||||
# Image-API contract version. Bump on BREAKING image changes only
|
||||
# (new required mount points, changed env-var contracts, removed
|
||||
# binaries, renamed in-VM paths). Routine agent-version refreshes
|
||||
# do NOT change this. See `crates/agent-vm/src/defaults.rs` for the
|
||||
# matching range in the binary.
|
||||
RUN echo 1 > /etc/agent-vm-image-version
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
curl wget \
|
||||
git \
|
||||
jq \
|
||||
bash \
|
||||
python3 python3-pip \
|
||||
ripgrep fd-find \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Chromium for the chrome-devtools MCP server (Phase 7). The MCP
|
||||
# launches it headless. Debian's chromium package is large (~400 MB)
|
||||
# but is the simplest source; users who don't want it can pass
|
||||
# AGENT_VM_NO_CHROME_MCP=1 to suppress the server entry in agent
|
||||
# settings.
|
||||
#
|
||||
# `sudo` + `libnss3-tools` are here because the chrome MCP runs as a
|
||||
# dedicated non-root `chrome` user (see the next RUN block). sudo is
|
||||
# how the wrapper drops into it; libnss3-tools (`certutil`) is how the
|
||||
# launcher injects the per-boot microsandbox CA into chrome's NSS DB.
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
chromium \
|
||||
fonts-liberation \
|
||||
sudo \
|
||||
libnss3-tools \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& ln -sf /usr/bin/chromium /usr/bin/google-chrome \
|
||||
&& ln -sf /usr/bin/chromium /usr/bin/google-chrome-stable \
|
||||
&& mkdir -p /opt/google/chrome \
|
||||
&& ln -sf /usr/bin/chromium /opt/google/chrome/chrome
|
||||
|
||||
# Dedicated `chrome` user so chromium's user-namespace sandbox can
|
||||
# initialize. As root chromium refuses to set up its sandbox and the
|
||||
# CDP target closes immediately (`Protocol error
|
||||
# (Target.setDiscoverTargets): Target closed`); the alternative is
|
||||
# `--no-sandbox`, which we'd rather not pass — the microVM is the
|
||||
# outer boundary, but keeping chromium's nested sandbox active is a
|
||||
# real defence-in-depth gain when the agent makes the browser load
|
||||
# untrusted content.
|
||||
#
|
||||
# /etc/passwd is edited by hand because debian-slim doesn't ship
|
||||
# `useradd`. Empty NSS DB is created at image time so the per-launch
|
||||
# `certutil -A` only has to add the CA, not also format the DB.
|
||||
# Sudoers rule keeps `root -> chrome` password-less; no other
|
||||
# (chrome -> root) transition is allowed.
|
||||
#
|
||||
# The wrapper script is what the in-image Chrome MCP entry actually
|
||||
# launches: it re-execs `npx chrome-devtools-mcp@latest` under the
|
||||
# `chrome` user with stdin/stdout preserved so the MCP JSON-RPC pipe
|
||||
# from claude still works.
|
||||
RUN echo 'chrome:x:9999:9999:Chrome MCP:/home/chrome:/bin/bash' >> /etc/passwd \
|
||||
&& echo 'chrome:!:19000:0:99999:7:::' >> /etc/shadow \
|
||||
&& echo 'chrome:x:9999:' >> /etc/group \
|
||||
&& mkdir -p /home/chrome/.pki/nssdb \
|
||||
&& certutil -d sql:/home/chrome/.pki/nssdb -N --empty-password \
|
||||
&& chown -R 9999:9999 /home/chrome \
|
||||
&& echo 'root ALL=(chrome) NOPASSWD: ALL' \
|
||||
> /etc/sudoers.d/agent-vm-chrome \
|
||||
&& chmod 0440 /etc/sudoers.d/agent-vm-chrome \
|
||||
&& printf '%s\n' \
|
||||
'#!/bin/bash' \
|
||||
'# agent-vm chrome MCP wrapper.' \
|
||||
'# Re-exec the MCP server under the `chrome` user so chromium'\''s' \
|
||||
'# user-namespace sandbox initializes (it refuses to as root).' \
|
||||
'# Args (npx + chrome-devtools-mcp + flags) come from the MCP' \
|
||||
'# config in ~/.claude.json; we just switch UID.' \
|
||||
'#' \
|
||||
'# Preserve list (passed through sudo'\''s env_reset):' \
|
||||
'# - NODE_EXTRA_CA_CERTS / SSL_CERT_FILE / CURL_CA_BUNDLE /' \
|
||||
'# REQUESTS_CA_BUNDLE: agentd boot-sets these so any HTTPS' \
|
||||
'# client trusts the microsandbox MITM CA. Without them' \
|
||||
'# node fails `npx -y` with SELF_SIGNED_CERT_IN_CHAIN.' \
|
||||
'# - PATH: needed so npx is findable (sudo'\''s secure_path' \
|
||||
'# would otherwise replace it).' \
|
||||
'# - HTTP_PROXY / HTTPS_PROXY / NO_PROXY (upper/lowercase):' \
|
||||
'# NOT set by agentd (microsandbox uses transparent network' \
|
||||
'# interception, no HTTP proxy env). Forwarded so that a' \
|
||||
'# user-supplied MCP `env:` block or .agent-vm.runtime.sh' \
|
||||
'# setting these is honoured by chromium.' \
|
||||
'# - CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS / CI / DEBUG:' \
|
||||
'# user/MCP-config opt-outs and debugging knobs that' \
|
||||
'# chrome-devtools-mcp documents.' \
|
||||
'# - TZ / LANG / LC_ALL / TMPDIR: locale + sane temp paths.' \
|
||||
'#' \
|
||||
'# NOT in the preserve list (deliberate):' \
|
||||
'# - USER / LOGNAME: sudo'\''s default `env_reset` policy re-' \
|
||||
'# derives these from the target passwd entry. Adding them' \
|
||||
'# here would forward root'\''s USER into the chrome session.' \
|
||||
'# - HOME: set by `-H` from chrome'\''s passwd entry (not by' \
|
||||
'# env passthrough).' \
|
||||
'#' \
|
||||
'# `cd /home/chrome` keeps chromium'\''s default CWD writable' \
|
||||
'# (the agent runs as root in /workspace, where chrome (UID' \
|
||||
'# 9999) cannot write; tools that emit relative paths' \
|
||||
'# (./trace.json, ./screenshot.png) would otherwise EACCES).' \
|
||||
'# Diagnostic on failure so Claude doesn'\''t just see the MCP' \
|
||||
'# transport close before handshake with no breadcrumb.' \
|
||||
'set -e' \
|
||||
'cd /home/chrome 2>/dev/null || {' \
|
||||
' echo "agent-vm-chrome-mcp: cannot cd to /home/chrome -- image misconfigured?" >&2' \
|
||||
' exit 1' \
|
||||
'}' \
|
||||
'exec sudo -u chrome -H -n \' \
|
||||
' --preserve-env=NODE_EXTRA_CA_CERTS,SSL_CERT_FILE,CURL_CA_BUNDLE,REQUESTS_CA_BUNDLE \' \
|
||||
' --preserve-env=PATH,HTTP_PROXY,HTTPS_PROXY,NO_PROXY,http_proxy,https_proxy,no_proxy \' \
|
||||
' --preserve-env=CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS,CI,DEBUG \' \
|
||||
' --preserve-env=TZ,LANG,LC_ALL,TMPDIR \' \
|
||||
' -- "$@"' \
|
||||
> /usr/local/bin/agent-vm-chrome-mcp \
|
||||
&& chmod 0755 /usr/local/bin/agent-vm-chrome-mcp
|
||||
|
||||
# GitHub CLI from the official apt repo (Phase 6).
|
||||
RUN install -dm 755 /etc/apt/keyrings \
|
||||
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
||||
> /etc/apt/keyrings/githubcli-archive-keyring.gpg \
|
||||
&& chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
|
||||
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
|
||||
> /etc/apt/sources.list.d/github-cli.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends gh \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Node.js 22 from NodeSource — needed for Claude Code, OpenCode, MCP servers.
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
||||
&& apt-get install -y --no-install-recommends nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Pre-warm chrome-devtools-mcp's npm cache as the chrome user so the
|
||||
# first browser tool call in any sandbox doesn't pay the multi-second
|
||||
# `npx -y` fetch cost. `--help` exits cleanly after install/version-
|
||||
# check; it's the cheapest exercise of the full install path.
|
||||
#
|
||||
# Pinned to a known-good version. The runtime MCP config in
|
||||
# `crates/agent-vm/src/secrets.rs::write_default_claude_root_state`
|
||||
# pins the SAME version — bump both together. Without the pin the
|
||||
# cache stops being useful the first time upstream cuts a release:
|
||||
# `npx -y` re-resolves `@latest` against the registry and re-
|
||||
# downloads under the writable upper layer.
|
||||
#
|
||||
# Best-effort: `|| true` because npm-registry transient failure or a
|
||||
# bad `--help` exit code shouldn't fail the whole `agent-vm setup`.
|
||||
# A missing cache costs a multi-second fetch on first browser tool
|
||||
# call; failing the image build is much worse.
|
||||
#
|
||||
# Must come AFTER the nodejs RUN above — npx didn't exist before it.
|
||||
RUN sudo -u chrome -H npx -y chrome-devtools-mcp@1.0.1 --help >/dev/null 2>&1 \
|
||||
|| echo "==> pre-warm skipped (will lazy-fetch chrome-devtools-mcp at first MCP call)"
|
||||
|
||||
# Claude Code official installer.
|
||||
RUN curl -fsSL https://claude.ai/install.sh | bash
|
||||
|
||||
# OpenCode official installer. Installs into ~/.opencode/bin; PATH already
|
||||
# includes it. Symlink into /usr/local/bin so non-login shells find it too.
|
||||
RUN curl -fsSL https://opencode.ai/install | bash \
|
||||
&& ln -sf /root/.opencode/bin/opencode /usr/local/bin/opencode
|
||||
|
||||
# Codex CLI official installer.
|
||||
RUN curl -fsSL https://github.com/openai/codex/releases/latest/download/install.sh | sh
|
||||
|
||||
# Sanity check at build time so a broken installer surfaces before we push.
|
||||
RUN claude --version && opencode --version && codex --version
|
||||
|
||||
# Smoke entrypoint; the launcher overrides this in Phase 2.
|
||||
CMD ["/bin/bash"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue