-
Notifications
You must be signed in to change notification settings - Fork 555
chore(agent): docker cleanups for the sandbox-agent sidecar #4762
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # Agent runner sidecar (sandbox-agent server), production image. | ||
| # | ||
| # Runs the TypeScript runner (src/server.ts) as a long-lived HTTP server on :8765. | ||
| # The Python agent service calls it in-network. Unlike Dockerfile.dev there is no | ||
| # `tsx watch` and no bind mount: the source is baked in. | ||
| # | ||
| # Licensing posture (see docker/README.md): | ||
| # - Pi (@earendil-works/pi-coding-agent, MIT) is baked via the npm dependencies. | ||
| # - Claude Code is proprietary (Anthropic Commercial Terms). It is NEVER baked into | ||
| # this image. The sandbox-agent daemon installs it at runtime from Anthropic over | ||
| # HTTPS (the reason ca-certificates is installed). That keeps Anthropic as the | ||
| # distributor, the only compliant path for an image we build and ship. | ||
| # - No credential is baked: no API key, no OAuth login. Auth is injected at runtime | ||
| # (ANTHROPIC_API_KEY / request secrets; OAuth self-host is a mounted opt-in only). | ||
|
|
||
| FROM node:24-slim | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # CA certificates: the sandbox-agent daemon (Rust) downloads harness CLIs (e.g. Claude | ||
| # Code) over HTTPS using the system trust store, which node:*-slim omits — without this | ||
| # the daemon's `install-agent claude` fails TLS verification. git lets npm/installers | ||
| # fetch git deps. | ||
| RUN apt-get update \ | ||
| && apt-get install -y --no-install-recommends ca-certificates git \ | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Load-bearing: ca-certificates is here because the daemon fetches Claude from Anthropic over TLS at runtime. node:*-slim omits the trust store, so without this |
||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| RUN corepack enable | ||
|
|
||
| # Install deps as a cached layer (manifest + lockfile only). The full dependency set is | ||
| # installed (not --prod): the runtime uses `tsx` and the extension build uses `esbuild`, | ||
| # both devDependencies. | ||
| COPY package.json pnpm-lock.yaml ./ | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Full dependency set is installed (not --prod) on purpose: the runtime uses tsx and the extension build uses esbuild, both devDependencies. Worth confirming this stays intentional if anyone later tries to slim the image. |
||
| RUN pnpm install --frozen-lockfile | ||
|
|
||
| # Bake the source (no bind mount in production). | ||
| COPY tsconfig.json ./ | ||
| COPY scripts ./scripts | ||
| COPY src ./src | ||
| COPY config ./config | ||
| COPY skills ./skills | ||
|
|
||
| # Bundle the Agenta Pi extension (tracing + tools) into dist/. runSandboxAgent installs | ||
| # this baked copy into Pi's agent dir on every run. Rebuild the image after editing | ||
| # src/extensions/agenta.ts or the tracer. | ||
| RUN pnpm run build:extension | ||
|
|
||
| ENV NODE_ENV=production \ | ||
| PORT=8765 | ||
|
|
||
| EXPOSE 8765 | ||
|
|
||
| # Call the local tsx binary directly to avoid pnpm/corepack HOME writes when the | ||
| # container runs as a non-root host uid. | ||
| CMD ["node_modules/.bin/tsx", "src/server.ts"] | ||
|
Comment on lines
+16
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Run the final container as a non-root user. The image still launches the HTTP server as root. For a network-facing agent runner, that widens the blast radius of any compromise. 🔧 Suggested fix RUN pnpm run build:extension
ENV NODE_ENV=production \
PORT=8765
EXPOSE 8765
+USER node
CMD ["node_modules/.bin/tsx", "src/server.ts"]Source: Linters/SAST tools |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| # Agent sidecar images | ||
|
|
||
| Images for the agent runner sidecar (the `sandbox-agent server` runtime in | ||
| `services/agent/src/server.ts`). The Python service calls it in-network at | ||
| `:8765`. | ||
|
|
||
| - `Dockerfile.dev` — dev image. `tsx watch`, source bind-mounted, hot reload. | ||
| - `Dockerfile` — production image. Source baked in, no watcher. | ||
|
|
||
| ## Licensing posture (read before changing any image or build recipe) | ||
|
|
||
| The rule that shapes every image here: | ||
|
|
||
| > **We ship build recipes, not Claude-containing images, and we never bake a | ||
| > credential into any image.** | ||
|
|
||
| Why: | ||
|
|
||
| - **Pi** (`@earendil-works/pi-coding-agent`) is MIT. We bake it freely via the npm | ||
| dependencies, in every image and snapshot. | ||
| - **Claude Code** is proprietary (© Anthropic PBC, governed by Anthropic's | ||
| [Commercial Terms](https://www.anthropic.com/legal/commercial-terms); | ||
| [legal & compliance](https://code.claude.com/docs/en/legal-and-compliance)). The | ||
| Commercial Terms grant a usage license only. They do not grant any right to | ||
| redistribute, resell, sublicense, or repackage the Services. So an image **we | ||
| build and distribute must not contain Claude Code.** | ||
| - Claude Code is installed **from Anthropic** (`npm install -g | ||
| @anthropic-ai/claude-code`, `https://claude.ai/install.sh`, or the daemon's | ||
| `install-agent claude`). That keeps Anthropic as the distributor, which is the | ||
| permitted path. The production sidecar does this at runtime; a snapshot we build | ||
| for our own use does it at build time. | ||
|
|
||
| ## Authentication | ||
|
|
||
| Auth is injected at runtime, never baked into a layer. | ||
|
|
||
| - **API key (default, and the only option for cloud / multi-tenant).** Set | ||
| `ANTHROPIC_API_KEY` (or pass provider keys as request secrets from the vault). | ||
| Anthropic directs products and services that interact with Claude to use API key | ||
| auth, so this is the path for any Agenta-orchestrated run that serves users. | ||
| - **OAuth subscription (self-host opt-in only).** An individual operator may mount | ||
| their own Claude login (e.g. `~/.claude`) into the container and run with their | ||
| own subscription. This is for personal, individual use of Claude Code, never for | ||
| serving other users, and it is the operator's responsibility. Anthropic restricts | ||
| Free/Pro/Max OAuth to first-party use and forbids third parties routing requests | ||
| through it (enforced since 2026-03). Cloud and multi-tenant deployments must stay | ||
| API-key only. | ||
|
|
||
| We never bake an OAuth login or an API key into an image. | ||
|
|
||
| ## Build recipes (two paths) | ||
|
|
||
| - **Cloud / Daytona (API key).** The Daytona snapshot recipe bakes Pi. Agenta Cloud | ||
| builds and uses its own snapshot internally; self-hosters run the same recipe | ||
| against their own Daytona account. We ship the build script (the recipe), not the | ||
| built snapshot, so we never distribute a Claude-containing artifact. Snapshot | ||
| builder: `docs/design/agent-workflows/scratch/wp-8-rivet-acp-runtime/poc/build_rivet_snapshot.py`. | ||
| Today it bases on rivet's `-full` image, which already bundles Claude. That is | ||
| compliant under the recipe-not-image model. **Cleaner-provenance follow-up | ||
| (needs a live Daytona build to verify):** base on a daemon-only rivet image and | ||
| install Claude from Anthropic at build, so the snapshot's Claude comes straight | ||
| from Anthropic rather than from a third party's bundled image. Relocation of the | ||
| builder into this folder is a follow-up. | ||
| - **Self-host (API key, OAuth optional).** Build the production `Dockerfile` (it | ||
| bakes neither Claude nor a credential), then supply auth at runtime: an | ||
| `ANTHROPIC_API_KEY` env var, or, for individual use, a mounted OAuth login dir. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the subtle compliance case. The -full base bundles Claude, so the built snapshot contains Claude. It stays compliant only because we ship this script and each operator builds the snapshot in their own account; we distribute nothing. The cleaner-provenance follow-up (daemon-only base + install from Anthropic at build) is parked pending a live Daytona check that the daemon-only tag ships the ACP adapters.