---
title: Execution
"og:title": Rust SDK Execution reference
description: Run sandbox commands and stream their output with the Rust SDK.
keywords: ["Rust SDK", "Rust execution", "microsandbox execution"]
---

See [Commands](/sandboxes/commands) for usage examples.

## Sandbox

#### <span className="msb-recv">sb.</span><span className="msb-hn">exec()</span>

```rust
async fn exec(
    &self,
    cmd: impl Into<String>,
    args: impl IntoIterator<Item = impl Into<String>>,
) -> MicrosandboxResult<ExecOutput>
```

<Accordion title="Example">

```rust
let out = sb.exec("python", ["-V"]).await?;
println!("{}", out.stdout()?);
```

</Accordion>

Run a command inside the sandbox and wait for it to complete. Collects all stdout and stderr into memory and returns them along with the exit status. `cmd` is passed literally to the guest agent: the image ENTRYPOINT is **not** consulted, and `args` are not shell-interpreted. For long-running processes or large output, use [`exec_stream()`](#sb-exec_stream); for shell syntax like pipes and redirects, use [`shell()`](#sb-shell).

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>cmd</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Command to execute, e.g. <code>"python"</code> or <code>"/usr/bin/node"</code>.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>args</code><span className="msb-type">impl IntoIterator</span></div>
    <div className="msb-param-desc">Command arguments, e.g. <code>["-c", "print('hi')"]</code>.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#execoutput">ExecOutput</a></div>
    <div className="msb-param-desc">Collected stdout, stderr, and exit status.</div>
  </div>
</div>

#### <span className="msb-recv">sb.</span><span className="msb-hn">exec_with()</span>

```rust
async fn exec_with(
    &self,
    cmd: impl Into<String>,
    f: impl FnOnce(ExecOptionsBuilder) -> ExecOptionsBuilder,
) -> MicrosandboxResult<ExecOutput>
```

<Accordion title="Example">

```rust
use std::time::Duration;

let out = sb.exec_with("python", |e| e
    .args(["compute.py"])
    .cwd("/app")
    .env("LOG_LEVEL", "debug")
    .timeout(Duration::from_secs(30)))
    .await?;
```

</Accordion>

Run a command with per-execution overrides and wait for completion. The closure receives an [`ExecOptionsBuilder`](#execoptionsbuilder) to set args, working directory, environment variables, user, timeout, resource limits, stdin mode, and TTY allocation. These overrides apply only to this execution and don't change the sandbox's defaults.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>cmd</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Command to execute.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>f</code><a className="msb-type" href="#execoptionsbuilder">FnOnce(ExecOptionsBuilder)</a></div>
    <div className="msb-param-desc">Configure execution options.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#execoutput">ExecOutput</a></div>
    <div className="msb-param-desc">Collected stdout, stderr, and exit status.</div>
  </div>
</div>

#### <span className="msb-recv">sb.</span><span className="msb-hn">shell()</span>

```rust
async fn shell(&self, script: impl Into<String>) -> MicrosandboxResult<ExecOutput>
```

<Accordion title="Example">

```rust
let out = sb.shell("cat /etc/os-release | grep PRETTY_NAME").await?;
println!("{}", out.stdout()?);
```

</Accordion>

Run a command through the sandbox's configured shell (default: `/bin/sh`, set via [`SandboxBuilder::shell()`](/sdk/rust/sandbox#sandbox-shell)). The script is run as `<shell> -c "<script>"`, so shell syntax like pipes, redirects, and `&&` chains works.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>script</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Shell command string, e.g. <code>"ls -la /app && echo done"</code>.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#execoutput">ExecOutput</a></div>
    <div className="msb-param-desc">Collected stdout, stderr, and exit status.</div>
  </div>
</div>

#### <span className="msb-recv">sb.</span><span className="msb-hn">shell_with()</span>

```rust
async fn shell_with(
    &self,
    script: impl Into<String>,
    f: impl FnOnce(ExecOptionsBuilder) -> ExecOptionsBuilder,
) -> MicrosandboxResult<ExecOutput>
```

<Accordion title="Example">

```rust
let out = sb.shell_with("env | sort", |e| e.env("STAGE", "build")).await?;
```

</Accordion>

Run a shell command with per-execution overrides and wait for completion. The `-c <script>` arguments are prepended to whatever the [`ExecOptionsBuilder`](#execoptionsbuilder) configures, so use the builder for env, cwd, user, timeout, and resource limits rather than for positional args.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>script</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Shell command string.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>f</code><a className="msb-type" href="#execoptionsbuilder">FnOnce(ExecOptionsBuilder)</a></div>
    <div className="msb-param-desc">Configure execution options.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#execoutput">ExecOutput</a></div>
    <div className="msb-param-desc">Collected stdout, stderr, and exit status.</div>
  </div>
</div>

#### <span className="msb-recv">sb.</span><span className="msb-hn">exec_stream()</span>

```rust
async fn exec_stream(
    &self,
    cmd: impl Into<String>,
    args: impl IntoIterator<Item = impl Into<String>>,
) -> MicrosandboxResult<ExecHandle>
```

<Accordion title="Example">

```rust
use microsandbox::ExecEvent;

let mut handle = sb.exec_stream("tail", ["-f", "/var/log/app.log"]).await?;
while let Some(event) = handle.recv().await {
    match event {
        ExecEvent::Stdout(chunk) => print!("{}", String::from_utf8_lossy(&chunk)),
        ExecEvent::Exited { code } => { println!("exited {code}"); break; }
        _ => {}
    }
}
```

</Accordion>

Run a command with streaming output. Returns an [`ExecHandle`](#exechandle) that emits stdout, stderr, started, and exit events as they happen, rather than buffering everything until the command finishes. Use this for long-running processes, large output, or when you need to process output incrementally.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>cmd</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Command to execute.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>args</code><span className="msb-type">impl IntoIterator</span></div>
    <div className="msb-param-desc">Command arguments.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#exechandle">ExecHandle</a></div>
    <div className="msb-param-desc">Streaming handle for receiving events and controlling the process.</div>
  </div>
</div>

#### <span className="msb-recv">sb.</span><span className="msb-hn">exec_stream_with()</span>

```rust
async fn exec_stream_with(
    &self,
    cmd: impl Into<String>,
    f: impl FnOnce(ExecOptionsBuilder) -> ExecOptionsBuilder,
) -> MicrosandboxResult<ExecHandle>
```

<Accordion title="Example">

```rust
let mut handle = sb.exec_stream_with("python", |e| e.stdin_pipe().tty(true)).await?;
if let Some(stdin) = handle.take_stdin() {
    stdin.write(b"print(2 + 2)\n").await?;
    stdin.close().await?;
}
```

</Accordion>

Streaming execution with per-execution overrides. Enable [`stdin_pipe()`](#exec_options-stdin_pipe) to write to the process's stdin via [`ExecHandle::take_stdin()`](#exechandle), and [`tty(true)`](#exec_options-tty) to allocate a pseudo-terminal for interactive programs like shells, REPLs, or editors.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>cmd</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Command to execute.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>f</code><a className="msb-type" href="#execoptionsbuilder">FnOnce(ExecOptionsBuilder)</a></div>
    <div className="msb-param-desc">Configure execution options.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#exechandle">ExecHandle</a></div>
    <div className="msb-param-desc">Streaming handle.</div>
  </div>
</div>

#### <span className="msb-recv">sb.</span><span className="msb-hn">shell_stream()</span>

```rust
async fn shell_stream(&self, script: impl Into<String>) -> MicrosandboxResult<ExecHandle>
```

<Accordion title="Example">

```rust
let mut handle = sb.shell_stream("for i in 1 2 3; do echo $i; sleep 1; done").await?;
let out = handle.collect().await?;
```

</Accordion>

Like [`shell()`](#sb-shell), but returns a streaming [`ExecHandle`](#exechandle) instead of waiting for completion.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>script</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Shell command string.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#exechandle">ExecHandle</a></div>
    <div className="msb-param-desc">Streaming handle.</div>
  </div>
</div>

#### <span className="msb-recv">sb.</span><span className="msb-hn">shell_stream_with()</span>

```rust
async fn shell_stream_with(
    &self,
    script: impl Into<String>,
    f: impl FnOnce(ExecOptionsBuilder) -> ExecOptionsBuilder,
) -> MicrosandboxResult<ExecHandle>
```

<Accordion title="Example">

```rust
let mut handle = sb.shell_stream_with("npm run build", |e| e.cwd("/app")).await?;
```

</Accordion>

Run a shell command with per-execution overrides and streaming I/O. As with [`shell_with()`](#sb-shell_with), the `-c <script>` arguments are prepended to whatever the builder configures.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>script</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Shell command string.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>f</code><a className="msb-type" href="#execoptionsbuilder">FnOnce(ExecOptionsBuilder)</a></div>
    <div className="msb-param-desc">Configure execution options.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#exechandle">ExecHandle</a></div>
    <div className="msb-param-desc">Streaming handle.</div>
  </div>
</div>

<p className="msb-member-group">Attach methods</p>

#### <span className="msb-recv">sb.</span><span className="msb-hn">attach()</span>

```rust
async fn attach(
    &self,
    cmd: impl Into<String>,
    args: impl IntoIterator<Item = impl Into<String>>,
) -> MicrosandboxResult<i32>
```

<Accordion title="Example">

```rust
let exit_code = sb.attach("bash", ["-l"]).await?;
```

</Accordion>

Bridge your terminal directly to a process inside the sandbox for a fully interactive PTY session. The host terminal is put into raw mode and its stdin, stdout, and window-size changes are wired to the guest process. Press the detach key (default `Ctrl+]`) to disconnect without stopping the process; it keeps running in the guest. Returns when the process exits or you detach.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>cmd</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Command to run.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>args</code><span className="msb-type">impl IntoIterator</span></div>
    <div className="msb-param-desc">Command arguments.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><span className="msb-type">i32</span></div>
    <div className="msb-param-desc">Exit code of the process, or <code>-1</code> if you detached before it exited.</div>
  </div>
</div>

#### <span className="msb-recv">sb.</span><span className="msb-hn">attach_with()</span>

```rust
async fn attach_with(
    &self,
    cmd: impl Into<String>,
    f: impl FnOnce(AttachOptionsBuilder) -> AttachOptionsBuilder,
) -> MicrosandboxResult<i32>
```

<Accordion title="Example">

```rust
let exit_code = sb.attach_with("zsh", |a| a
    .env("TERM", "xterm-256color")
    .detach_keys("ctrl-p,ctrl-q"))
    .await?;
```

</Accordion>

Interactive PTY attach with options. The closure receives an [`AttachOptionsBuilder`](#attachoptionsbuilder) to set args, environment variables, working directory, user, custom detach keys, and resource limits.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>cmd</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Command to run.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>f</code><a className="msb-type" href="#attachoptionsbuilder">FnOnce(AttachOptionsBuilder)</a></div>
    <div className="msb-param-desc">Configure attach options.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><span className="msb-type">i32</span></div>
    <div className="msb-param-desc">Exit code of the process, or <code>-1</code> if you detached.</div>
  </div>
</div>

#### <span className="msb-recv">sb.</span><span className="msb-hn">attach_shell()</span>

```rust
async fn attach_shell(&self) -> MicrosandboxResult<i32>
```

<Accordion title="Example">

```rust
let exit_code = sb.attach_shell().await?;
```

</Accordion>

Attach to the sandbox's default shell (configured via [`SandboxBuilder::shell()`](/sdk/rust/sandbox#sandbox-shell), default `/bin/sh`) with an interactive PTY session.

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><span className="msb-type">i32</span></div>
    <div className="msb-param-desc">Exit code, or <code>-1</code> if you detached.</div>
  </div>
</div>

## ExecOptionsBuilder

Builder for per-execution overrides passed to [`exec_with()`](#sb-exec_with), [`exec_stream_with()`](#sb-exec_stream_with), [`shell_with()`](#sb-shell_with), and [`shell_stream_with()`](#sb-shell_stream_with). Does not change the sandbox's defaults. Every setter returns `Self`, so calls chain.

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">arg()</span>

```rust
fn arg(self, arg: impl Into<String>) -> Self
```

Append a single command-line argument, e.g. `"-la"` or `"/tmp"`.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>arg</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Argument to append.</div>
  </div>
</div>

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">args()</span>

```rust
fn args(self, args: impl IntoIterator<Item = impl Into<String>>) -> Self
```

Append multiple command-line arguments.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>args</code><span className="msb-type">impl IntoIterator</span></div>
    <div className="msb-param-desc">Arguments to append.</div>
  </div>
</div>

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">cwd()</span>

```rust
fn cwd(self, cwd: impl Into<String>) -> Self
```

Override the working directory for this command. Overrides the sandbox default set via the builder's `workdir`.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>cwd</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Absolute path inside the guest.</div>
  </div>
</div>

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">user()</span>

```rust
fn user(self, user: impl Into<String>) -> Self
```

Override the guest user for this command.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>user</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">User name or UID.</div>
  </div>
</div>

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">env()</span>

```rust
fn env(self, key: impl Into<String>, value: impl Into<String>) -> Self
```

Set an environment variable for this command. Merged on top of the sandbox-level env vars.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>key</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Variable name.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>value</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Variable value.</div>
  </div>
</div>

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">envs()</span>

```rust
fn envs(
    self,
    vars: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
) -> Self
```

Set multiple environment variables for this command at once.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>vars</code><span className="msb-type">impl IntoIterator</span></div>
    <div className="msb-param-desc">Key-value pairs to set.</div>
  </div>
</div>

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">timeout()</span>

```rust
fn timeout(self, timeout: Duration) -> Self
```

Kill the process with SIGKILL if it hasn't exited within this duration.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>timeout</code><span className="msb-type">Duration</span></div>
    <div className="msb-param-desc">Maximum run time before SIGKILL.</div>
  </div>
</div>

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">tty()</span>

```rust
fn tty(self, enabled: bool) -> Self
```

Allocate a pseudo-terminal. Enable for interactive programs (shells, editors, `top`); disable for scripts and batch jobs. When enabled, stdout and stderr are merged at the kernel level inside the guest. Default: `false`.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>enabled</code><span className="msb-type">bool</span></div>
    <div className="msb-param-desc"><code>true</code> to allocate a PTY.</div>
  </div>
</div>

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">stdin_null()</span>

```rust
fn stdin_null(self) -> Self
```

Stdin reads from `/dev/null`. This is the default.

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">stdin_pipe()</span>

```rust
fn stdin_pipe(self) -> Self
```

Enable a stdin writer via [`ExecSink`](#execsink). Use with [`ExecHandle::take_stdin()`](#exechandle) on the returned streaming handle to send data to the process.

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">stdin_bytes()</span>

```rust
fn stdin_bytes(self, data: impl Into<Vec<u8>>) -> Self
```

Provide fixed bytes as stdin. The process reads them and then sees EOF.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>data</code><span className="msb-type">impl Into&lt;Vec&lt;u8&gt;&gt;</span></div>
    <div className="msb-param-desc">Bytes fed to the process's stdin.</div>
  </div>
</div>

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">rlimit()</span>

```rust
fn rlimit(self, resource: RlimitResource, limit: u64) -> Self
```

<Accordion title="Example">

```rust
use microsandbox::sandbox::RlimitResource;

let out = sb.exec_with("./worker", |e| e.rlimit(RlimitResource::Nofile, 1024)).await?;
```

</Accordion>

Set a POSIX resource limit with soft equal to hard. Applied via `setrlimit()` before exec.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>resource</code><a className="msb-type" href="#rlimitresource">RlimitResource</a></div>
    <div className="msb-param-desc">Which resource to limit.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>limit</code><span className="msb-type">u64</span></div>
    <div className="msb-param-desc">Limit value (soft = hard).</div>
  </div>
</div>

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">rlimit_range()</span>

```rust
fn rlimit_range(self, resource: RlimitResource, soft: u64, hard: u64) -> Self
```

Set a resource limit with different soft and hard values. [`build()`](#exec_options-build) errors if `soft > hard`.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>resource</code><a className="msb-type" href="#rlimitresource">RlimitResource</a></div>
    <div className="msb-param-desc">Which resource to limit.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>soft</code><span className="msb-type">u64</span></div>
    <div className="msb-param-desc">Soft limit (raisable by the process up to hard).</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>hard</code><span className="msb-type">u64</span></div>
    <div className="msb-param-desc">Hard ceiling.</div>
  </div>
</div>

#### <span className="msb-recv">exec_options.</span><span className="msb-hn">build()</span>

```rust
fn build(self) -> MicrosandboxResult<ExecOptions>
```

Finalize the options. Called automatically by the `*_with` methods when you use the closure form. Returns an error if any rlimit has `soft > hard`.

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><span className="msb-type">ExecOptions</span></div>
    <div className="msb-param-desc">Validated execution options.</div>
  </div>
</div>

## AttachOptionsBuilder

Builder for interactive attach options passed to [`attach_with()`](#sb-attach_with). Every setter returns `Self`, so calls chain.

#### <span className="msb-recv">attach_options.</span><span className="msb-hn">arg()</span>

```rust
fn arg(self, arg: impl Into<String>) -> Self
```

Append a single command-line argument to the attached command.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>arg</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Argument to append.</div>
  </div>
</div>

#### <span className="msb-recv">attach_options.</span><span className="msb-hn">args()</span>

```rust
fn args(self, args: impl IntoIterator<Item = impl Into<String>>) -> Self
```

Append multiple command-line arguments.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>args</code><span className="msb-type">impl IntoIterator</span></div>
    <div className="msb-param-desc">Arguments to append.</div>
  </div>
</div>

#### <span className="msb-recv">attach_options.</span><span className="msb-hn">cwd()</span>

```rust
fn cwd(self, cwd: impl Into<String>) -> Self
```

Override the working directory for the attached session.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>cwd</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Absolute path inside the guest.</div>
  </div>
</div>

#### <span className="msb-recv">attach_options.</span><span className="msb-hn">user()</span>

```rust
fn user(self, user: impl Into<String>) -> Self
```

Override the guest user for the attached session.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>user</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">User name or UID.</div>
  </div>
</div>

#### <span className="msb-recv">attach_options.</span><span className="msb-hn">env()</span>

```rust
fn env(self, key: impl Into<String>, value: impl Into<String>) -> Self
```

Set an environment variable for the attached session. Merged on top of the sandbox-level env vars.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>key</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Variable name.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>value</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Variable value.</div>
  </div>
</div>

#### <span className="msb-recv">attach_options.</span><span className="msb-hn">envs()</span>

```rust
fn envs(
    self,
    vars: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
) -> Self
```

Set multiple environment variables for the attached session at once.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>vars</code><span className="msb-type">impl IntoIterator</span></div>
    <div className="msb-param-desc">Key-value pairs to set.</div>
  </div>
</div>

#### <span className="msb-recv">attach_options.</span><span className="msb-hn">detach_keys()</span>

```rust
fn detach_keys(self, keys: impl Into<String>) -> Self
```

Set the key sequence that detaches from the session without stopping the process. Docker-style syntax: `"ctrl-]"` (default), `"ctrl-p,ctrl-q"` for a multi-key sequence, or a single character like `"q"`.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>keys</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Detach key specification.</div>
  </div>
</div>

#### <span className="msb-recv">attach_options.</span><span className="msb-hn">rlimit()</span>

```rust
fn rlimit(self, resource: RlimitResource, limit: u64) -> Self
```

Set a POSIX resource limit with soft equal to hard for the attached process.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>resource</code><a className="msb-type" href="#rlimitresource">RlimitResource</a></div>
    <div className="msb-param-desc">Which resource to limit.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>limit</code><span className="msb-type">u64</span></div>
    <div className="msb-param-desc">Limit value (soft = hard).</div>
  </div>
</div>

#### <span className="msb-recv">attach_options.</span><span className="msb-hn">rlimit_range()</span>

```rust
fn rlimit_range(self, resource: RlimitResource, soft: u64, hard: u64) -> Self
```

Set a resource limit with different soft and hard values for the attached process. [`build()`](#attach_options-build) errors if `soft > hard`.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>resource</code><a className="msb-type" href="#rlimitresource">RlimitResource</a></div>
    <div className="msb-param-desc">Which resource to limit.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>soft</code><span className="msb-type">u64</span></div>
    <div className="msb-param-desc">Soft limit.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>hard</code><span className="msb-type">u64</span></div>
    <div className="msb-param-desc">Hard ceiling.</div>
  </div>
</div>

#### <span className="msb-recv">attach_options.</span><span className="msb-hn">build()</span>

```rust
fn build(self) -> MicrosandboxResult<AttachOptions>
```

Finalize the options. Called automatically by [`attach_with()`](#sb-attach_with). Returns an error if any rlimit has `soft > hard`.

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><span className="msb-type">AttachOptions</span></div>
    <div className="msb-param-desc">Validated attach options.</div>
  </div>
</div>

## ExecHandle


<p className="msb-backref">Returned by <a href="#sb-exec_stream">exec_stream()</a> · <a href="#sb-exec_stream_with">exec_stream_with()</a> · <a href="#sb-shell_stream">shell_stream()</a> · <a href="#sb-shell_stream_with">shell_stream_with()</a></p>

A handle to a running streaming execution. Receives [`ExecEvent`](#execevent)s as the process produces output, and provides control over stdin, signals, and the PTY size.

A configured streaming timeout is enforced by the SDK even when the caller is not reading output. At expiry, the SDK waits for process startup before sending SIGKILL. Waiting or collecting reports `ExecTimeout` after the guest confirms exit; cancelling a pending wait does not cancel the deadline. Raw event readers receive the actual exit event. If termination cannot be delivered, the SDK reports a separate error without claiming the process exited. The SDK connection must remain open for this client-owned deadline to run.

#### <span className="msb-recv">h.</span><span className="msb-hn">recv()</span>

```rust
recv()
```

Receive the next event. `None` when the session has ended and all output has been delivered.

<p className="msb-label">Returns</p>

`Option<`[`ExecEvent`](#execevent)`>`

#### <span className="msb-recv">h.</span><span className="msb-hn">wait()</span>

```rust
wait()
```

Wait for the process to exit, discarding any remaining output.

<p className="msb-label">Returns</p>

`Result<`[`ExitStatus`](#exitstatus)`>`

#### <span className="msb-recv">h.</span><span className="msb-hn">collect()</span>

```rust
collect()
```

Wait for exit and collect all remaining stdout/stderr.

<p className="msb-label">Returns</p>

`Result<`[`ExecOutput`](#execoutput)`>`

#### <span className="msb-recv">h.</span><span className="msb-hn">id()</span>

```rust
id()
```

Session ID for this execution. Can be used to reattach later.

<p className="msb-label">Returns</p>

`String`

#### <span className="msb-recv">h.</span><span className="msb-hn">control()</span>

```rust
control()
```

A cloneable control handle for sending signals and resizes from another task.

<p className="msb-label">Returns</p>

[`ExecControl`](#execcontrol)

#### <span className="msb-recv">h.</span><span className="msb-hn">take_stdin()</span>

```rust
take_stdin()
```

Take the stdin writer. Only available if `stdin_pipe()` was set; returns `None` after the first call.

<p className="msb-label">Returns</p>

`Option<`[`ExecSink`](#execsink)`>`

#### <span className="msb-recv">h.</span><span className="msb-hn">signal()</span>

```rust
signal(signal)
```

Send a POSIX signal to the process (e.g. `libc::SIGTERM`).

<p className="msb-label">Returns</p>

`Result<()>`

#### <span className="msb-recv">h.</span><span className="msb-hn">kill()</span>

```rust
kill()
```

Send SIGKILL to the process.

<p className="msb-label">Returns</p>

`Result<()>`

#### <span className="msb-recv">h.</span><span className="msb-hn">resize()</span>

```rust
resize(rows, cols)
```

Resize the PTY for this session.

<p className="msb-label">Returns</p>

`Result<()>`

## ExecControl


<p className="msb-backref">Returned by <a href="#exechandle">ExecHandle.control()</a></p>

A cloneable, lightweight control handle for a streaming exec session. Lets a task other than the one owning the [`ExecHandle`](#exechandle) send signals and PTY resizes. Carries no event stream.


#### <span className="msb-recv">control.</span><span className="msb-hn">id()</span>

```rust
id()
```

Session ID for this execution.

<p className="msb-label">Returns</p>

`String`

#### <span className="msb-recv">control.</span><span className="msb-hn">signal()</span>

```rust
signal(signal)
```

Send a POSIX signal to the process (e.g. `libc::SIGTERM`).

<p className="msb-label">Returns</p>

`Result<()>`

#### <span className="msb-recv">control.</span><span className="msb-hn">kill()</span>

```rust
kill()
```

Send SIGKILL to the process.

<p className="msb-label">Returns</p>

`Result<()>`

#### <span className="msb-recv">control.</span><span className="msb-hn">resize()</span>

```rust
resize(rows, cols)
```

Resize the PTY for this session.

<p className="msb-label">Returns</p>

`Result<()>`

## ExecSink


<p className="msb-backref">Returned by <a href="#exechandle">ExecHandle.take_stdin()</a></p>

A writer for sending data to a running process's stdin. Obtained via [`ExecHandle::take_stdin()`](#exechandle) after enabling [`stdin_pipe()`](#exec_options-stdin_pipe).


#### <span className="msb-recv">sink.</span><span className="msb-hn">write()</span>

```rust
write(data)
```

Write bytes to the process's stdin. The write completes once the guest accepts the data, so it waits while the command is slow to read. For a command that also produces output, read output concurrently to avoid blocking each other. Writing empty data does nothing. Call [`close()`](#sink-close) when done writing; in pipe mode it sends EOF.

#### <span className="msb-recv">sink.</span><span className="msb-hn">close()</span>

```rust
close()
```

Close stdin. The process sees EOF on its stdin.

## ExecOutput


<p className="msb-backref">Returned by <a href="#sb-exec">exec()</a> · <a href="#sb-exec_with">exec_with()</a> · <a href="#sb-shell">shell()</a> · <a href="#sb-shell_with">shell_with()</a> · <a href="#exechandle">ExecHandle.collect()</a></p>

The result of a completed command execution. Holds the exit status and all captured output.


#### <span className="msb-recv">out.</span><span className="msb-hn">status()</span>

```rust
status()
```

Exit code and success flag.

<p className="msb-label">Returns</p>

[`ExitStatus`](#exitstatus)

#### <span className="msb-recv">out.</span><span className="msb-hn">stdout()</span>

```rust
stdout()
```

Collected stdout decoded as UTF-8. Errors if the output is not valid UTF-8.

<p className="msb-label">Returns</p>

`Result<String, FromUtf8Error>`

#### <span className="msb-recv">out.</span><span className="msb-hn">stderr()</span>

```rust
stderr()
```

Collected stderr decoded as UTF-8.

<p className="msb-label">Returns</p>

`Result<String, FromUtf8Error>`

#### <span className="msb-recv">out.</span><span className="msb-hn">stdout_bytes()</span>

```rust
stdout_bytes()
```

Raw stdout bytes without decoding.

<p className="msb-label">Returns</p>

`&Bytes`

#### <span className="msb-recv">out.</span><span className="msb-hn">stderr_bytes()</span>

```rust
stderr_bytes()
```

Raw stderr bytes without decoding.

<p className="msb-label">Returns</p>

`&Bytes`

## Types

### ExecEvent

<p className="msb-backref">Yielded by <a href="#exechandle">ExecHandle.recv()</a></p>

An event emitted by a streaming execution.

| Variant | Fields | Description |
|---------|--------|-------------|
| `Started` | `pid: u32` | The process has started. `pid` is the guest-side PID. |
| `Stdout` | `Bytes` | A chunk of stdout data. May arrive in arbitrary sizes. |
| `Stderr` | `Bytes` | A chunk of stderr data. |
| `Exited` | `code: i32` | The process exited normally. `code` is the exit code. |
| `Failed` | `ExecFailed` | The process failed to spawn (binary not found, permission denied, etc.). The user code never ran. Terminal: no further events follow. |
| `StdinError` | `ExecStdinError` | A stdin write to the child failed (e.g. broken pipe). Non-terminal: the session keeps running and may still emit output and an `Exited` event. |

### ExitStatus

<p className="msb-backref">Returned by <a href="#exechandle">ExecHandle.wait()</a> · <a href="#execoutput">ExecOutput.status()</a> · <a href="/sdk/rust/sandbox#sb-wait">sb.wait()</a> · <a href="/sdk/rust/sandbox#sb-stop_and_wait">sb.stop_and_wait()</a></p>

The exit status of a completed process.

| Field | Type | Description |
|-------|------|-------------|
| code | `i32` | Exit code. `0` typically means success. |
| success | `bool` | `true` if `code` is `0`. |

### Rlimit

<p className="msb-backref">Configured via <a href="#rlimit">rlimit()</a> · <a href="#exec_options-rlimit_range">rlimit_range()</a></p>

A POSIX resource limit. Built indirectly by [`rlimit()`](#rlimit) and [`rlimit_range()`](#exec_options-rlimit_range) on the option builders; you rarely construct it by hand.

| Field | Type | Description |
|-------|------|-------------|
| resource | [`RlimitResource`](#rlimitresource) | Which resource to limit. |
| soft | `u64` | Soft limit; the process may raise it up to `hard`. |
| hard | `u64` | Hard ceiling; raising it requires privileges. |

### RlimitResource

<p className="msb-backref">Used by <a href="#rlimit">rlimit()</a> · <a href="#exec_options-rlimit_range">rlimit_range()</a> · <a href="#attach_options-rlimit">AttachOptionsBuilder.rlimit()</a></p>

POSIX resource limit identifiers. Each maps to an `RLIMIT_*` constant.

| Value | Description |
|-------|-------------|
| `Cpu` | Max CPU time in seconds (`RLIMIT_CPU`) |
| `Fsize` | Max file size in bytes (`RLIMIT_FSIZE`) |
| `Data` | Max data segment size (`RLIMIT_DATA`) |
| `Stack` | Max stack size (`RLIMIT_STACK`) |
| `Core` | Max core file size (`RLIMIT_CORE`) |
| `Rss` | Max resident set size (`RLIMIT_RSS`) |
| `Nproc` | Max number of processes (`RLIMIT_NPROC`) |
| `Nofile` | Max open file descriptors (`RLIMIT_NOFILE`) |
| `Memlock` | Max locked memory (`RLIMIT_MEMLOCK`) |
| `As` | Max address space size (`RLIMIT_AS`) |
| `Locks` | Max file locks (`RLIMIT_LOCKS`) |
| `Sigpending` | Max pending signals (`RLIMIT_SIGPENDING`) |
| `Msgqueue` | Max bytes in POSIX message queues (`RLIMIT_MSGQUEUE`) |
| `Nice` | Max nice priority (`RLIMIT_NICE`) |
| `Rtprio` | Max real-time priority (`RLIMIT_RTPRIO`) |
| `Rttime` | Max real-time timeout (`RLIMIT_RTTIME`) |

### ExecFailureKind

The `kind` field of a structured `ExecFailed` payload. See [spawn-time failures](/sdk/errors#spawn-time-exec-failures) for handling examples.

#### NotFound

The binary is missing from `PATH` or the explicit path.

#### PermissionDenied

The binary exists but permission to execute it was denied.

#### NotExecutable

The kernel cannot execute the file: for example, invalid ELF, a missing script interpreter, or the wrong architecture.

#### BadCwd

The working directory is missing, is not a directory, or cannot be entered.

#### BadArgs

Arguments or environment are too large, a path is too long, path resolution encounters too many symlinks, or arguments contain invalid bytes.

#### ResourceLimit

A resource limit prevented startup, such as a rejected limit, exhausted process limit, or file-descriptor exhaustion.

#### UserSetupFailed

The requested user does not exist or user/group setup failed.

#### OutOfMemory

Memory pressure prevented process creation.

#### PtySetupFailed

PTY allocation or attachment failed.

#### Other

Another startup error. Inspect `errno`, `message`, and `stage` for details.

## Managed jobs

Run and reconnect to background commands. See [Background jobs](/sandboxes/commands#background-jobs) for lifecycle, retention, and runtime limits.

#### <span className="msb-recv">sb.</span><span className="msb-hn">exec_detached()</span>

```rust
async fn exec_detached(
    &self, cmd: impl Into<String>,
    args: impl IntoIterator<Item = impl Into<String>>,
) -> JobResult<Job>
```

Start a job after confirming guest startup. Stdin defaults to a retained pipe.

#### <span className="msb-recv">sb.</span><span className="msb-hn">exec_detached_with()</span>

```rust
async fn exec_detached_with(
    &self, cmd: impl Into<String>,
    configure: impl FnOnce(ExecOptionsBuilder) -> ExecOptionsBuilder,
) -> JobResult<Job>
```

Configure launch with `ExecOptionsBuilder`. `stdin_null()` closes input; `stdin_bytes()` sends at most 64 KiB followed by EOF.

<Accordion title="Example">

```rust
use microsandbox::jobs::JobLogOptions;

let job = sb.exec_detached_with("cat", |e| e.cwd("/tmp")).await?;
let id = job.id().to_string();
let io = job.attach().await?;
io.write_stdin(b"hello\n").await?;
io.detach().await?;

let job = sb.get_job(&id).await?;
job.eof().await?;
let exit = job.wait().await?;
let output = job.logs(&JobLogOptions::default()).await?;
let page = sb.list_jobs_with(|b| b.all(true).limit(50)).await?;
```

</Accordion>

#### <span className="msb-recv">sb.</span><span className="msb-hn">get_job()</span>

```rust
async fn get_job(&self, id: impl AsRef<str>) -> JobResult<Job>
```

Retrieve a retained job by ID. Also available on `SandboxHandle`.

#### <span className="msb-recv">sb.</span><span className="msb-hn">list_jobs()</span>

```rust
async fn list_jobs(&self) -> JobResult<JobPage>
```

List up to 50 active jobs. Returns items and a next-page cursor. Use `list_jobs_with()` to include retained history or change the page size. Also available on `SandboxHandle`.

#### <span className="msb-recv">sb.</span><span className="msb-hn">list_jobs_with()</span>

```rust
async fn list_jobs_with(
    &self, configure: impl FnOnce(JobListBuilder) -> JobListBuilder,
) -> JobResult<JobPage>
```

Configure a page with `JobListBuilder`. Also available on `SandboxHandle`.

### Job

#### <span className="msb-recv">job.</span><span className="msb-hn">id()</span>

```rust
fn id(&self) -> &JobId
```

The job ID within its sandbox.

#### <span className="msb-recv">job.</span><span className="msb-hn">inspect()</span>

```rust
async fn inspect(&self) -> JobResult<JobInfo>
```

Read current metadata without starting a stopped sandbox.

#### <span className="msb-recv">job.</span><span className="msb-hn">wait()</span>

```rust
async fn wait(&self) -> JobResult<JobExit>
```

Wait for a confirmed exit without consuming output. The result contains the exit code, success flag, and timeout flag. Cancelling the future leaves the job running.

#### <span className="msb-recv">job.</span><span className="msb-hn">signal()</span>

```rust
async fn signal(&self, signal: i32) -> JobResult<()>
```

Send a Linux signal to the process group.

#### <span className="msb-recv">job.</span><span className="msb-hn">kill()</span>

```rust
async fn kill(&self) -> JobResult<()>
```

Request SIGKILL. Wait separately to confirm exit.

#### <span className="msb-recv">job.</span><span className="msb-hn">eof()</span>

```rust
async fn eof(&self) -> JobResult<()>
```

Permanently close pipe input after accepted bytes. PTYs reject this operation; use terminal control characters instead.

#### <span className="msb-recv">job.</span><span className="msb-hn">logs()</span>

```rust
async fn logs(&self, options: &JobLogOptions) -> JobResult<Vec<JobLogEntry>>
```

Read retained output. Entries contain original bytes, a source, a Unix-millisecond timestamp, and an opaque cursor belonging to this job.

#### <span className="msb-recv">job.</span><span className="msb-hn">log_stream()</span>

```rust
async fn log_stream(&self, options: &JobLogOptions) -> JobResult<JobLogStream>
```

Read retained output and optionally follow new records. The stream yields `JobResult<JobLogEntry>`; drop it when finished.

#### <span className="msb-recv">job.</span><span className="msb-hn">follow_logs()</span>

```rust
async fn follow_logs(&self, options: &JobLogOptions) -> JobResult<JobLogStream>
```

Stream logs with following enabled.

#### <span className="msb-recv">job.</span><span className="msb-hn">attach()</span>

```rust
async fn attach(&self) -> JobResult<JobAttachment>
```

Attach with input ownership and no replay.

#### <span className="msb-recv">job.</span><span className="msb-hn">attach_with()</span>

```rust
async fn attach_with(
    &self, configure: impl FnOnce(JobAttachOptionsBuilder) -> JobAttachOptionsBuilder,
) -> JobResult<JobAttachment>
```

Attach to live I/O. Only one attachment may own input; read-only observers may coexist. Recent-byte replay and cursor replay are alternatives.

### JobAttachment

#### <span className="msb-recv">attachment.</span><span className="msb-hn">recv()</span>

```rust
async fn recv(&self) -> JobResult<Option<JobEvent>>
```

Read an `Output(JobLogEntry)`, `Gap { cursor }`, or `Completed(JobInfo)` event. Only one receiver may run at a time; input writes may run concurrently.

#### <span className="msb-recv">attachment.</span><span className="msb-hn">write_stdin()</span>

```rust
async fn write_stdin(&self, data: impl AsRef<[u8]>) -> JobResult<()>
```

Write up to 16 KiB through the input-owning attachment. Cancelling this future does not kill the job.

#### <span className="msb-recv">attachment.</span><span className="msb-hn">resize()</span>

```rust
async fn resize(&self, rows: u16, cols: u16) -> JobResult<()>
```

Resize the job terminal.

#### <span className="msb-recv">attachment.</span><span className="msb-hn">detach()</span>

```rust
async fn detach(&self) -> JobResult<()>
```

Release the attachment. Detaching or dropping it leaves the job and its input open.

#### <span className="msb-recv">attachment.</span><span className="msb-hn">interact_terminal()</span>

```rust
async fn interact_terminal(&self) -> JobResult<Option<i32>>
```

Connect the host terminal, handle resizing, and restore raw mode on exit. Requires terminal stdin. Returns an exit code on completion or `None` on detach. See [interrupted input](/sandboxes/commands#background-jobs) for Ctrl-], the 1 MiB lookahead limit, two-second drain, and `input_not_flushed` errors.

### JobListBuilder

#### <span className="msb-recv">list.</span><span className="msb-hn">all()</span>

```rust
fn all(self, all: bool) -> Self
```

Include retained terminal records. Default: `false`.

#### <span className="msb-recv">list.</span><span className="msb-hn">limit()</span>

```rust
fn limit(self, limit: usize) -> Self
```

Set the page size, from 1 to 100. Default: 50.

#### <span className="msb-recv">list.</span><span className="msb-hn">cursor()</span>

```rust
fn cursor(self, cursor: JobId) -> Self
```

Continue after the previous page’s cursor.

### JobAttachOptionsBuilder

#### <span className="msb-recv">options.</span><span className="msb-hn">read_only()</span>

```rust
fn read_only(self, read_only: bool) -> Self
```

Observe without owning input. Default: `false`.

#### <span className="msb-recv">options.</span><span className="msb-hn">replay()</span>

```rust
fn replay(self, replay: JobReplay) -> Self
```

Choose `JobReplay::None` (default), `Recent { max_bytes }` (up to 1 MiB), or `After(String)` with a cursor from this job.

### JobLogOptions

```rust
pub struct JobLogOptions {
    pub tail: Option<usize>,
    pub since: Option<i64>,
    pub until: Option<i64>,
    pub sources: Vec<String>,
    pub from_cursor: Option<String>,
    pub follow: bool,
}
```

`tail` selects the last N filtered records. `since` is inclusive and `until` exclusive, both in Unix milliseconds. Empty `sources` selects program output. `from_cursor` resumes after a job cursor and cannot be combined with `since`. `follow` continues after retained output.
