docs/changelog/2026-05-22.mdx
Rotation-aware log streaming
Sandbox.log_stream / logStream / LogStream lands in the Rust, Node, Python, and Go SDKs, alongside read_logs for sorted snapshots. The engine merges exec.log, runtime.log, and kernel.log including their rotated siblings, and each entry carries an opaque cursor so consumers can resume after a disconnect. msb logs and msb logs -f now ride the same engine; if a follower falls behind retention it receives a typed MissedRotation error with a hint to restart instead of silently skipping records.
async for entry in sandbox.log_stream(follow=True):
print(entry.timestamp, entry.source, entry.data)
See Sandbox logs.
Raw agent client in every SDK
A new AgentClient surface lets SDK callers talk to a sandbox's agentd directly through the relay, sending and receiving framed CBOR without going through the typed sandbox API. Rust ships both a typed tier (with a validated core.ready handshake and cached ready bytes) and a raw tier; Node, Python, and Go expose the raw tier and keep CBOR encoding in userland. Useful for experimenting with new agent operations or building higher-level tooling on top of the protocol.
const client = await AgentClient.connectSandbox("dev");
const ready = client.ready();
const response = await client.request(frame);
See the TypeScript SDK reference.
Per-mount passthroughfs policies
Two explicit knobs replace the previous boolean flags on every mount. stat_virtualization chooses between Strict, Relaxed, and Off, and host_permissions chooses between Private and Mirror. Defaults stay Strict + Private, so existing sandboxes are unchanged. The new combinations make it possible to bind-mount foreign-owned read-only paths like /tmp, mirror guest chmod bits to the host inode, and create real host-visible symlinks from inside the guest. The --mount flag gains tag:host[:ro][,stat-virt=...][,host-perms=...] for opting in.
See Sandbox filesystem.
Network policy CLI consolidation
The egress and ingress flags collapse into one consistent surface. --net-default <allow|deny> sets both directions at once, *.foo.com is now accepted as a suffix shorthand inside --net-rule targets, and --no-net is sugar for --net-default deny (it now also blocks ingress, matching its name). TLD-broad patterns like *.com are rejected at parse time so they cannot widen blast radius by accident. --deny-domain and --deny-domain-suffix are removed; express the same intent with --net-rule "deny@..." plus --net-default allow.
msb run alpine \
--net-default deny \
--net-rule "allow@*.good.com,allow@host"
See the networking overview.
Configurable port bind addresses
Published ports can now bind to a specific host address rather than the implicit loopback. The CLI accepts BIND_ADDR:HOST:GUEST and /udp variants, and the Rust, Python, TypeScript, and Go SDKs gained matching bind-aware port APIs. Existing HOST:GUEST mappings continue to bind to 127.0.0.1.
msb run nginx --port 0.0.0.0:8080:80 --port 127.0.0.1:9000:9000/udp
See the networking overview.
Ergonomic --script flag
msb create --script NAME=BODY now produces a runnable script: the body is wrapped with a shebang derived from --shell (default /bin/sh), and the escapes \n, \t, \r, \\, \", \' are decoded so multiline scripts survive normal shell quoting. A new --script-raw NAME=BODY writes the body byte-for-byte for callers that bring their own shebang. --shell rejects empty, whitespace, and NUL values at parse time so a stray newline cannot inject lines into every generated script.
msb create --replace alpine --name py \
--script start='echo hello\npython3 -c "print(123)"'
msb exec py -- start
See the CLI reference.
Other features
Passthrough violation policy forwards requests containing secret placeholders without substituting the real value, scoped to a configurable list of host patterns. Useful for letting non-sensitive traffic flow to hosts that happen to share placeholder syntax. See Sandbox secrets.sandbox.exec("python3", ["script.py"], cwd="/app", env={...}, timeout=30.0) now works directly, instead of requiring an ExecOptions dict. See the Python SDK reference.100.96.0.0/11 to 172.16.0.0/12 to avoid colliding with Tailscale.msb start accepts variadic sandbox names. Start more than one sandbox in a single command.metrics.capacity in ~/.microsandbox/config.json; the public metrics API is unchanged. See Sandbox metrics.msb stop and sandbox.stop(); previously the host could tear down the VM before the guest synced ext4, occasionally losing in-flight writes across stop and start. As part of this fix, replace_with_grace is renamed to replace_with_timeout across the CLI, Rust, Python, Node, and Go SDKs, and stop_with_timeout / connect_with_timeout are now available on SandboxHandle.0755, creating those directories first when they are missing, so distro trust-store refreshes pick it up.msb logs --follow --json emits JSON Lines for the follow records, not just the initial snapshot, so streaming consumers no longer hit a parse error mid-stream.msb run ./path and other dot-prefixed inputs are now recognized as local image inputs instead of being interpreted as registry references.AllSandboxMetrics in the Go SDK reports the real uptime instead of zero.--net-rule suffix entry points now reject TLD-broad patterns like *.com at parse time, with a clear SuffixTooBroad error.