The current backend runs agents in Docker containers: docker run -i --rm, one container per message invocation. Crackbox replaces Docker with QEMU/KVM virtual machines. The agent inside is identical — same image, same IPC, same skills. The isolation boundary is stronger: a separate kernel, no shared namespace with the host.
Crackbox is a QEMU/KVM VM platform designed for agent sandboxing:
container/runner.go implements one function: Run(ctx, cfg, onOutput) error. It starts a Docker container with the given config, streams output to onOutput, and returns when the container exits.
A crackbox backend would implement the same signature, replacing Docker with a QEMU VM boot (or pool claim). The gateway calls runner.Run() and never imports Docker. The gateway does not need to change.
// current: container/runner.go
func Run(ctx context.Context, cfg RunConfig, onOutput func(string)) error {
// docker run -i --rm ...
}
// crackbox: crackbox/runner.go
func Run(ctx context.Context, cfg RunConfig, onOutput func(string)) error {
// claim or boot a QEMU VM
// mount group folder via virtio-9p
// run agent-runner inside the VM
// stream output via SSH or serial console
}
The current model is one-shot: docker run -i --rm with JSON on stdin, output on stdout. Crackbox's claude-stream endpoint (as prototyped) takes a different approach. A new /v1/run endpoint that accepts the same start.json format and returns streaming output is needed before the two can be wired together.
Docker uses bind mounts (-v /host/path:/container/path) to give the container access to the group folder. VMs use virtio-9p (Plan 9 filesystem protocol) for host-to-guest file sharing. The mount behavior is similar but not identical — file locking semantics differ. Needs testing under load.
The current IPC design uses a unix socket mounted into the container via a volume. Unix sockets do not cross VM boundaries. The crackbox backend needs a TCP bridge instead:
| Current (Docker) | Crackbox (VM) |
|---|---|
socat STDIO UNIX-CONNECT:/workspace/ipc/router.sock | socat STDIO TCP:host-gateway:PORT |
| Unix socket volume-mounted into container | TCP port forwarded through TAP network |
The MCP server in the gateway would listen on a TCP port (bound to the TAP network interface) instead of a unix socket. The settings.json socat command in the agent container changes accordingly. Everything else in the MCP stack is unchanged.
arizuko itself runs in Docker (see docker-compose.yml). QEMU/KVM requires /dev/kvm access, which must be passed through from the host:
# docker-compose.yml fragment for crackbox mode
services:
gated:
devices:
- /dev/kvm:/dev/kvm
This works on Linux hosts with KVM enabled. It does not work on Docker Desktop (macOS/Windows) without additional configuration. Crackbox is a Linux-only deployment.
Docker container start: under 1 second. QEMU VM boot: 15–30 seconds (Alpine, minimal image, KVM acceleration). This makes crackbox unsuitable for per-message invocations in high-frequency conversations. The intended use is long-running conversations — a session that lasts 10 minutes does not notice a 20-second boot.
A warm VM pool mitigates this: boot VMs ahead of time and claim them at invocation. The pool size is a configuration tradeoff between memory cost and boot latency.
container.Run() is the seam. The VM becomes a different backend implementing the same input/output protocol. The agent inside the VM is identical — same Alpine image, same Claude Code CLI, same IPC, same skill files. From the gateway's perspective, a crackbox run and a Docker run are the same call with the same return contract.
This is why the container runner is isolated in its own package with no gateway-internal imports. The backend is replaceable. The agent is not.