refactor(cleanup): compose-ls driven + drop pipelock CIDR allowlist #36

Merged
didericis merged 2 commits from chunk-4-cleanup-cli into main 2026-05-26 00:04:30 -04:00
Owner

Summary

PRD 0018 chunk 4. Two commits:

Commit 1 — spike result: networks become compose-managed. I empirically verified that pipelock's SSRF guard checks the destination of proxied requests (e.g. api.anthropic.com → public IP) and not the source IP of incoming connections. The bottle's own internal CIDR was being added to ssrf.ip_allowlist defensively, but probing pipelock directly (curl --proxy http://pipelock https://api.anthropic.com/) returns 404 from upstream rather than blocking — the allowlist isn't load-bearing. So networks become compose-managed (internal: true on the internal network); chunk 3's pre-create + CIDR introspection + pipelock yaml re-render dance all come out.

Commit 2 — cleanup CLI on docker compose ls. Backend's prepare_cleanup derives its work from docker compose ls --all --format json filtered to claude-bottle- projects. Per project: one compose down --volumes removes the containers + compose-managed networks atomically. Three fallback buckets handle anything outside that — stray containers without compose labels (pre-compose orphans), stray networks (same), and orphan state dirs (no live project + no .preserve marker).

The CLI collapses to one y/N — backend handles state-dir reaping inside the same plan instead of via a separate prompt. .preserve (capability-block or crash auto-preserve) explicitly opts out of reaping; manual rm -rf remains the only path for preserved state.

Stacking note

This branch is based on PR #35 (chunk 3), not main. Merge order: #35 first, then this.

Status

  • 423 unit tests pass (7 new for orphan-state-dir detection)
  • Supervise integration test passes
  • ./cli.py start implementer end-to-end works with compose-managed networks (no SSRF blocks)
  • ./cli.py cleanup correctly enumerates + reaps projects, strays, and orphan state dirs
## Summary PRD 0018 chunk 4. Two commits: **Commit 1 — spike result: networks become compose-managed.** I empirically verified that pipelock's SSRF guard checks the *destination* of proxied requests (e.g. `api.anthropic.com` → public IP) and not the source IP of incoming connections. The bottle's own internal CIDR was being added to `ssrf.ip_allowlist` defensively, but probing pipelock directly (`curl --proxy http://pipelock https://api.anthropic.com/`) returns 404 from upstream rather than blocking — the allowlist isn't load-bearing. So networks become compose-managed (`internal: true` on the internal network); chunk 3's pre-create + CIDR introspection + pipelock yaml re-render dance all come out. **Commit 2 — cleanup CLI on `docker compose ls`.** Backend's `prepare_cleanup` derives its work from `docker compose ls --all --format json` filtered to `claude-bottle-` projects. Per project: one `compose down --volumes` removes the containers + compose-managed networks atomically. Three fallback buckets handle anything outside that — stray containers without compose labels (pre-compose orphans), stray networks (same), and orphan state dirs (no live project + no `.preserve` marker). The CLI collapses to one y/N — backend handles state-dir reaping inside the same plan instead of via a separate prompt. `.preserve` (capability-block or crash auto-preserve) explicitly opts out of reaping; manual `rm -rf` remains the only path for preserved state. ## Stacking note This branch is based on PR #35 (chunk 3), not main. Merge order: #35 first, then this. ## Status - 423 unit tests pass (7 new for orphan-state-dir detection) - Supervise integration test passes - `./cli.py start implementer` end-to-end works with compose-managed networks (no SSRF blocks) - `./cli.py cleanup` correctly enumerates + reaps projects, strays, and orphan state dirs
didericis added 2 commits 2026-05-25 23:48:17 -04:00
PRD 0018 chunk 4 spike: empirically verified that pipelock's SSRF
guard checks proxied-request destinations (e.g. api.anthropic.com →
public IP) and not source IPs of incoming connections. The
bottle's own internal CIDR was being added to ssrf.ip_allowlist
defensively, but that defense isn't load-bearing — direct pipelock
probe (`curl --proxy http://pipelock https://api.anthropic.com/`)
returns 404 from upstream rather than blocking on SSRF.

So:

  - Networks become compose-managed (`internal: true` on the
    internal network; the egress one is a normal user-defined
    bridge). Compose creates + removes them via up/down.
  - launch.py drops the `docker network create` + `network_inspect_cidr`
    + pipelock yaml re-render dance.
  - The pre-create/external scaffolding from chunk 3 goes with it.

End-to-end `./cli.py start` still works; cleanup leaves no
orphans. If real-world use surfaces an SSRF block we hadn't
predicted, the allowlist can come back via subnet-pinning rather
than pre-create.
refactor(cleanup): compose-ls driven, plus orphan state-dir reaping
test / unit (pull_request) Successful in 17s
test / integration (pull_request) Successful in 1m9s
aee249f119
PRD 0018 chunk 4. `claude-bottle cleanup` now derives its work
from `docker compose ls --all --format json`, filtered to projects
whose name starts with `claude-bottle-`. Per project: one `compose
down --volumes` removes the containers + the compose-managed
networks atomically.

The plan also enumerates three fallback buckets:

  - Stray containers — `claude-bottle-*` containers with no
    `com.docker.compose.project` label (left over from pre-compose
    code paths). Cleared via `docker rm -f`.
  - Stray networks — `claude-bottle-*` networks with no compose
    project label. Cleared via `docker network rm`.
  - Orphan state dirs — per-bottle `~/.claude-bottle/state/<id>/`
    dirs with no live project AND no `.preserve` marker. The
    `.preserve` marker (capability-block or auto-preserve-on-crash)
    explicitly opts-out of reaping; manual `rm -rf` is the only
    path for preserved state.

cli/cleanup.py collapses to a single y/N prompt — backend.prepare_cleanup
returns everything in one plan, backend.cleanup processes everything,
no more double-prompt for state. The CLI-side state-dir enumeration
+ `_state_summary` flags from PR #25 are gone; the backend's
orphan-detection rules subsume them.
didericis force-pushed chunk-4-cleanup-cli from 9f2498397f to aee249f119 2026-05-25 23:48:17 -04:00 Compare
didericis merged commit 0ae544d2a6 into main 2026-05-26 00:04:30 -04:00
Sign in to join this conversation.