Skip to content

feat(runtime-vm): credential proxy, egress allowlist and local vm images (ZOD128) - #131

Merged
orivibes merged 1 commit into
feat/zod109-vm-runtimefrom
feat/zod128-vm-smoke-tester
Oct 13, 2026
Merged

orivibes merged 1 commit into
feat/zod109-vm-runtimefrom
feat/zod128-vm-smoke-tester

Conversation

@orivibes

Copy link
Copy Markdown
Contributor

Part of #109 (cycle 2 of 2) and #128. Stacked on #127: merge that first, or merge both together after the full smoke run.

What

  • vm.allow_hosts and vm.git. A vm agent with allow_hosts gets --net and one --allow-host per entry. Without it, the guest still has no network. vm.git must be an https URL whose host is in allow_hosts, and it reaches the agent as ZOOID_VM_GIT.
  • Local images. vm.image starting with /, ./, ../ or ~/ is a docker save archive or rootfs directory. A size+mtime stamp joins the settings hash, so rebuilding the archive recreates the machine.
  • Credential proxy (pi only).
    • PRESETS.pi.vmCredential describes the contract.
    • The daemon starts an HTTP proxy on a host Unix socket for each such agent, mounts the socket into the guest with --mount-socket, and starts a guest TCP→Unix forwarder on 127.0.0.1:8787 (machine exec -d).
    • A credential-free guest setup points pi's openai-codex provider at the forwarder with a placeholder JWT and transport: sse.
    • The proxy forwards only /backend-api/codex/ to chatgpt.com. It swaps in the real bearer and account id, and streams both ways.
    • It reads a dedicated device-code login at ~/.zooid/cred-proxy/pi/auth.json, which it alone refreshes, under a lock file with a re-read under the lock.
  • Wiring. provisionVms runs proxy → create/start → guest setup → forwarder, closes the proxies on stop or on a failed setup, and logs [cred-proxy] <agent> <METHOD> <path> → <status> (<ms>ms). vm agents get PI_CODING_AGENT_DIR=/root/.pi/agent and skip the zooid-tasks extension (installPiExtensions is extracted from start-daemon).
  • Cycle-1 machines are kept. The new hash keys join only when set; a test pins the cycle-1 hash literal.

Found on the way: smolvm kills an exec after a networked first boot

With --net --allow-host, smolvm 1.25.2 SIGKILLs (exit 137) whatever exec is in flight about 130ms after a fresh machine's first boot, whatever the command:

  • A fast true can finish first and leave the kill for the next exec. In the egress suite that took 2 of 5 runs.
  • Restarts of an existing machine don't do it.

ensureVmMachine now settles a networked machine it just started with a sleep 1 exec, retried until one survives. That's one second per networked boot, and 6/6 green since.

Deviations from the plan

  • The forwarder script sits at argv index 8, not 9. The plan's own array has it at 8.
  • The plan's guest setup said to copy .pi-agent and then rm -rf …/sessions from the copy. Done in place, that would delete pi's live guest sessions on every start. The copy is staged instead, and a test pins that guest sessions survive.
  • The plan's Step 4.4 literals don't hold:
    • pi creates an empty auth.json ({}) in the guest.
    • zooid-placeholder is inside the base64 payload, so grep -c gives 0.
    • Checked instead: the only token in the guest is the placeholder, and its decoded claim is zooid-placeholder.
  • The image pins pi 1.0.2 / pi-acp 0.0.34, copied from the agent-pi Dockerfile, where the plan expected them in docker/versions.json. pi 1.0.2's codex provider has the same request shape, client id and token URL as the 0.87.1 the plan read.

Verification

  • pnpm test, pnpm typecheck and the CLI build are green: 1,480 tests pass.
  • pnpm -C packages/runtime-vm test:vm (now including vm-egress.integration.test.ts) and pnpm -C packages/cli test:vm (vm-cred-proxy.integration.test.ts) pass against real smolvm machines:
    • An allowed host is reachable and another host is blocked.
    • The host socket is reachable through the forwarder.
    • A guest call reaches a fake upstream carrying the host credential.
    • A disallowed path gets a 403.
  • Step 4.4, real credential. Laptop, agent-smoke image, local-workstation config:
    • Guest pi --print returned proxy ok, logged as [cred-proxy] smoke-tester POST /backend-api/codex/responses → 200 (1924ms).
    • chatgpt.com is blocked from the guest, and github.com returns 200.
  • ZOD091 dry run, not the done criterion. Without the live daemon, the guest ran bin/smoke checkout/build/configure/start on main. The real smoke_matrix watch code then ran steps 2–4:
    • Typing stays continuous across both concurrent replies.
    • Typing drops before the elicitation request, returns after the answer, and clears within 6ms of the last reply.

Not verified: Step 4.5, the unattended run from a Lead handoff in #product. It needs the local-workstation daemon running this build. The local-workstation changes (image, smoke tester, workforce) go in the z repo after this merges, per the plan.

🤖 Generated with Claude Code

…ges (ZOD128)

ZOD109 cycle 2. A runtime: vm agent can reach the network and its model
without the credential entering the guest.

- core: vm.allow_hosts (no network without it) and vm.git (https, host
  must be allowlisted).
- runtime-vm: --net/--allow-host/--mount-socket in the create argv, hashed
  only when set so cycle-1 machines are kept; local image archives with a
  size+mtime stamp; a guest TCP→Unix forwarder; a settle exec after a
  networked first boot, because smolvm 1.25.2 SIGKILLs the exec in flight
  then.
- acp-client: the pi preset's vmCredential (openai-codex proxy contract,
  placeholder key, credential-free guest setup).
- cli: host credential proxy on a Unix socket with a single-refresher
  codex OAuth login; provisionVms wires proxy → machine → guest setup →
  forwarder; vm agents get guest env and skip the zooid-tasks extension.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@orivibes
orivibes force-pushed the feat/zod128-vm-smoke-tester branch from 159d400 to e5929c3 Compare October 13, 2026 21:33
@orivibes
orivibes added this pull request to stack #139 October 13, 2026 21:34
@orivibes
orivibes merged commit aa7699f into main Oct 13, 2026
2 checks passed
@orivibes
orivibes deleted the feat/zod128-vm-smoke-tester branch October 13, 2026 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant