feat(sidecars): bundle image + init supervisor (PRD 0024 chunk 1) #55

Merged
didericis merged 4 commits from prd-0024-chunk-1-bundle-image into main 2026-05-27 00:37:56 -04:00
Owner

Summary

PRD 0024 chunk 1: ships the claude-bottle-sidecars bundle image and its Python init supervisor. No renderer changes — chunk 2 wires the bundle into the docker compose flow behind a feature flag.

Dockerfile.sidecars is a multi-stage build that pulls the pinned pipelock and gitleaks binaries into a mitmproxy-base final image, installs git + openssh-client, and drops in the project's existing egress addon + supervise server alongside the new init at /app/sidecar_init.py.

claude_bottle/sidecar_init.py is PID 1 in the bundle. It spawns the daemons listed in CLAUDE_BOTTLE_SIDECAR_DAEMONS (or all four by default), forwards SIGTERM/SIGINT with an 8s grace before SIGKILL, and exits with the first-unexpected-child exit code — implementing the "any unexpected death tears down the bundle" semantics PRD 0024 picked as the default for open question 1.

claude_bottle/egress_entrypoint.sh is the existing Dockerfile.egress sh -c ENTRYPOINT extracted verbatim into a script so the supervisor can call it as a normal child.

Tests

  • Unit (14 cases, all passing): _selected_daemons env-var subset filtering (7 cases including whitespace, ordering, unknown names), _Supervisor end-to-end signal/exit-code semantics including SIGKILL escalation past the grace deadline, and an end-to-end main() test that runs the supervisor as a real subprocess and sends it SIGTERM.
  • Integration (5 cases): builds the image locally and verifies pipelock/gitleaks/mitmdump/python-imports-supervise are all present + executable, plus a no-daemons-selected smoke test of the ENTRYPOINT. Skipped under act_runner (multi-stage build pulls 200+MB worth of base images; out of scope for the CI runner's storage/time budget).

Out of scope for this chunk

  • Renderer collapse (compose: 5 services → 2). Chunk 2.
  • Deletion of Dockerfile.{egress,git-gate,supervise}. Chunk 3.
  • Bundle-aware orphan cleanup + container-name helper consolidation. Chunk 3.
## Summary PRD 0024 chunk 1: ships the `claude-bottle-sidecars` bundle image and its Python init supervisor. No renderer changes — chunk 2 wires the bundle into the docker compose flow behind a feature flag. `Dockerfile.sidecars` is a multi-stage build that pulls the pinned pipelock and gitleaks binaries into a mitmproxy-base final image, installs `git` + `openssh-client`, and drops in the project's existing egress addon + supervise server alongside the new init at `/app/sidecar_init.py`. `claude_bottle/sidecar_init.py` is PID 1 in the bundle. It spawns the daemons listed in `CLAUDE_BOTTLE_SIDECAR_DAEMONS` (or all four by default), forwards SIGTERM/SIGINT with an 8s grace before SIGKILL, and exits with the first-unexpected-child exit code — implementing the "any unexpected death tears down the bundle" semantics PRD 0024 picked as the default for open question 1. `claude_bottle/egress_entrypoint.sh` is the existing Dockerfile.egress `sh -c` ENTRYPOINT extracted verbatim into a script so the supervisor can call it as a normal child. ## Tests - **Unit** (14 cases, all passing): `_selected_daemons` env-var subset filtering (7 cases including whitespace, ordering, unknown names), `_Supervisor` end-to-end signal/exit-code semantics including SIGKILL escalation past the grace deadline, and an end-to-end `main()` test that runs the supervisor as a real subprocess and sends it SIGTERM. - **Integration** (5 cases): builds the image locally and verifies pipelock/gitleaks/mitmdump/python-imports-supervise are all present + executable, plus a no-daemons-selected smoke test of the ENTRYPOINT. Skipped under act_runner (multi-stage build pulls 200+MB worth of base images; out of scope for the CI runner's storage/time budget). ## Out of scope for this chunk - Renderer collapse (compose: 5 services → 2). Chunk 2. - Deletion of `Dockerfile.{egress,git-gate,supervise}`. Chunk 3. - Bundle-aware orphan cleanup + container-name helper consolidation. Chunk 3.
didericis added 1 commit 2026-05-27 00:05:23 -04:00
feat(sidecars): bundle image + Python init supervisor (PRD 0024 chunk 1)
test / unit (pull_request) Successful in 22s
test / integration (pull_request) Successful in 1m12s
61f63684ac
New Dockerfile.sidecars multi-stage build: pulls the pinned
pipelock and gitleaks binaries into a mitmproxy-base final image,
installs git + openssh-client, and ships the project's egress
addon + supervise server alongside a stdlib-Python init at
/app/sidecar_init.py.

The init supervisor (claude_bottle/sidecar_init.py) is PID 1 in
the bundle. It spawns the daemons named in
CLAUDE_BOTTLE_SIDECAR_DAEMONS (or all four by default),
propagates SIGTERM/SIGINT to children with an 8s grace before
SIGKILL, and exits with the first-unexpected-child exit code so
a daemon crash tears down the bundle (per PRD 0024 open
question 1's default).

claude_bottle/egress_entrypoint.sh extracted verbatim from
Dockerfile.egress's prior inline sh -c so the supervisor can
call it as a normal child.

Tests:
- unit: _selected_daemons env-var subset behavior (7 cases),
  _Supervisor signal/exit-code semantics including SIGKILL
  escalation, and end-to-end main() via subprocess.
- integration: builds the image and probes that pipelock,
  gitleaks, mitmdump, and the supervise Python module are
  present + executable, plus a no-daemons-selected smoke test
  of the entrypoint wiring. Skipped under act_runner (200+MB
  base pulls + multi-stage build).

Renderer collapse and the deletion of Dockerfile.{egress,git-gate,
supervise} land in chunk 2 + 3.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis added 1 commit 2026-05-27 00:10:38 -04:00
chore(sidecars): drop documentation-only EXPOSE
test / unit (pull_request) Successful in 20s
test / integration (pull_request) Successful in 1m12s
fa9b754d77
EXPOSE doesn't publish ports — the compose renderer does that.
Carrying it just to document the in-container port set adds
noise without doing work.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis added 1 commit 2026-05-27 00:19:54 -04:00
fix(sidecars): child death no longer tears down the bundle
test / unit (pull_request) Successful in 20s
test / integration (pull_request) Successful in 1m8s
62109a1caf
Reverses chunk 1's "any unexpected child death tears down the
rest" policy. New behavior: a daemon dying is logged but does
NOT initiate shutdown — the surviving daemons keep running and
whatever the dead one served starts failing visibly on the
agent side. The supervisor exits only when (a) it receives
SIGTERM/SIGINT, or (b) every child has died on its own.

Eventual design is restart-the-dead-daemon plus a notification
to the supervise sidecar so the operator sees the event
explicitly; this commit ships only the "log and leave alone"
half. PRD 0024 open question 1 updated to reflect the new
intent.

Tests updated: replaced "crash propagates exit code via
auto-teardown" with three cases that exercise the new policy
(crash without shutdown leaves survivors up, crash-then-signal
surfaces the nonzero code, all-children-die-unattended still
converges the loop).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis added 1 commit 2026-05-27 00:24:28 -04:00
chore(sidecars): re-add EXPOSE with documentation comment
test / unit (pull_request) Successful in 20s
test / integration (pull_request) Successful in 1m11s
c06decd53d
Reverts the earlier removal — EXPOSE is doc-only on the
renderer-driven publish path, but keeping it in the Dockerfile
(with the comment naming it as such) documents the bundle's
port surface for anyone reading the file.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis merged commit 40aeb0c356 into main 2026-05-27 00:37:56 -04:00
Sign in to join this conversation.