# Proposal: approval-free "box" containers on the laptop
Jan, 2026-09-26 (voice): JARVIS should be able to start Docker containers on the
laptop with no access to any local device — root inside, clone its own GitHub
repos, install anything, push — without anyone approving. It should be able to
create its own lanes for them ("create a new GitHub project and work on it").
And: Claude AND Codex must work in them on Jan's subscriptions.
This needs gateway/ and ops/ changes, which JARVIS may not write. This is the spec
for Claude on the laptop.
## What already exists without the laptop
A `container` lane on the VPS already is this: root in JARVIS's container, the
jarvis-jhk token wired into git, Claude and Codex both on Jan's subscriptions,
no approvals. `npx tsx scripts/lane.ts add … --github <repo>` (agent, 2026-09-26)
creates the repo, clones it into data/work/<lane>/ and registers the lane.
What the laptop adds: more isolation from JARVIS's own tree, more CPU/RAM,
and Docker-in-the-box being possible. Cost: the laptop must stay awake while a
box runs.
## 1. The wrapper: `/usr/local/sbin/jarvis-box` (new, sibling of jarvis-spawn)
Same threat model as jarvis-spawn (stdin JSON is attacker-controlled), fixed shape:
- image `jarvis-box:latest` (Dockerfile.box: Ubuntu, git, build-essential,
node 22, `@anthropic-ai/claude-code`, `@openai/codex`, python3, jq, curl)
- `--user 0` inside, but: no `--privileged`, `--cap-drop ALL --cap-add`
CHOWN,DAC_OVERRIDE,FOWNER,SETUID,SETGID,NET_BIND_SERVICE,KILL (enough for apt),
`--security-opt no-new-privileges`, default seccomp/apparmor
- **no bind mounts at all**, no devices, no docker socket, no host network,
no host pid/ipc
- storage: one named volume per project, `jarvis-box-<project>` → `/work`
(survives between runs, so a clone is not redone every time)
- limits: `--cpus 4 --memory 6g --pids-limit 1024`, hard timeout (default 4 h)
- network: its own bridge `jarvis-box-net` with egress to the internet but the
LAN dropped: `DOCKER-USER` rules rejecting 10/8, 172.16/12, 192.168/16,
169.254/16 and the laptop's own addresses from that bridge (DNS via Docker's
resolver). Without this a box can reach the phones, the Pi, the router and
the gateway port — with no human in the loop that is the one thing that must
not be open.
- credentials on a tmpfs, read-only, removed at exit (as jarvis-spawn does):
`CLAUDE_CODE_OAUTH_TOKEN` (Jan's Claude subscription), `~/.codex/auth.json`
(Jan's ChatGPT login for Codex — needs a writable copy, the CLI refreshes it),
`GH_TOKEN` for jarvis-jhk only. None of Jan's own GitHub credentials.
- project id `^[a-z][a-z0-9-]{0,31}$`; engine `claude|codex`; model from a fixed list.
## 2. The gateway
- `POST /v1/box {project, prompt, engine, model?, effort?, reason}` → starts a
detached box worker; `GET /v1/box/:id`, `/logs`, `POST …/message|continue|stop`
mirroring the existing worker endpoints, so the agent side can reuse its
laptop-lane follower.
- Policy: new tier-free class `box`, **allowed without judge or human** when
`policy.json` has `"box": {"auto": true}`, still audited, still HMAC'd.
Caps: max 2 concurrent boxes, 20 starts/hour, protectedPathPatterns applied to
the prompt as for spawn.
- The judge may still be asked for the record, but must not block.
- Usage-limit park/resume: same as detached workers.
## 3. The agent side (JARVIS does this once the gateway exists)
- lane kind `box` with `boxProject`; `scripts/lane.ts add --kind box` creates
the repo and the lane; the run prompt tells the worker to clone into /work.
- The report API URL goes into the box prompt like every lane.
## Open questions for Jan
1. Is LAN-blocking OK? (Recommended: yes. A box that needs a LAN device goes
through the normal gateway with approval.)
2. Keep the laptop awake while a box runs (systemd-inhibit), or only run
boxes when it is already awake?
3. Docker-in-the-box (building images inside) needs sysbox or rootless dind —
wanted, or later?