fix(smolmachines): docker push fails on Docker Desktop — daemon-side route differs from host loopback #74

Merged
didericis-claude merged 13 commits from fix-local-registry-docker-desktop into main 2026-05-27 16:10:46 -04:00
2 changed files with 37 additions and 0 deletions
Showing only changes of commit 45c821a8f3 - Show all commits
+21
View File
@@ -190,6 +190,27 @@ The container is removed automatically when the session ends. If the script
is killed with SIGKILL the exit trap won't fire and the container may be is killed with SIGKILL the exit trap won't fire and the container may be
left running; remove it with `docker rm -f <container-name>`. left running; remove it with `docker rm -f <container-name>`.
### Smolmachines backend (experimental, macOS-only)
A second backend runs the agent in a smolvm micro-VM (libkrun) with the
sidecar bundle still in Docker. Selected via
`CLAUDE_BOTTLE_BACKEND=smolmachines ./cli.py start <agent>`. Requires
`smolvm` on PATH (`curl -sSL https://smolmachines.com/install.sh | sh`).
**Known limitation, v1:** smolvm's TSI uses macOS networking, and
Docker Desktop's container IPs aren't reachable from macOS, so the
smolmachines bottle dials the sidecar bundle through host loopback
port-forwards (`127.0.0.1:<random>`). TSI filters by IP only, so the
allowlist is `127.0.0.1/32` — meaning the agent VM can reach **any
service bound to macOS's loopback**, not just the bundle's published
ports. Practical implication: while a smolmachines bottle is running,
host-local dev services (postgres on 5432, dev servers, etc.) are
reachable from inside the agent even if you intended them to be
host-private. The docker backend keeps the bottle on a `--internal`
docker network and doesn't have this issue. A future revision will
narrow this via a per-bottle loopback alias + host-side proxy (see
PRD 0023's "loopback scoping" section).
## Manifest ## Manifest
Bottles and agents live as Markdown files with YAML frontmatter under Bottles and agents live as Markdown files with YAML frontmatter under
+16
View File
@@ -600,6 +600,22 @@ PRD 0024's bundle image is a prerequisite — this PRD assumes
the plan is to filter on a deterministic name prefix the plan is to filter on a deterministic name prefix
`claude-bottle-<slug>` + cross-reference with on-disk metadata `claude-bottle-<slug>` + cross-reference with on-disk metadata
under `state/<slug>/`. under `state/<slug>/`.
8. **Loopback scoping (Docker Desktop pivot).** The original
design pinned the bundle at a docker bridge IP and set TSI's
allowlist to `<bundle-ip>/32`. On Docker Desktop / macOS the
daemon runs inside its own Linux VM, so bridge IPs aren't
reachable from macOS networking — TSI's syscall impersonation
can't reach them. Resolution: publish each agent-facing bundle
port on host loopback (`-p 127.0.0.1::<port>`) and set TSI to
`127.0.0.1/32`. **This widens the TSI allowlist to anything
bound to macOS's loopback** — postgres, dev servers, other
bottles' published ports, mDNSResponder, etc. The agent can't
reach them by intent, but TSI can't filter by port. Follow-up
to scope back: bind each bottle's bundle ports on a per-bottle
loopback alias (e.g. `127.0.0.2` for bottle A, `127.0.0.3` for
B) added via `ifconfig lo0 alias`, set TSI to that single /32.
Needs sudo for alias setup; a small daemon-or-script we ship
alongside the launcher could handle it.
## References ## References