Back to Microsandbox

Execution

docs/sdk/go/execution.mdx

0.5.1036.8 KB
Original Source

Run commands inside a running sandbox, collect their output, or stream events live. See Commands for usage examples.

The exec API mirrors os/exec conventions: a non-zero exit code is not a Go error. Transport, timeout, and spawn-failure paths return an error; a program that ran and exited non-zero is a normal *ExecOutput result, inspect Success() or ExitCode().

<div className="msb-glance"> <p className="msb-gl"><span className="msb-dot instance"></span>Methods · *Sandbox<span className="msb-ct">4</span></p> <a className="msb-row" href="#sb-exec"><span className="msb-rn">sb.Exec()</span><span className="msb-rg">run a command, collect output</span></a> <a className="msb-row" href="#sb-shell"><span className="msb-rn">sb.Shell()</span><span className="msb-rg">run via /bin/sh -c</span></a> <a className="msb-row" href="#sb-execstream"><span className="msb-rn">sb.ExecStream()</span><span className="msb-rg">run with streaming events</span></a> <a className="msb-row" href="#sb-shellstream"><span className="msb-rn">sb.ShellStream()</span><span className="msb-rg">streaming shell</span></a> <p className="msb-gl"><span className="msb-dot instance"></span>Methods · *ExecHandle<span className="msb-ct">8</span></p> <a className="msb-row" href="#h-recv"><span className="msb-rn">h.Recv()</span><span className="msb-rg">next streamed event</span></a> <a className="msb-row" href="#h-collect"><span className="msb-rn">h.Collect()</span><span className="msb-rg">drain into ExecOutput</span></a> <a className="msb-row" href="#h-wait"><span className="msb-rn">h.Wait()</span><span className="msb-rg">wait for exit code</span></a> <a className="msb-row" href="#h-takestdin"><span className="msb-rn">h.TakeStdin()</span><span className="msb-rg">take the stdin pipe</span></a> <a className="msb-row" href="#h-id"><span className="msb-rn">h.ID()</span><span className="msb-rg">session correlation id</span></a> <a className="msb-row" href="#h-signal"><span className="msb-rn">h.Signal()</span><span className="msb-rg">send a Unix signal</span></a> <a className="msb-row" href="#h-kill"><span className="msb-rn">h.Kill()</span><span className="msb-rg">send SIGKILL</span></a> <a className="msb-row" href="#h-close"><span className="msb-rn">h.Close()</span><span className="msb-rg">release the handle</span></a> <p className="msb-gl"><span className="msb-dot instance"></span>Methods · *ExecOutput<span className="msb-ct">6</span></p> <a className="msb-row" href="#out-stdout"><span className="msb-rn">out.Stdout()</span><span className="msb-rg">stdout as string</span></a> <a className="msb-row" href="#out-stderr"><span className="msb-rn">out.Stderr()</span><span className="msb-rg">stderr as string</span></a> <a className="msb-row" href="#out-stdoutbytes"><span className="msb-rn">out.StdoutBytes()</span><span className="msb-rg">raw stdout bytes</span></a> <a className="msb-row" href="#out-stderrbytes"><span className="msb-rn">out.StderrBytes()</span><span className="msb-rg">raw stderr bytes</span></a> <a className="msb-row" href="#out-exitcode"><span className="msb-rn">out.ExitCode()</span><span className="msb-rg">exit code, or -1</span></a> <a className="msb-row" href="#out-success"><span className="msb-rn">out.Success()</span><span className="msb-rg">exited with code 0</span></a> <p className="msb-gl"><span className="msb-dot instance"></span>Methods · *ExecSink<span className="msb-ct">3</span></p> <a className="msb-row" href="#sink-write"><span className="msb-rn">sink.Write()</span><span className="msb-rg">write to stdin (io.Writer)</span></a> <a className="msb-row" href="#sink-writectx"><span className="msb-rn">sink.WriteCtx()</span><span className="msb-rg">write with explicit ctx</span></a> <a className="msb-row" href="#sink-close"><span className="msb-rn">sink.Close()</span><span className="msb-rg">send EOF, finalize</span></a> <p className="msb-gl"><span className="msb-dot builder"></span>Options · ExecOption<span className="msb-ct">5</span></p> <div className="msb-chiprow"> <a className="msb-chip" href="#withexeccwd">WithExecCwd()</a> <a className="msb-chip" href="#withexectimeout">WithExecTimeout()</a> <a className="msb-chip" href="#withexecstdinpipe">WithExecStdinPipe()</a> <a className="msb-chip" href="#withexecuser">WithExecUser()</a> <a className="msb-chip" href="#withexecenv">WithExecEnv()</a> </div> <p className="msb-gl"><span className="msb-dot type"></span>Types</p> <div className="msb-chiprow"> <a className="msb-typepill" href="#execoutput">ExecOutput</a> <a className="msb-typepill" href="#exechandle">ExecHandle</a> <a className="msb-typepill" href="#execsink">ExecSink</a> <a className="msb-typepill" href="#execevent">ExecEvent</a> <a className="msb-typepill" href="#execeventkind">ExecEventKind</a> <a className="msb-typepill" href="#execfailure">ExecFailure</a> <a className="msb-typepill" href="#execconfig">ExecConfig</a> <a className="msb-typepill" href="#execoption">ExecOption</a> </div> </div> <p className="msb-label" id="typical-flow">Typical flow</p>
go
import m "github.com/superradcompany/microsandbox/sdk/go"

out, err := sb.Exec(ctx, "python3", []string{"-c", "print(1 + 1)"})
if err != nil {
    return err // transport / timeout / spawn failure
}
if !out.Success() {
    log.Printf("exited %d: %s", out.ExitCode(), out.Stderr())
}
fmt.Print(out.Stdout())

Sandbox methods

The exec entry points live on *Sandbox.


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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (s *Sandbox) Exec(ctx context.Context, cmd string, args []string, opts ...ExecOption) (*ExecOutput, error)

Run a command in the sandbox and return its collected output. Blocks until the command exits. The returned error is non-nil only on transport or runtime failures; a non-zero exit code is reported via ExecOutput.ExitCode, not as an error.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the wait. The guest process may continue in the background.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>cmd</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Program to run. Passed literally to the guest agent (the image ENTRYPOINT is not consulted).</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>args</code><span className="msb-type">[]string</span></div> <div className="msb-param-desc">Command arguments. May be <code>nil</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>opts</code><a className="msb-type" href="#execoption">...ExecOption</a></div> <div className="msb-param-desc">Per-command cwd, timeout, user, and env.</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 code.</div> </div> </div> <Accordion title="Example">
go
out, err := sb.Exec(ctx, "make", []string{"build"},
    m.WithExecCwd("/app"),
    m.WithExecEnv(map[string]string{"DEBUG": "1"}),
)
</Accordion>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (s *Sandbox) Shell(ctx context.Context, command string, opts ...ExecOption) (*ExecOutput, error)

Run /bin/sh -c command in the sandbox and collect its output. Blocks until the command exits. A convenience wrapper over Exec for shell one-liners.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the wait.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>command</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Shell command line passed to <code>/bin/sh -c</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>opts</code><a className="msb-type" href="#execoption">...ExecOption</a></div> <div className="msb-param-desc">Per-command cwd, timeout, user, and env.</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 code.</div> </div> </div> <Accordion title="Example">
go
out, err := sb.Shell(ctx, "echo $HOME && ls /tmp")
if err != nil {
    return err
}
fmt.Print(out.Stdout())
</Accordion>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (s *Sandbox) ExecStream(ctx context.Context, cmd string, args []string, opts ...ExecOption) (*ExecHandle, error)

Start a streaming exec session and return an *ExecHandle. The handle MUST be closed with Close when the stream is no longer needed. Nonblocking: the handle returns immediately and the stream starts in the background.

ctx controls only the start handshake; individual Recv calls take their own ctx. Non-zero exit codes are not errors, inspect ExecEventExited.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Controls the start handshake only.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>cmd</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Program to run.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>args</code><span className="msb-type">[]string</span></div> <div className="msb-param-desc">Command arguments. May be <code>nil</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>opts</code><a className="msb-type" href="#execoption">...ExecOption</a></div> <div className="msb-param-desc">Per-command options; add <a className="msb-type" href="#withexecstdinpipe">WithExecStdinPipe()</a> to enable stdin.</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">Live exec session. Close it when done.</div> </div> </div> <Accordion title="Example">
go
h, err := sb.ExecStream(ctx, "cat", nil, m.WithExecStdinPipe())
if err != nil {
    return err
}
defer h.Close()
</Accordion>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (s *Sandbox) ShellStream(ctx context.Context, command string, opts ...ExecOption) (*ExecHandle, error)

Run /bin/sh -c command with streaming output. A convenience wrapper over ExecStream. Nonblocking: the handle returns immediately and the stream starts in the background.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Controls the start handshake only.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>command</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Shell command line passed to <code>/bin/sh -c</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>opts</code><a className="msb-type" href="#execoption">...ExecOption</a></div> <div className="msb-param-desc">Per-command 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">Live exec session. Close it when done.</div> </div> </div> <Accordion title="Example">
go
h, err := sb.ShellStream(ctx, "tail -f /var/log/app.log")
if err != nil {
    return err
}
defer h.Close()

for {
    ev, err := h.Recv(ctx)
    if err != nil {
        return err
    }
    switch ev.Kind {
    case m.ExecEventStdout:
        os.Stdout.Write(ev.Data)
    case m.ExecEventDone:
        return nil
    }
}
</Accordion>

ExecHandle methods

A live streaming exec session returned by ExecStream and ShellStream. Not safe for concurrent use from multiple goroutines.


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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (h *ExecHandle) ID() (string, error)

Return the unique identifier for this exec session, assigned by the guest agent. Useful for correlating log entries or referencing the session from external tooling.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">string</span></div> <div className="msb-param-desc">Session correlation id.</div> </div> </div>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (h *ExecHandle) TakeStdin() *ExecSink

Return the stdin sink for this exec session. Single-take: returns nil if the session was not started with WithExecStdinPipe, or if TakeStdin was already called on this handle (matching the Node and Python SDKs). The caller is responsible for closing the sink when done writing; closing the sink without closing the exec handle is fine, they own different Rust-side resources.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#execsink">*ExecSink</a></div> <div className="msb-param-desc">Stdin writer, or <code>nil</code> if unavailable.</div> </div> </div> <Accordion title="Example">
go
sink := h.TakeStdin()
sink.Write([]byte("hello\n"))
sink.Close()
</Accordion>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (h *ExecHandle) Recv(ctx context.Context) (*ExecEvent, error)

Block until the next event arrives or the stream ends. Returns an event with Kind == ExecEventDone when all events have been consumed. ctx controls the wait; cancellation causes Recv to return ctx.Err() immediately. The underlying Rust call may continue to completion in the background.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the wait for the next event.</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="#execevent">*ExecEvent</a></div> <div className="msb-param-desc">The next streamed event.</div> </div> </div> <Accordion title="Example">
go
for {
    ev, err := h.Recv(ctx)
    if err != nil {
        return err
    }
    switch ev.Kind {
    case m.ExecEventStarted:
        fmt.Printf("started pid=%d\n", ev.PID)
    case m.ExecEventStdout:
        os.Stdout.Write(ev.Data)
    case m.ExecEventStderr:
        os.Stderr.Write(ev.Data)
    case m.ExecEventExited:
        fmt.Printf("exited code=%d\n", ev.ExitCode)
    case m.ExecEventDone:
        return nil
    }
}
</Accordion>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (h *ExecHandle) Collect(ctx context.Context) (*ExecOutput, error)

Drain the stream, accumulate all output, and return it as an *ExecOutput. Equivalent to calling Recv in a loop and assembling the result. The handle should be closed after Collect returns.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the drain.</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 code.</div> </div> </div> <Accordion title="Example">
go
out, err := h.Collect(ctx)
if err != nil {
    return err
}
fmt.Print(out.Stdout())
</Accordion>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (h *ExecHandle) Wait(ctx context.Context) (int, error)

Block until the process exits and return its exit code. Unlike Collect, stdout and stderr are discarded. The handle should be closed after Wait returns.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the wait.</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">int</span></div> <div className="msb-param-desc">Process exit code.</div> </div> </div>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (h *ExecHandle) Kill(ctx context.Context) error

Send SIGKILL to the running process.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the call.</div> </div> </div>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (h *ExecHandle) Signal(ctx context.Context, signal int) error

Send a Unix signal to the running process. Pass values from syscall (e.g. int(syscall.SIGTERM)).

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the call.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>signal</code><span className="msb-type">int</span></div> <div className="msb-param-desc">Signal number, e.g. <code>int(syscall.SIGTERM)</code>.</div> </div> </div> <Accordion title="Example">
go
import "syscall"

if err := h.Signal(ctx, int(syscall.SIGTERM)); err != nil {
    return err
}
</Accordion>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (h *ExecHandle) Close() error

Release the Rust-side exec handle. Does not kill the running process; call Signal or Kill first if you need to terminate it. Safe to call after ExecEventDone has been received.

<Accordion title="Example">
go
defer h.Close()
</Accordion>

ExecOutput methods

The collected result of a completed command execution, returned by Exec, Shell, and Collect.


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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (e *ExecOutput) Stdout() string

Captured standard output as a string.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">string</span></div> <div className="msb-param-desc">Collected stdout.</div> </div> </div>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (e *ExecOutput) StdoutBytes() []byte

Captured standard output as raw bytes. Use when the output may not be valid UTF-8.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">[]byte</span></div> <div className="msb-param-desc">Raw stdout bytes.</div> </div> </div>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (e *ExecOutput) Stderr() string

Captured standard error as a string.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">string</span></div> <div className="msb-param-desc">Collected stderr.</div> </div> </div>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (e *ExecOutput) StderrBytes() []byte

Captured standard error as raw bytes.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">[]byte</span></div> <div className="msb-param-desc">Raw stderr bytes.</div> </div> </div>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (e *ExecOutput) ExitCode() int

The process's exit code, or -1 if the guest did not report one (e.g. the process was killed by a signal).

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">int</span></div> <div className="msb-param-desc">Exit code, or <code>-1</code>.</div> </div> </div>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (e *ExecOutput) Success() bool

Reports whether the command exited with code 0.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">bool</span></div> <div className="msb-param-desc"><code>true</code> if <code>ExitCode()</code> is <code>0</code>.</div> </div> </div>

ExecSink methods

A write-only pipe to a running process's stdin, obtained from ExecHandle.TakeStdin. Implements io.WriteCloser. Write and Close use context.Background() under the hood; for caller-controlled cancellation use WriteCtx, or tear the session down via ExecHandle.Kill / Close.


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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (sk *ExecSink) Write(p []byte) (int, error)

Send data to the process stdin. Implements io.Writer. Uses context.Background() internally, there is no way to cancel a stuck write through this method alone.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>p</code><span className="msb-type">[]byte</span></div> <div className="msb-param-desc">Bytes to write to stdin.</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">int</span></div> <div className="msb-param-desc">Number of bytes written.</div> </div> </div> <Accordion title="Example">
go
h, err := sb.ExecStream(ctx, "cat", nil, m.WithExecStdinPipe())
if err != nil {
    return err
}
defer h.Close()

sink := h.TakeStdin()
sink.Write([]byte("hello\n"))
sink.Close()

out, _ := h.Collect(ctx)
fmt.Print(out.Stdout()) // "hello\n"
</Accordion>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (sk *ExecSink) WriteCtx(ctx context.Context, p []byte) (int, error)

Like Write, but with a caller-controlled context, so a stuck stdin write can be cancelled.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the write.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>p</code><span className="msb-type">[]byte</span></div> <div className="msb-param-desc">Bytes to write to stdin.</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">int</span></div> <div className="msb-param-desc">Number of bytes written.</div> </div> </div>

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

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (sk *ExecSink) Close() error

Close the stdin pipe, sending EOF to the process. Implements io.Closer.

<Accordion title="Example">
go
sink.Close()
</Accordion>

Types


ExecOutput

<div className="msb-tags"><span className="msb-tag is-type">struct</span></div> <p className="msb-backref">Returned by <a href="#sb-exec">sb.Exec()</a> · <a href="#sb-shell">sb.Shell()</a> · <a href="#h-collect">h.Collect()</a></p>

The collected result of a command execution. A non-zero ExitCode is not treated as a Go error, callers inspect Success or ExitCode explicitly, matching how os/exec.Cmd.Output works against a script that exits non-zero.

MethodReturnsDescription
Stdout()stringCaptured stdout as a string
StdoutBytes()[]byteRaw stdout bytes
Stderr()stringCaptured stderr as a string
StderrBytes()[]byteRaw stderr bytes
ExitCode()intExit code, or -1 if the guest did not report one
Success()booltrue if ExitCode() is 0

ExecHandle

<div className="msb-tags"><span className="msb-tag is-type">struct</span></div> <p className="msb-backref">Returned by <a href="#sb-execstream">sb.ExecStream()</a> · <a href="#sb-shellstream">sb.ShellStream()</a></p>

A live streaming exec session. Must be closed with Close when done to release Rust-side resources. Not safe for concurrent use from multiple goroutines.

MethodReturnsDescription
ID()(string, error)Session correlation id assigned by the guest agent
TakeStdin()*ExecSinkTake the stdin writer (single-take; only with WithExecStdinPipe)
Recv(ctx)(*ExecEvent, error)Block until the next event arrives
Collect(ctx)(*ExecOutput, error)Drain remaining output into an ExecOutput
Wait(ctx)(int, error)Wait for exit, discarding output; returns the exit code
Kill(ctx)errorSend SIGKILL
Signal(ctx, signal)errorSend a Unix signal
Close()errorRelease the Rust-side handle

ExecSink

<div className="msb-tags"><span className="msb-tag is-type">type alias</span></div> <p className="msb-backref">Returned by <a href="#h-takestdin">h.TakeStdin()</a></p>
go
type ExecSink = ffi.ExecSink

A write-only pipe to a running process's stdin. Implements io.WriteCloser.

MethodReturnsDescription
Write(p)(int, error)Implements io.Writer
WriteCtx(ctx, p)(int, error)Write with explicit context
Close()errorSend EOF and finalize

ExecEvent

<div className="msb-tags"><span className="msb-tag is-type">struct</span></div> <p className="msb-backref">Returned by <a href="#h-recv">h.Recv()</a></p>

One event from a streaming exec session. Kind identifies which fields are populated.

FieldTypeDescription
KindExecEventKindIdentifies which fields are populated
PIDuint32Guest process id, set on ExecEventStarted
Data[]byteChunk of stdout or stderr, set on ExecEventStdout / ExecEventStderr
ExitCodeintProcess exit code, set on ExecEventExited
Failure*ExecFailureFailure detail, set on ExecEventFailed and ExecEventStdinError

ExecEventKind

<div className="msb-tags"><span className="msb-tag is-type">type alias</span></div> <p className="msb-backref">Field of <a href="#execevent">ExecEvent</a></p>
go
type ExecEventKind = ffi.ExecEventKind

Identifies what an ExecEvent carries.

ConstantDescription
ExecEventStartedSent once when the guest process starts; PID is valid
ExecEventStdoutA chunk of stdout; Data is valid
ExecEventStderrA chunk of stderr; Data is valid
ExecEventExitedThe process exited; ExitCode is valid
ExecEventFailedThe user program never started (binary missing, permission denied, ...); Failure is valid and ExitCode is not meaningful
ExecEventStdinErrorA stdin write failed; Failure is valid. Non-terminal: the process may still exit normally
ExecEventDoneAll events have been consumed

ExecFailure

<div className="msb-tags"><span className="msb-tag is-type">type alias</span></div> <p className="msb-backref">Field of <a href="#execevent">ExecEvent</a></p>
go
type ExecFailure = ffi.ExecFailure

Structured detail about a failed-to-start exec, populated on ExecEventFailed and ExecEventStdinError. See Error Handling for the kinds it carries (not_found, permission_denied, etc.) and how to branch on them.

FieldTypeDescription
KindstringFailure category, e.g. not_found, permission_denied
Errno*intUnderlying errno, if known
ErrnoNamestringSymbolic errno name, if known
MessagestringHuman-readable failure message
PathstringOffending path, if applicable

ExecConfig

<div className="msb-tags"><span className="msb-tag is-type">struct</span></div> <p className="msb-backref">Populated by <a href="#execoption">ExecOption</a></p>

Configures a single Exec or ExecStream call. Most callers set fields through the WithExec* functional options; ExecConfig is exported for parity with the other SDKs' config types.

FieldTypeDescription
CwdstringWorking directory inside the guest
Timeouttime.DurationKill the process after this duration; sub-second precision is rounded up
StdinPipeboolEnable a stdin pipe; required for TakeStdin
UserstringGuest user (UID or name)
Envmap[string]stringPer-command environment variables

ExecOption

<div className="msb-tags"><span className="msb-tag is-type">type</span></div> <p className="msb-backref">Accepted by <a href="#sb-exec">sb.Exec()</a> · <a href="#sb-shell">sb.Shell()</a> · <a href="#sb-execstream">sb.ExecStream()</a> · <a href="#sb-shellstream">sb.ShellStream()</a></p>
go
type ExecOption func(*ExecConfig)

A functional option that mutates an ExecConfig. Construct them with the WithExec* functions below.


<span className="msb-hn">WithExecCwd()</span>

<div className="msb-tags"><span className="msb-tag is-builder">option</span></div>
go
func WithExecCwd(path string) ExecOption

Set the working directory for a single command.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Absolute path inside the guest.</div> </div> </div>

<span className="msb-hn">WithExecTimeout()</span>

<div className="msb-tags"><span className="msb-tag is-builder">option</span></div>
go
func WithExecTimeout(d time.Duration) ExecOption

Set a per-command timeout. When exceeded, the guest terminates the process and the call returns an error with Kind == ErrExecTimeout. Sub-second precision rounds up to whole seconds; pass at least 1 second.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>d</code><span className="msb-type">time.Duration</span></div> <div className="msb-param-desc">Timeout duration, rounded up to whole seconds.</div> </div> </div> <Accordion title="Example">
go
out, err := sb.Shell(ctx, "long-running-task",
    m.WithExecTimeout(30*time.Second))
if m.IsKind(err, m.ErrExecTimeout) {
    log.Println("timed out")
}
</Accordion>

<span className="msb-hn">WithExecStdinPipe()</span>

<div className="msb-tags"><span className="msb-tag is-builder">option</span></div>
go
func WithExecStdinPipe() ExecOption

Enable a stdin pipe for the exec session, allowing data to be written via ExecHandle.TakeStdin.


<span className="msb-hn">WithExecUser()</span>

<div className="msb-tags"><span className="msb-tag is-builder">option</span></div>
go
func WithExecUser(user string) ExecOption

Run the command as the given guest user (UID or name).

<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">string</span></div> <div className="msb-param-desc">Guest user, as a UID or name.</div> </div> </div>

<span className="msb-hn">WithExecEnv()</span>

<div className="msb-tags"><span className="msb-tag is-builder">option</span></div>
go
func WithExecEnv(env map[string]string) ExecOption

Add per-command environment variables. Called repeatedly, maps merge; later keys overwrite earlier ones.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>env</code><span className="msb-type">map[string]string</span></div> <div className="msb-param-desc">Environment variables for this command.</div> </div> </div> <Accordion title="Example">
go
out, err := sb.Exec(ctx, "make", []string{"build"},
    m.WithExecCwd("/app"),
    m.WithExecEnv(map[string]string{"DEBUG": "1"}),
)
</Accordion>