Allow opting out of `exec.log` capture (per sandbox and/or per exec) — interactive sessions are currently written to disk chunk by chunk

Author: hexfredCreated Sep 12, 2026Updated Sep 19, 2026
LabelsSDKCLI

Summary

Since #650, every exec session's stdout/stderr is appended to <sandbox>/logs/exec.log, including interactive tty: true sessions. There is no way to turn this off. For workloads that stream a lot of interactive traffic through exec, that puts a JSON-encode and a file write on the hot path of every output chunk. I'd like to request an opt-out. I'd be happy to contribute the PR if you agree on the shape.

Context: our use case

We run training labs on microsandbox, with hundreds of concurrent sandboxes, and each player gets an interactive terminal through an exec session (tty: true). Our design rule for the lab data path is memory, network and IPC only: no disk I/O per interaction. Everything on our side of the relay respects that.

With capture-all, each keystroke echo and each chunk of output (a player running cat on a large file, find /, a loop printing to the terminal) becomes:

  • a CBOR decode of the frame,
  • a serde_json encode, where terminal escape sequences inflate badly (ESC becomes ),
  • a write_all on exec.log,

all done inline in ring_reader_task before the frame is routed to its client. So one noisy session also adds latency to every other session on the same sandbox. The 10 MiB × 3 rotation bounds disk usage, but not write volume: under load the file is just rewritten continuously.

A second, smaller concern: interactive transcripts end up persisted on the host, including anything a root shell prints. In our case that can include challenge secrets. Some deployments would rather not keep that on disk.

Reproduction (msb 0.6.18, macOS arm64)

  1. Create a sandbox whose detached startup command is a long-running process (session id 1).
  2. Open an interactive exec session with tty: true and type echo secret.
  3. tail -f ~/.microsandbox/sandboxes/<name>/logs/exec.log:
jsonl
{"t":"2026-09-12T09:14:08.071Z","s":"output","d":"e","id":3}
{"t":"2026-09-12T09:14:08.173Z","s":"output","d":"c","id":3}
{"t":"2026-09-12T09:14:08.215Z","s":"output","d":"h","id":3}
{"t":"2026-09-12T09:14:08.428Z","s":"output","d":"o","id":3}
{"t":"2026-09-12T09:14:08.590Z","s":"output","d":" ","id":3}
{"t":"2026-09-12T09:14:09.057Z","s":"output","d":"s","id":3}
{"t":"2026-09-12T09:14:09.357Z","s":"output","d":"e","id":3}
{"t":"2026-09-12T09:14:09.446Z","s":"output","d":"c","id":3}
{"t":"2026-09-12T09:14:09.784Z","s":"output","d":"r","id":3}
{"t":"2026-09-12T09:14:11.398Z","s":"output","d":"e","id":3}
{"t":"2026-09-12T09:14:11.676Z","s":"output","d":"t","id":3}

Each echoed keystroke is its own JSON line and its own write, and full output (prompts, command results) follows the same path.

Where it happens (v0.6.18, fa3e439)

main as of 2026-09-11 (74b8db2f) is the same.

Docs still describe the old primary-session model

#650 says the capture-all model is intentional ("the earlier first-session-wins design is gone"). Several doc comments still say only the primary session is captured, which is how we first misread the behaviour:

  • exec_log.rs module doc: "Only the primary session — the first exec session opened against the sandbox per lifetime — feeds this file" (L12-L14)
  • relay.rs doc comments at L107, L244, L677 and L771-L774. The last one mentions a primary_session_id CAS that doesn't exist in the code.
  • vm.rs L660

Proposal

Any one of these would solve it for us. They're listed from the smallest change to the most flexible:

  1. Sandbox-level switch, e.g. SandboxBuilder::exec_log(false) / msb run --no-exec-log. The runtime then skips LogWriter::open, and, as the existing comment at relay.rs L845-847 notes, the CBOR decode is skipped too when there's no writer.
  2. Per-exec opt-out, e.g. a capture: bool on the exec options / ExecRequest, checked when the session is registered. This keeps the default workload's output in exec.log (useful for msb logs) while interactive sessions stay off disk.
  3. Default: don't capture tty: true sessions, or make that the behaviour of a switch. This one is debatable, since pty capture was deliberately added (LogSource::Output), so we'd understand if you prefer 1 or 2.

Independently of the above, the stale doc comments should be updated to describe capture-all.

Thanks for microsandbox! The rest of the logging design (rotation-aware readers, typed sources, boot-error.json) has been very pleasant to build on.

Source: superradcompany/microsandbox