Second step of PRD 0005. The mitmproxy sidecar from the previous
commit now actually runs alongside pipelock when a bottle launches.
- BottleBackend gains a non-abstract provision_ca with a default
no-op so non-Docker backends aren't forced to implement TLS
interception. provision() orchestrates ca → prompt → skills → ssh
→ git; CA goes first so trust is set up before anything else runs
inside the agent.
- DockerBottlePlan gains `mitmproxy_plan: MitmproxyProxyPlan`. The
prepare step builds it alongside the existing pipelock plan; no
new manifest schema or host-side scratch files.
- DockerBottleBackend grows self._mitm, threads it through prepare
and launch. Mirror of the existing self._proxy pattern.
- launch.py brings the mitmproxy sidecar up between pipelock and
the agent container, passing pipelock's service-name URL via
env. ExitStack callback handles teardown in reverse order.
- The agent's HTTPS_PROXY / HTTP_PROXY now point at mitmproxy (not
pipelock directly). Three new -e flags inject the CA trust trio
(NODE_EXTRA_CA_CERTS / SSL_CERT_FILE / REQUESTS_CA_BUNDLE) at
docker run time; Docker propagates those into docker exec so the
claude process sees them without per-exec threading.
- New provisioner backend/docker/provision/ca.py extracts the CA
cert from the running mitmproxy sidecar, copies it into the agent
at /usr/local/share/ca-certificates/claude-bottle-mitm.crt, runs
update-ca-certificates, and emits a stderr line with the SHA-256
fingerprint (stdlib ssl + hashlib; no subprocess).
Cleanup needs no change — `docker ps --filter name=^claude-bottle-`
already catches the new claude-bottle-mitm-<slug> containers.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The helper is a thin subprocess wrapper over `container_exists` +
`docker rm -f`, so it belongs alongside the other docker primitives
in util.py rather than as a private in launch.py.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move the resolution, bring-up, and orphan-cleanup logic out of
backend.py into three topic-named modules. DockerBottleBackend becomes
a thin façade that wires the per-instance pipelock proxy and the
provision orchestrator into the free functions.
backend.py drops from ~360 to ~70 lines and each topic now reads
end-to-end in one place. Mirrors the existing provision/ split.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>