feat(apple-container): Apple Container runtime + remote OneCLI gateway#2809
feat(apple-container): Apple Container runtime + remote OneCLI gateway#2809hidenwalker wants to merge 2 commits into
Conversation
Env-gated CONTAINER_RUNTIME=container selects Apple's `container` CLI instead of Docker. Defaults to docker, with a byte-identical service env, so existing installs are unaffected. Apple Container (macOS): - container-runtime.ts: runtime-binary switch, bridge100 host-gateway autodetect (192.168.64.0/24), `ls --format json` orphan cleanup scoped by install label, system + builder start. - No --add-host under Apple Container, so rewrite the host.docker.internal literal that OneCLI bakes into its injected proxy env to the bridge gateway IP (value-driven; -v mount args untouched). - onecli-forwarder.ts: host-side TCP relay (bridge IP:10255 -> 127.0.0.1) so the VM can reach a LOCAL OneCLI gateway running in Docker Desktop. Binds the host-only bridge IP only, never 0.0.0.0; refuses to start if the unauthenticated control API is reachable off-loopback. - index.ts: assert the install lives under HOME and pin TMPDIR under HOME (Apple Container only shares mount sources under HOME; fail fast instead of spawning with silently-missing mounts). - egress-lockdown hard-gated off (Apple Container has no Docker bridge net). Remote OneCLI gateway (works under either runtime): - A non-loopback ONECLI_URL skips the local forwarder; the agent reaches the remote gateway directly over the LAN. - ONECLI_GATEWAY_REWRITE="from=to" rewrites the gateway host OneCLI bakes into the proxy env (its own container-bridge IP) to a LAN-reachable address, so a remote agent can reach the proxy. setup/service.ts emits CONTAINER_RUNTIME into the launchd/systemd env so the runtime flip survives a service reload. .env.example documents every knob, including the requirement that Docker Desktop stay running to host a local OneCLI gateway. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Triage verdict: defer until one blocker is fixed, then this is still high-risk and should get maintainer macOS/Apple Container E2E before merge. Verified on a fresh clone merged onto current
Blocker:
Risk notes after that fix:
|
… autodetect detectHostGateway() returned `addr || NANOCLAW_HOST_GATEWAY_IP || .1`, so the documented override was a no-op whenever a bridge100 address existed. Swap to `NANOCLAW_HOST_GATEWAY_IP || addr || .1` so an explicit override wins, and add a regression test that mocks config + re-imports the runtime to exercise that path. Reported by mksocial19-code on PR nanocoai#2809 (blocker). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014uQUtemkjFQGeg3BEhDFiB
|
Thanks for the careful triage, @mksocial19-code — the blocker is a real bug and you're right about the precedence. Fixed in d950a51:
Full suite green on a clean clone: On the risk note: agreed this touches core spawn/runtime and warrants a maintainer macOS Apple Container E2E before merge. For what it's worth, this exact branch has been running live on a Mac mini (Apple Container + a remote OneCLI gateway over LAN) since submission — local and remote OneCLI modes both exercised end-to-end. Happy to provide repro steps or logs if that helps a maintainer reproduce. |
What
Adds an env-gated
CONTAINER_RUNTIME(dockerdefault |container= Apple Container, macOS) and first-class support for pointing NanoClaw at a remote OneCLI gateway.CONTAINER_RUNTIME=dockeris the default and is byte-identical to today's behavior — existing installs are unaffected (the new code paths are all gated behindIS_APPLE_CONTAINER/ a non-loopbackONECLI_URL).Apple Container (macOS)
container-runtime.ts: runtime-binary switch, bridge100 host-gateway autodetect (192.168.64.0/24),ls --format jsonorphan cleanup scoped by install label,system+builderstart.--add-hostunder Apple Container, so thehost.docker.internalliteral OneCLI bakes into its injected proxy env is rewritten to the bridge gateway IP (value-driven, on-evalues only; mount args untouched). The gateway is resolved lazily at spawn time, after the bridge is up.onecli-forwarder.ts: host-side TCP relay (bridge IP:10255 → 127.0.0.1) so the VM can reach a local OneCLI gateway in Docker Desktop. Binds the host-only bridge IP only — never 0.0.0.0 — rejects off-bridge clients, and refuses to start if the unauthenticated control API is reachable off-loopback.index.ts: asserts the install lives under$HOMEand pinsTMPDIRthere (Apple Container only shares mount sources under HOME — fail fast instead of spawning with silently-missing mounts).Remote OneCLI gateway (works under either runtime)
ONECLI_URLskips the local forwarder; the agent reaches the remote gateway over the LAN.host.docker.internal, which assumes a Docker-Desktop-local agent) is retargeted to the remote gateway's host, derived automatically fromONECLI_URL— so remote OneCLI usually needs onlyONECLI_URLset.ONECLI_GATEWAY_REWRITE="from=to"is an explicit override for when the gateway is reachable at a different address than the control API host (e.g. a Tailscale IP); validated eagerly so a malformed value fails fast.setup/service.tsemitsCONTAINER_RUNTIMEinto the launchd/systemd service env so the runtime flip survives a service reload..env.exampledocuments every knob and the security model (the proxy port carries vault credentials — keep it off the open LAN).Why
Lets a macOS host run agent containers on Apple's lightweight
containerruntime, and — paired with a remote OneCLI gateway on another host — drop Docker Desktop entirely for a meaningful RAM saving, while keeping the default Docker path unchanged.Testing
src/apple-container.test.ts: host-gateway fallback (regression guard for the empty-string env default), bridge-subnet guard, and remote-host derivation including the bracketed IPv6 loopback[::1]case.tscclean; Prettier clean.🤖 Generated with Claude Code