Skip to main content
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().

Typical flow

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.

sb.Exec()

func (s *Sandbox) Exec(ctx context.Context, cmd string, args []string, opts ...ExecOption) (*ExecOutput, error)
out, err := sb.Exec(ctx, "make", []string{"build"},
    m.WithExecCwd("/app"),
    m.WithExecEnv(map[string]string{"DEBUG": "1"}),
)
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.

Parameters

ctxcontext.Context
Cancels the wait. The guest process may continue in the background.
cmdstring
Program to run. Passed literally to the guest agent (the image ENTRYPOINT is not consulted).
args[]string
Command arguments. May be nil.
Per-command cwd, timeout, user, and env.

Returns

Collected stdout, stderr, and exit code.

sb.Shell()

func (s *Sandbox) Shell(ctx context.Context, command string, opts ...ExecOption) (*ExecOutput, error)
out, err := sb.Shell(ctx, "echo $HOME && ls /tmp")
if err != nil {
    return err
}
fmt.Print(out.Stdout())
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.

Parameters

ctxcontext.Context
Cancels the wait.
commandstring
Shell command line passed to /bin/sh -c.
Per-command cwd, timeout, user, and env.

Returns

Collected stdout, stderr, and exit code.

sb.ExecStream()

func (s *Sandbox) ExecStream(ctx context.Context, cmd string, args []string, opts ...ExecOption) (*ExecHandle, error)
h, err := sb.ExecStream(ctx, "cat", nil, m.WithExecStdinPipe())
if err != nil {
    return err
}
defer h.Close()
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.

Parameters

ctxcontext.Context
Controls the start handshake only.
cmdstring
Program to run.
args[]string
Command arguments. May be nil.
Per-command options; add WithExecStdinPipe() to enable stdin.

Returns

Live exec session. Close it when done.

sb.ShellStream()

func (s *Sandbox) ShellStream(ctx context.Context, command string, opts ...ExecOption) (*ExecHandle, error)
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
    }
}
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.

Parameters

ctxcontext.Context
Controls the start handshake only.
commandstring
Shell command line passed to /bin/sh -c.
Per-command options.

Returns

Live exec session. Close it when done.

ExecHandle methods

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

h.ID()

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.

Returns

string
Session correlation id.

h.TakeStdin()

func (h *ExecHandle) TakeStdin() *ExecSink
sink := h.TakeStdin()
sink.Write([]byte("hello\n"))
sink.Close()
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.

Returns

Stdin writer, or nil if unavailable.

h.Recv()

func (h *ExecHandle) Recv(ctx context.Context) (*ExecEvent, error)
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
    }
}
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.

Parameters

ctxcontext.Context
Cancels the wait for the next event.

Returns

The next streamed event.

h.Collect()

func (h *ExecHandle) Collect(ctx context.Context) (*ExecOutput, error)
out, err := h.Collect(ctx)
if err != nil {
    return err
}
fmt.Print(out.Stdout())
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.

Parameters

ctxcontext.Context
Cancels the drain.

Returns

Collected stdout, stderr, and exit code.

h.Wait()

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.

Parameters

ctxcontext.Context
Cancels the wait.

Returns

int
Process exit code.

h.Kill()

func (h *ExecHandle) Kill(ctx context.Context) error
Send SIGKILL to the running process.

Parameters

ctxcontext.Context
Cancels the call.

h.Signal()

func (h *ExecHandle) Signal(ctx context.Context, signal int) error
import "syscall"

if err := h.Signal(ctx, int(syscall.SIGTERM)); err != nil {
    return err
}
Send a Unix signal to the running process. Pass values from syscall (e.g. int(syscall.SIGTERM)).

Parameters

ctxcontext.Context
Cancels the call.
signalint
Signal number, e.g. int(syscall.SIGTERM).

h.Close()

func (h *ExecHandle) Close() error
defer h.Close()
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.

ExecOutput methods

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

out.Stdout()

func (e *ExecOutput) Stdout() string
Captured standard output as a string.

Returns

string
Collected stdout.

out.StdoutBytes()

func (e *ExecOutput) StdoutBytes() []byte
Captured standard output as raw bytes. Use when the output may not be valid UTF-8.

Returns

[]byte
Raw stdout bytes.

out.Stderr()

func (e *ExecOutput) Stderr() string
Captured standard error as a string.

Returns

string
Collected stderr.

out.StderrBytes()

func (e *ExecOutput) StderrBytes() []byte
Captured standard error as raw bytes.

Returns

[]byte
Raw stderr bytes.

out.ExitCode()

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).

Returns

int
Exit code, or -1.

out.Success()

func (e *ExecOutput) Success() bool
Reports whether the command exited with code 0.

Returns

bool
true if ExitCode() is 0.

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.

sink.Write()

func (sk *ExecSink) Write(p []byte) (int, error)
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"
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.

Parameters

p[]byte
Bytes to write to stdin.

Returns

int
Number of bytes written.

sink.WriteCtx()

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.

Parameters

ctxcontext.Context
Cancels the write.
p[]byte
Bytes to write to stdin.

Returns

int
Number of bytes written.

sink.Close()

func (sk *ExecSink) Close() error
sink.Close()
Close the stdin pipe, sending EOF to the process. Implements io.Closer.

Types

ExecOutput

Returned by sb.Exec() · sb.Shell() · h.Collect()

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

Returned by sb.ExecStream() · sb.ShellStream()

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

Returned by h.TakeStdin()

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

Returned by h.Recv()

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

Field of ExecEvent

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

Field of ExecEvent

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

Populated by ExecOption

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

Accepted by sb.Exec() · sb.Shell() · sb.ExecStream() · sb.ShellStream()

type ExecOption func(*ExecConfig)
A functional option that mutates an ExecConfig. Construct them with the WithExec* functions below.

WithExecCwd()

func WithExecCwd(path string) ExecOption
Set the working directory for a single command.

Parameters

pathstring
Absolute path inside the guest.

WithExecTimeout()

func WithExecTimeout(d time.Duration) ExecOption
out, err := sb.Shell(ctx, "long-running-task",
    m.WithExecTimeout(30*time.Second))
if m.IsKind(err, m.ErrExecTimeout) {
    log.Println("timed out")
}
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.

Parameters

dtime.Duration
Timeout duration, rounded up to whole seconds.

WithExecStdinPipe()

func WithExecStdinPipe() ExecOption
Enable a stdin pipe for the exec session, allowing data to be written via ExecHandle.TakeStdin.

WithExecUser()

func WithExecUser(user string) ExecOption
Run the command as the given guest user (UID or name).

Parameters

userstring
Guest user, as a UID or name.

WithExecEnv()

func WithExecEnv(env map[string]string) ExecOption
out, err := sb.Exec(ctx, "make", []string{"build"},
    m.WithExecCwd("/app"),
    m.WithExecEnv(map[string]string{"DEBUG": "1"}),
)
Add per-command environment variables. Called repeatedly, maps merge; later keys overwrite earlier ones.

Parameters

envmap[string]string
Environment variables for this command.