Skip to content

[feat] All-harness self-hostable agent sidecar (Claude Code + Pi)#4880

Draft
mmabrouk wants to merge 1 commit into
big-agentsfrom
feat/agent-allharness-sidecar
Draft

[feat] All-harness self-hostable agent sidecar (Claude Code + Pi)#4880
mmabrouk wants to merge 1 commit into
big-agentsfrom
feat/agent-allharness-sidecar

Conversation

@mmabrouk

Copy link
Copy Markdown
Member

Context

We run a second agent-runner sidecar (agenta-claude-sub-sidecar) that authenticates Claude Code with a subscription OAuth (a read-only ~/.claude mount) instead of an API key. It is the same runner image as the main sandbox-agent, but started with the plain default entrypoint, as a non-root user, with no Pi provisioning. The result: it can host claude, but every Pi run crashes with EACCES: mkdir '/pi-agent/extensions'.

Why: the main runner only hosts Pi because the dev compose service overrides the image CMD — it runs as root, does mkdir -p /pi-agent + copies the Pi login + rebuilds the extension, then serves. A bare docker run of that image as a non-root user has no writable PI_CODING_AGENT_DIR, so the runner daemon cannot lay the Agenta Pi extension and fails. There was no single image a self-hoster could docker build + docker run that serves every harness.

This PR adds that image: a self-host recipe that bakes the Pi provisioning into the image (no compose CMD), keeps the ~/.claude OAuth mount working, and serves all three harnesses (pi_core, pi_agenta, claude).

What changed

  • services/agent/docker/Dockerfile.sidecar (new) — FROM the production runner image (reuses that build, does not fork it). As root it creates /pi-agent owned by the runtime user (node) and, behind ARG INSTALL_CLAUDE_CODE=true, installs Claude Code from Anthropic; then sets PI_CODING_AGENT_DIR=/pi-agent, a writable HOME=/home/node, AGENTA_AGENT_RUNNER_HOST=0.0.0.0, SANDBOX_AGENT_PROVIDER=local, drops to USER node, and serves.
  • services/agent/docker/sidecar-entrypoint.sh (new) — ensures PI_CODING_AGENT_DIR and seeds an optional read-only Pi login mounted at /pi-agent-ro (the same cp -a /pi-agent-ro/. the compose CMD does), then execs the server. The Pi extension bundle is already in dist/ from the runner image's build:extension, so there is no runtime rebuild.
  • Docsservices/agent/docker/README.md gains an "all-harness self-host sidecar" section (build + run + licensing boundary); the subscription-sidecar project README cross-links it as the productized form; the sidecar-deployment-proposal status logs it.

Provisioning vs the old compose CMD

Compose CMD (runtime, root) This image (baked)
mkdir -p /pi-agent as root /pi-agent created at build, owned by node (works non-root, no EACCES)
cp -a /pi-agent-ro/. (Pi login seed) entrypoint copies /pi-agent-ro if mounted
node scripts/build-extension.mjs (rebuild, src bind-mounted) bundle already baked in dist/ (src is baked, no rebuild)
exec tsx src/server.ts ENTRYPOINT + CMD serve as node

Scope / risk

  • Additive. New files only (one Dockerfile, one entrypoint, doc edits). The existing services/agent/docker/Dockerfile and Dockerfile.dev, the compose stack, and the live agenta-claude-sub-sidecar are untouched. Nothing wires this image into the app.
  • Licensing boundary (the thing to review). This is a build recipe, not an image Agenta publishes. With the default INSTALL_CLAUDE_CODE=true it installs Claude Code from Anthropic at build, so the self-hoster who runs docker build is the one pulling it for their own individual use. The resulting image must not be published/distributed by Agenta — that would make Agenta a Claude Code redistributor, which Anthropic's Commercial Terms forbid. --build-arg INSTALL_CLAUDE_CODE=false gives a redistribution-safe base that installs Claude at runtime instead. No credential is ever baked.
  • Local-only by design. No Daytona (Claude Code's IP is banned on Daytona). Subscription OAuth is individual-use only and must never serve other users; cloud/multi-tenant must stay API-key only. Host port bound to 127.0.0.1 (the runner trusts its caller with resolved secrets).
  • uid caveat: the runtime user is node (uid 1000); on a host whose uid is not 1000 the read-only ~/.claude mount may be unreadable — documented.

How to QA

Prerequisites: Docker; a Claude subscription OAuth at ~/.claude/.credentials.json (token prefix sk-ant-oat01-) on a uid-1000 host, OR an ANTHROPIC_API_KEY.

  1. Build the base runner image, then the sidecar:
    docker build -t agenta-sandbox-agent:latest -f services/agent/docker/Dockerfile services/agent
    docker build -t agenta-allharness-sidecar:test -f services/agent/docker/Dockerfile.sidecar services/agent
  2. Run an isolated test container (distinct name + loopback port, does not touch any live sidecar):
    docker run -d --name allharness-test -p 127.0.0.1:8791:8765 \
      -v "$HOME/.claude":/home/node/.claude:ro agenta-allharness-sidecar:test
    curl -s http://127.0.0.1:8791/health
    Expected: {"status":"ok",...,"harnesses":["pi_core","claude","pi_agenta"]}.
  3. Claude via subscription OAuth (no API key):
    curl -s -X POST http://127.0.0.1:8791/run -H 'content-type: application/json' \
      -d '{"harness":"claude","sandbox":"local","model":"haiku","credentialMode":"runtime_provided",
           "messages":[{"role":"user","content":"Reply with exactly: hi. Nothing else."}]}'
    Expected: {"ok":true,...}.
  4. Pi (key via resolved secrets), and confirm no EACCES:
    curl -s -X POST http://127.0.0.1:8791/run -H 'content-type: application/json' \
      -d '{"harness":"pi_core","sandbox":"local","model":"claude-haiku-4-5","provider":"anthropic",
           "credentialMode":"env","secrets":{"ANTHROPIC_API_KEY":"sk-ant-..."},
           "messages":[{"role":"user","content":"Reply with exactly: hi from pi. Nothing else."}]}'
    docker exec allharness-test ls -la /pi-agent/extensions   # agenta.js present, no EACCES
    Expected: {"ok":true,"output":"hi from pi",...}.
  5. Clean up: docker rm -f allharness-test && docker rmi agenta-allharness-sidecar:test.

Edge cases worth a look: --build-arg INSTALL_CLAUDE_CODE=false (Claude installs at runtime on first run, ~10s); seeding a Pi login via -v "$HOME/.pi/agent":/pi-agent-ro:ro.

Verified in isolation (this PR)

Built both images, ran a test container on :8791 with the ~/.claude mount (the live :8790 sidecar and the :8280 stack untouched). Both harnesses passed: a claude+haiku subscription-OAuth run returned ok:true in ~4.3s (no install delay -> baked Claude Code used), and a pi_core+claude-haiku-4-5 run returned ok:true with no /pi-agent EACCES and /pi-agent/extensions/agenta.js installed. Test container and images removed afterward.

https://claude.ai/code/session_01GYo3UEfvsZpncagqb28Mbc

@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jun 27, 2026 6:23pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a6ee61c8-e002-40c8-8d8f-1619d26900df

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agent-allharness-sidecar

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mmabrouk

Copy link
Copy Markdown
Member Author

Draft, do-not-merge. Code review needed on three specific things:

  1. The Dockerfile approach. Dockerfile.sidecar does FROM the production runner image (ARG RUNNER_IMAGE=agenta-sandbox-agent:latest) and only layers provisioning on top — it reuses the existing build rather than forking the build steps. Is that the seam you want, or would you rather it base on the dev image / be a single self-contained Dockerfile? It is a two-step build (build the runner image, then this).

  2. The licensing boundary. Claude Code is baked from Anthropic at build behind ARG INSTALL_CLAUDE_CODE, defaulting to true. The framing: this file is a self-host recipe, not an image Agenta publishes; the resulting image must never be redistributed; =false gives a redistribution-safe base that installs Claude at runtime. Please sanity-check that default + the boundary wording in the Dockerfile header and docker/README.md against our 'never bake/distribute Claude Code' rule. If you'd prefer the default be false, that's a one-line change.

  3. That it actually hosts both harnesses. Verified in isolation on a throwaway :8791 container (live :8790 sidecar + :8280 stack untouched): claude+haiku subscription-OAuth run -> ok:true (no install delay, baked Claude used); pi_core+claude-haiku-4-5 -> ok:true with no /pi-agent EACCES and the Pi extension installed. Exact repro commands are in the PR body.

Not a functionality/UX check — nothing is wired into the app. CodeRabbit will run once this is marked ready; left as draft per the task.

@mmabrouk mmabrouk added the needs-review Agent updated; awaiting Mahmoud's review label Jun 26, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dbcfa6ae-bb6a-402e-b72e-d3632583d372

📥 Commits

Reviewing files that changed from the base of the PR and between 329cfa0 and 97b95ef.

📒 Files selected for processing (5)
  • docs/design/agent-workflows/projects/sidecar-deployment-proposal/status.md
  • docs/design/agent-workflows/projects/subscription-sidecar/README.md
  • services/agent/docker/Dockerfile.sidecar
  • services/agent/docker/README.md
  • services/agent/docker/sidecar-entrypoint.sh

FROM ${RUNNER_IMAGE}

# Re-declare after FROM so the ARG is in scope for the RUN below.
ARG INSTALL_CLAUDE_CODE=true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Dockerfile.sidecar relevant lines ==\n'
sed -n '1,180p' services/agent/docker/Dockerfile.sidecar | nl -ba | sed -n '70,120p'

printf '\n== Search for INSTALL_CLAUDE_CODE usage ==\n'
rg -n "INSTALL_CLAUDE_CODE|Claude Code|redistribut|non-redistribut" services/agent/docker services/agent -S

printf '\n== Find related Dockerfiles / docs ==\n'
fd -a -t f "Dockerfile*" services/agent/docker

Repository: Agenta-AI/agenta

Length of output: 235


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Dockerfile.sidecar lines 70-120 ==\n'
awk 'NR>=70 && NR<=120 { printf "%4d  %s\n", NR, $0 }' services/agent/docker/Dockerfile.sidecar

printf '\n== Search for INSTALL_CLAUDE_CODE usage ==\n'
rg -n "INSTALL_CLAUDE_CODE|Claude Code|redistribut|non-redistribut" services/agent/docker services/agent -S

printf '\n== Dockerfiles under services/agent/docker ==\n'
fd -a -t f "Dockerfile*" services/agent/docker

Repository: Agenta-AI/agenta

Length of output: 12628


Make the Claude-baked image opt-in.

INSTALL_CLAUDE_CODE=true makes a plain build produce the non-redistributable variant. Flip the default to false and opt in explicitly when you need the baked image.


# Idempotent: /pi-agent is pre-created and owned by the runtime user at image build. This
# only matters if PI_CODING_AGENT_DIR was overridden to a fresh, writable path.
mkdir -p "$PI_DIR" 2>/dev/null || true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fail fast when PI_CODING_AGENT_DIR is not writable.

Silencing mkdir errors here lets the container boot and only fail later when the runner tries to install the Pi extension. If this directory cannot be created or written, startup should stop immediately.

Suggested change
-mkdir -p "$PI_DIR" 2>/dev/null || true
+mkdir -p "$PI_DIR"
+[ -w "$PI_DIR" ] || {
+    echo "PI_CODING_AGENT_DIR is not writable: $PI_DIR" >&2
+    exit 1
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mkdir -p "$PI_DIR" 2>/dev/null || true
mkdir -p "$PI_DIR"
[ -w "$PI_DIR" ] || {
echo "PI_CODING_AGENT_DIR is not writable: $PI_DIR" >&2
exit 1
}

Comment on lines +28 to +29
if [ -d /pi-agent-ro ]; then
cp -a /pi-agent-ro/. "$PI_DIR"/ 2>/dev/null || true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Seed once without overwriting existing Pi state.

cp -a will overwrite files on every restart. If $PI_DIR is persisted, a stale /pi-agent-ro seed can roll back newer login/session data and extension state. This should be a no-clobber seed, not a resync.

Suggested change
 if [ -d /pi-agent-ro ]; then
-    cp -a /pi-agent-ro/. "$PI_DIR"/ 2>/dev/null || true
+    cp -an /pi-agent-ro/. "$PI_DIR"/
 fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [ -d /pi-agent-ro ]; then
cp -a /pi-agent-ro/. "$PI_DIR"/ 2>/dev/null || true
if [ -d /pi-agent-ro ]; then
cp -an /pi-agent-ro/. "$PI_DIR"/
fi

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
services/agent/docker/README.md (1)

99-106: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Licensing boundary wording is careful, but the true default is a foot-gun.

The explanation correctly distinguishes recipe from published image and warns against redistribution. However, since INSTALL_CLAUDE_CODE=true is the default, a self-hoster who misses the warning builds a non-redistributable image. Consider adding a bold warning at the top of the build instructions (Line 108) or in the bullet on Line 9, or flipping the default to false as the author noted is a one-line change. The current docs are legally defensible; making the safe path the default would be more robust.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d0e875ff-9141-4d5f-9401-08dd8b0133a5

📥 Commits

Reviewing files that changed from the base of the PR and between 97b95ef and 2d4eb44.

📒 Files selected for processing (5)
  • docs/design/agent-workflows/projects/sidecar-deployment-proposal/status.md
  • docs/design/agent-workflows/projects/subscription-sidecar/README.md
  • services/agent/docker/Dockerfile.sidecar
  • services/agent/docker/README.md
  • services/agent/docker/sidecar-entrypoint.sh
✅ Files skipped from review due to trivial changes (3)
  • services/agent/docker/sidecar-entrypoint.sh
  • docs/design/agent-workflows/projects/subscription-sidecar/README.md
  • docs/design/agent-workflows/projects/sidecar-deployment-proposal/status.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • services/agent/docker/Dockerfile.sidecar

Comment on lines +12 to +13
`pi_agenta`, and `claude`. Optionally bakes Claude Code from Anthropic for fast cold
starts. See [The all-harness self-host sidecar](#the-all-harness-self-host-sidecar)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

"Optionally bakes" contradicts the true default.

The bullet says "Optionally bakes Claude Code" but the default INSTALL_CLAUDE_CODE=true means it bakes unless explicitly disabled. Change to "Bakes Claude Code by default (disable with INSTALL_CLAUDE_CODE=false)" or similar to avoid implying opt-in when it's opt-out.

New Dockerfile.sidecar + sidecar-entrypoint.sh: one self-host image that serves
every harness (pi_core, pi_agenta, claude) on :8765 with no compose CMD override.
Builds on the production runner image and bakes the Pi provisioning the dev compose
CMD does inline (a writable /pi-agent so the Agenta Pi extension installs without
EACCES), gives Claude Code a writable HOME for the read-only ~/.claude OAuth mount,
and optionally bakes Claude Code from Anthropic (INSTALL_CLAUDE_CODE, self-host
licensing boundary documented). Docs synced in docker/README.md + the
subscription-sidecar and sidecar-deployment-proposal project docs.

Claude-Session: https://claude.ai/code/session_01GYo3UEfvsZpncagqb28Mbc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review Agent updated; awaiting Mahmoud's review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant