sandbox daemon: push and streaming surface for live consumers (ranked asks)
Author: rohitg00Created Aug 11, 2026Updated Aug 11, 2026
The sandbox daemon's surface is complete for request/response agent use, but everything a live console (or any subscriber) needs is missing: nothing is observable without polling, long output cannot stream, and several state fields never leave the daemon. These asks came out of building the sandbox console surface in iii-hq/workers#762 (plus #766/#767), each was hit in practice, and three now have a second confirmed consumer from live agent sessions.
Ranked by unblocked value:
- Lifecycle events on the bus — emit
created/ready/stopped/reaped(with reason) as a subscribable trigger. Today the reaper kills VMs invisibly. Workers#762 ships a watcher that pollssandbox::listserver-side and re-emits change-only snapshots purely to fake this; that module deletes itself the day the daemon emits its own events. - Streaming exec — stdout/stderr chunk channels on
sandbox::exec, plus atruncatedflag onExecResponse(the 1 MiB cap is currently silent). Consumers: live output tail in the console, and shell's sandbox-targeted background jobs, which today can only capture output at completion because they sit on one buffered exec. - Exec cancel — no way to abort a running exec short of stopping the whole sandbox. A hung foreground command holds one of 4 slots for its full timeout. Confirmed twice: the console can only watch, and
shell::killon a sandbox job flips its record but cannot reach the in-VM process. - Idle visibility on
sandbox::list—idle_secs+idle_timeout_secs(or a computedreap_in_secs) per sandbox. The console ships a dormant TTL countdown that lights up when these fields appear. Client-side approximation is impossible: a viewer cannot see other actors' fs activity, which bumps the idle clock. sandbox::describe— cpus, memory_mb, network on/off, wall-clock created_at. cpus/memory are dropped after boot today (not retained on the daemon's own state), and created_at is a monotonic Instant that cannot serialize.- Real pull progress — the create-phase events exist but fire after the pull with
progress_bytes: 0; cold creates are 5 to 30 seconds of dead air. Byte-level progress during the pull enables an honest boot checklist. - Honor
stop.wait— the field is accepted and ignored; callers cannot block on teardown. - Config echo — effective
max_concurrent_sandboxes, image allowlist, and defaults are not queryable;exec_slots_freeis the only limit that leaks out. - Docs drift — README's
sandbox::listresponse documents fields that do not exist (status,created_at,last_exec_at),FsMatchis documented as{path, line_no, byte_offset, line}vs the actual{path, line, content}, andiii.worker.yamlsays "14 triggers" vs the actual 16.
Source: iii-hq/iii