NYX / ULTRON PROGRAM / ARCHITECTURE REPORT

Computer Use, Micro-VMs, and the Sandbox Substrate

2026-07-03 — written after a multi-hour failure to build/run Organ F (full-desktop computer-use) live on the Mac. Separates what was actually broken from what merely looked broken, decides whether computer-use should exist, and designs the provisioning substrate: fast micro-VMs + LunarFS + node_modules handling.
  1. Nothing about the computer-use runtime is broken. Every failure was in image provisioning — pulling and building the container image — not in Xvfb, the MCP server, the governor wiring, or teardown. Those are sound.
  2. The feature should exist, but scoped and relocated. Browser use (Organ A) is the 80% case and already runs natively with zero containers. Full desktop (Organ F) is a genuine but rare last-resort capability. Neither should be built or hosted on the 16GB Mac coordinator.
  3. The real fix is provisioning discipline, not a fancier VM: bake the image once, pull it once (serialized), keep a warm pool, run it on the Linux/WSL2 fleet — not Firecracker-on-Mac. "Spin up" drops from minutes (build) to ~1s (run cached).
  4. LunarFS belongs in the coding-worker fan-out, not the desktop image. Its instant copy-on-write fork solves the git-worktree + pnpm install cost and the node_modules-pollution bug class. The desktop image is baked, not forked, so LunarFS does little for it directly.
  5. node_modules is the crux, and the answer is to keep it out of the fork path: the container image owns installed dependencies; LunarFS forks only the source tree. node_modules never goes through per-file CoW hydration.

1. Why it was unreliable — the actual root causes

Today's timeline, deduplicated to root causes (all provisioning-layer):

SymptomRoot causeLayer
2-hour "still building", no imagedocker build hung at FROM python on parallel layer-download stallsregistry pull
Docker Desktop VM docker info hangs, EIO on bootstrapDocker Desktop's macOS VM backend is fragile without an interactive GUI sessionhost VM
Colima pull of any image hangscontainerd-snapshotter pull path broken in this Colima buildimage store
Colima build times out at FROM even after classic pull worksbuildkit has its own image resolver with a short metadata deadlinebuilder
The thing that finally workedmax-concurrent-downloads: 1 (serialize layers) + pre-pulled base + classic builderregistry pull

The single recurring villain across both Docker Desktop and Colima was parallel layer downloads stalling — small HTTP requests and even a 10 MB bulk download from the VM ran at 9 MB/s, but dockerd opening several blob connections at once wedged. Serializing fixed it instantly.

The deeper architectural faults these symptoms expose

2. Should computer-use exist at all?

Yes — as a tiered, last-resort capability, not a headline feature. The honest framing is the resourcefulness hierarchy the Ultron design already names:

direct API / SDK  →  existing MCP tool  →  browser (Organ A)  →  full desktop (Organ F)  →  park / ask
cheapest, most reliable ―――――――――――――――――――――――――――――――――― most expensive, least reliable

Killing Organ F would be wrong (it is the only answer for native-only software), but treating it as a first-class always-ready feature is also wrong. Tier it.

3. The provisioning fix (the part that actually matters)

MOVE 1 — Bake the image once; never build at use-time
Build nyx-desktop-mcp and nyx-browser-mcp once (CI, or a one-time local build) and publish to a registry the fleet can pull, or ship a docker save tarball. Pin max-concurrent-downloads: 1 in the daemon config the fleet uses. A worker session does docker run on a cached image — seconds, not the 2-hour cliff.
MOVE 2 — Warm pool + reuse
Keep N pre-started desktop/browser containers idle (memory-capped) so a session attaches to a warm one instead of cold-starting. Tear down on session end as today; replenish asynchronously. Turns ~1-2s into ~0.
MOVE 3 — Relocate off the Mac
Route all containerized computer-use to the fleet worker-hosts (Windows/WSL2 today, Linux later), never the coordinator. Add a desktop label to the scheduler's routing and pin Organ E/F WorkItems to it. The Mac coordinator stays Docker-free.

4. Faster micro-VMs — honest options

  1. Pre-baked image + warm pool (do this first) — 95% of the perceived speed win, runtime-agnostic. The 2-hour pain was build, not boot.
  2. Firecracker / Cloud Hypervisor microVMs (~125ms boot; what Fly.io and AWS Lambda use) — the right long-term substrate for the fleet, but needs a Linux KVM host. Does not run on macOS directly. Excellent on the Linux/WSL2 fleet, irrelevant for the Mac.
  3. libkrun / krunvm (OCI images as microVMs; on macOS uses Virtualization.framework) — promising but young and operationally rough. Not worth betting the reliability story on right now.
  4. Persistent Lima/Colima VM + many containers (what we have) — boots slowly once, containers are fast after. With Move 1-2 this is perfectly adequate as the interim substrate.
RecommendationMove 1-3 now (image discipline + warm pool + fleet). Firecracker as the fleet's microVM engine later. Do not try to make the Mac a fast-microVM host.

5. Where LunarFS fits

LunarFS (github.com/Emotions-Research/LunarFS): BLAKE3 content-addressed store; fork_workspace returns an instant copy-on-write mount (32-byte root hash, O(1) regardless of repo size — 13ms to fork the 2GB / 94,695-file Linux kernel vs 7.4s for git worktree add, 548x). Mounts via the OS NFS client on macOS/Linux; files hydrate lazily on first read. MCP tools: fork_workspace, mount, list_workspaces, push, grant_access, destroy. Engine AGPL-3.0, client Apache-2.0.

Its home is the coding-worker fan-out, not the desktop image. Two different problems:

The clean pictureTwo orthogonal layers: container image = OS + tools isolation (Docker/microVM) × LunarFS fork = instant repo materialization. A worker is a warm container that mounts a LunarFS fork of the source.

6. node_modules — the crux, and the strategy

node_modules is the classic adversary for any CoW / overlay / network filesystem. Why it hurts LunarFS specifically:

Strategy — keep node_modules out of the fork path entirely. Ranked:

  1. Deps live in the container image; LunarFS forks only source (recommended). The worker image is built with pnpm install --frozen-lockfile already run (correct arch, native modules compiled for the container). Each worker mounts a LunarFS fork of the source tree only. Working tree = baked node_modules ∪ forked source. Deps change ⇒ rebuild the image (infrequent, and now baked-once per Move 1). Dissolves the tiny-file, symlink, and ABI problems at once.
  2. Shared read-only pnpm store, node_modules materialized per fork via --offline. Point PNPM_STORE_DIR at a shared warm volume; each fork runs pnpm install --offline (seconds, no network, no compile if store is warm). Not instant, but robust.
  3. Fork node_modules through LunarFS but pre-warm the hot paths. Weakest option; fights the filesystem instead of avoiding the fight.
The designImage owns dependencies, LunarFS owns source. Composes perfectly with Move 1 (baked images) — the same "build deps once, run many" discipline solves both the computer-use provisioning problem and the node_modules problem.

7. Recommendation & proposed next steps

(for operator decision — not yet queued)

The through-line: the mistake was building/hosting a Linux sandbox lazily on a Mac. Bake once, run cached, on the fleet — and let LunarFS handle the source, not the deps.
NYX AGENT ORCHESTRATOR / ULTRON PROGRAM — ARCHITECTURE REPORT 2026-07-03