Bsdkrun.Sandbox (bsdkrun_ex v0.5.0)

Copy Markdown View Source

A handle to a running (or stopped) bsdkrun microVM. Create one with create/1, reconnect with get/1, or enumerate with list/1.

{:ok, sbx} = Bsdkrun.Sandbox.create(os: :linux, image: "alpine")
{:ok, res} = Bsdkrun.Sandbox.exec(sbx, ["uname", "-a"])
:ok = Bsdkrun.Sandbox.stop(sbx)

Every fallible function returns {:ok, value} or {:error, %Bsdkrun.Error{}}, with a bang counterpart that unwraps or raises. Functions that act on a machine accept either a %Bsdkrun.Sandbox{} struct or a bare id string.

The bang lifecycle functions (stop!/1, start!/1, remove!/2, update!/2, connect_network!/2, disconnect_network!/1) return ref itself — not :ok — so they chain with |>:

Bsdkrun.create!(os: :linux, image: "alpine")
|> Bsdkrun.exec!(["apk", "add", "curl"])
|> Bsdkrun.stop!()

exec!/3, logs!/2, status!/1, ssh_setup!/2 and tailscale_up!/2 return their unwrapped value instead (a Result, a string, ...), since that value — not the sandbox — is the point of calling them. Reach for tap/2 to run one mid-chain without losing the sandbox:

Bsdkrun.create!(os: :linux, image: "alpine")
|> tap(&(Bsdkrun.exec!(&1, ["uname", "-a"]) |> Bsdkrun.Types.Result.text() |> IO.puts()))
|> Bsdkrun.stop!()

Summary

Types

A sandbox handle or a bare machine id.

t()

Functions

Join or switch this machine to a global network. Applies on next start/1.

Like connect_network/2, but raises on failure and returns ref (for chaining).

Boot a new microVM (detached) and return a handle to it.

Like create/1, but returns the sandbox or raises Bsdkrun.Error.

Detach this machine from its network. Applies on next start/1.

Like disconnect_network/1, but raises on failure and returns ref (for chaining).

Run a command in the guest through its exec agent.

Like exec/3, but returns the Result or raises Bsdkrun.Error.

Reconnect to an existing machine by id (a unique prefix is enough).

Like get/1, but returns the sandbox or raises Bsdkrun.Error.

Extract the machine id from a sandbox struct or a bare id string.

List machines. all: true includes exited ones (default: running only).

Like list/1, but returns the list or raises Bsdkrun.Error.

Read the machine's console log. Pass boot: true for bsdkrun's own boot log.

Like logs/2, but returns the log or raises Bsdkrun.Error.

Start building create/1 options via |> (with_*/2 calls), finished with create/1 or create!/1. See Bsdkrun.Sandbox.Builder.

Remove the machine and its state. force: true stops it first if running.

Like remove/2, but raises on failure and returns ref (for chaining).

Whether the machine is currently running (false if it can't be found).

Attach an interactive shell to the machine, inheriting the current terminal. Blocks until the shell exits; returns its exit status.

Install SSH keys in the guest via the agent (ssh setup). With no :key, the CLI installs your local ~/.ssh/*.pub. Opts: :user, :key (a literal key string or .pub path, or a list of them).

Like ssh_setup/2, but returns the Result or raises Bsdkrun.Error.

Restart a stopped machine in place — same id, disk/rootfs, resources.

Like start/1, but raises on failure and returns ref (for chaining).

Fetch this machine's current status row, or nil if it's gone.

Like status/1, but returns the row (or nil) or raises Bsdkrun.Error.

Stop the machine (BSD guests clean-poweroff; Linux is SIGTERM'd).

Like stop/1, but raises on failure and returns ref (for chaining).

Put the guest on your tailnet via the agent (tailscale setup). The :authkey is forwarded as TS_AUTHKEY (kept off the arg list). Opts: :authkey, :hostname, :args (passthrough).

Like tailscale_up/2, but returns the Result or raises Bsdkrun.Error.

Change the recorded vCPU / RAM (:cpus, :mem). Applies on next start/1.

Like update/2, but raises on failure and returns ref (for chaining).

Set the command run after -- (Linux / firmware / kernel guests).

Set the vCPU count.

Attach an extra raw disk as virtio-blk (repeatable) — a path, optionally "path:ro".

Set environment variables for the guest's entrypoint (Linux guests).

Set one environment variable — see with_env/2.

Set the guest RAM, in MiB.

Add a host<->guest mount (repeatable) — "~/project:/src" or "~/data:/data:ro".

Add several mounts at once — see with_mount/2.

Set the machine's name.

Join a global network on boot (like --network; see Bsdkrun.Networks).

Set an arbitrary create/1 option — the escape hatch for anything not wrapped above.

Add a host<->guest port forward (repeatable) — "8080:80", {2222, 22}, or %{host: 2222, guest: 22}.

Add several port forwards at once — see with_port/2.

Set the persistent volume to boot from/into (-v).

Types

ref()

@type ref() :: t() | String.t()

A sandbox handle or a bare machine id.

t()

@type t() :: %Bsdkrun.Sandbox{id: String.t(), ssh_port: non_neg_integer() | nil}

Functions

connect_network(ref, network)

@spec connect_network(ref(), String.t()) :: :ok | {:error, Bsdkrun.Error.t()}

Join or switch this machine to a global network. Applies on next start/1.

connect_network!(ref, network)

@spec connect_network!(ref(), String.t()) :: ref()

Like connect_network/2, but raises on failure and returns ref (for chaining).

create(opts)

@spec create(keyword() | map() | Bsdkrun.Sandbox.Builder.t()) ::
  {:ok, t()} | {:error, Bsdkrun.Error.t()}

Boot a new microVM (detached) and return a handle to it.

opts is a keyword list, a map, or a Bsdkrun.Sandbox.Builder (see new/1), discriminated on :os (:linux, :freebsd, :netbsd, :firmware, :kernel), plus the per-kind keys accepted by Bsdkrun.Args. :log_level (default 1) controls boot diagnostics.

create!(opts)

@spec create!(keyword() | map() | Bsdkrun.Sandbox.Builder.t()) :: t()

Like create/1, but returns the sandbox or raises Bsdkrun.Error.

disconnect_network(ref)

@spec disconnect_network(ref()) :: :ok | {:error, Bsdkrun.Error.t()}

Detach this machine from its network. Applies on next start/1.

disconnect_network!(ref)

@spec disconnect_network!(ref()) :: ref()

Like disconnect_network/1, but raises on failure and returns ref (for chaining).

exec(ref, command, opts \\ [])

@spec exec(ref(), String.t() | [String.t()], keyword()) ::
  {:ok, Bsdkrun.Types.Result.t()} | {:error, Bsdkrun.Error.t()}

Run a command in the guest through its exec agent.

command is an argv list, or a bare program name string (with :args). Opts:

  • :args — args when command is a bare program name.
  • :env — environment variables (-e K=V).
  • :tty — allocate a pseudo-TTY (-t).
  • :stdin — data piped to the command's stdin.
  • :cwd — working directory (emulated via sh -c 'cd …').
  • :throw_on_error— return {:error, _} on a non-zero exit (default false).
  • :log_level — per-command bsdkrun log level (default 0).
  • :on_stdout — function called with stdout chunks as they arrive.
  • :on_stderr — function called with stderr chunks as they arrive.

Returns {:ok, %Bsdkrun.Types.Result{}}. With throw_on_error: true, a non-zero exit yields {:error, %Bsdkrun.Error{}} instead.

exec!(ref, command, opts \\ [])

@spec exec!(ref(), String.t() | [String.t()], keyword()) :: Bsdkrun.Types.Result.t()

Like exec/3, but returns the Result or raises Bsdkrun.Error.

get(id)

@spec get(String.t()) :: {:ok, t()} | {:error, Bsdkrun.Error.t()}

Reconnect to an existing machine by id (a unique prefix is enough).

get!(id)

@spec get!(String.t()) :: t()

Like get/1, but returns the sandbox or raises Bsdkrun.Error.

id(id)

@spec id(ref()) :: String.t()

Extract the machine id from a sandbox struct or a bare id string.

list(opts \\ [])

@spec list(keyword()) ::
  {:ok, [Bsdkrun.Types.SandboxInfo.t()]} | {:error, Bsdkrun.Error.t()}

List machines. all: true includes exited ones (default: running only).

list!(opts \\ [])

@spec list!(keyword()) :: [Bsdkrun.Types.SandboxInfo.t()]

Like list/1, but returns the list or raises Bsdkrun.Error.

logs(ref, opts \\ [])

@spec logs(
  ref(),
  keyword()
) :: {:ok, String.t()} | {:error, Bsdkrun.Error.t()}

Read the machine's console log. Pass boot: true for bsdkrun's own boot log.

logs!(ref, opts \\ [])

@spec logs!(
  ref(),
  keyword()
) :: String.t()

Like logs/2, but returns the log or raises Bsdkrun.Error.

new(opts \\ [])

@spec new(keyword() | map()) :: Bsdkrun.Sandbox.Builder.t()

Start building create/1 options via |> (with_*/2 calls), finished with create/1 or create!/1. See Bsdkrun.Sandbox.Builder.

remove(ref, opts \\ [])

@spec remove(
  ref(),
  keyword()
) :: :ok | {:error, Bsdkrun.Error.t()}

Remove the machine and its state. force: true stops it first if running.

remove!(ref, opts \\ [])

@spec remove!(
  ref(),
  keyword()
) :: ref()

Like remove/2, but raises on failure and returns ref (for chaining).

running?(ref)

@spec running?(ref()) :: boolean()

Whether the machine is currently running (false if it can't be found).

shell(ref)

@spec shell(ref()) :: integer()

Attach an interactive shell to the machine, inheriting the current terminal. Blocks until the shell exits; returns its exit status.

ssh_setup(ref, opts \\ [])

@spec ssh_setup(
  ref(),
  keyword()
) :: {:ok, Bsdkrun.Types.Result.t()} | {:error, Bsdkrun.Error.t()}

Install SSH keys in the guest via the agent (ssh setup). With no :key, the CLI installs your local ~/.ssh/*.pub. Opts: :user, :key (a literal key string or .pub path, or a list of them).

ssh_setup!(ref, opts \\ [])

@spec ssh_setup!(
  ref(),
  keyword()
) :: Bsdkrun.Types.Result.t()

Like ssh_setup/2, but returns the Result or raises Bsdkrun.Error.

start(ref)

@spec start(ref()) :: :ok | {:error, Bsdkrun.Error.t()}

Restart a stopped machine in place — same id, disk/rootfs, resources.

start!(ref)

@spec start!(ref()) :: ref()

Like start/1, but raises on failure and returns ref (for chaining).

status(ref)

@spec status(ref()) ::
  {:ok, Bsdkrun.Types.SandboxInfo.t() | nil} | {:error, Bsdkrun.Error.t()}

Fetch this machine's current status row, or nil if it's gone.

status!(ref)

@spec status!(ref()) :: Bsdkrun.Types.SandboxInfo.t() | nil

Like status/1, but returns the row (or nil) or raises Bsdkrun.Error.

stop(ref)

@spec stop(ref()) :: :ok | {:error, Bsdkrun.Error.t()}

Stop the machine (BSD guests clean-poweroff; Linux is SIGTERM'd).

stop!(ref)

@spec stop!(ref()) :: ref()

Like stop/1, but raises on failure and returns ref (for chaining).

tailscale_up(ref, opts \\ [])

@spec tailscale_up(
  ref(),
  keyword()
) :: {:ok, Bsdkrun.Types.Result.t()} | {:error, Bsdkrun.Error.t()}

Put the guest on your tailnet via the agent (tailscale setup). The :authkey is forwarded as TS_AUTHKEY (kept off the arg list). Opts: :authkey, :hostname, :args (passthrough).

tailscale_up!(ref, opts \\ [])

@spec tailscale_up!(
  ref(),
  keyword()
) :: Bsdkrun.Types.Result.t()

Like tailscale_up/2, but returns the Result or raises Bsdkrun.Error.

update(ref, opts)

@spec update(
  ref(),
  keyword()
) :: :ok | {:error, Bsdkrun.Error.t()}

Change the recorded vCPU / RAM (:cpus, :mem). Applies on next start/1.

update!(ref, opts)

@spec update!(
  ref(),
  keyword()
) :: ref()

Like update/2, but raises on failure and returns ref (for chaining).

with_command(b, cmd)

Set the command run after -- (Linux / firmware / kernel guests).

with_cpus(b, n)

Set the vCPU count.

with_disk(b, path)

Attach an extra raw disk as virtio-blk (repeatable) — a path, optionally "path:ro".

with_env(b, vars)

@spec with_env(Bsdkrun.Sandbox.Builder.t(), map() | [{term(), term()}]) ::
  Bsdkrun.Sandbox.Builder.t()

Set environment variables for the guest's entrypoint (Linux guests).

Merges rather than replaces, so it composes down a pipeline instead of the last call winning:

Bsdkrun.Sandbox.new(os: :linux, image: "node:22")
|> Bsdkrun.Sandbox.with_env(%{"NODE_ENV" => "production"})
|> Bsdkrun.Sandbox.with_env("PORT", "3000")
|> Bsdkrun.Sandbox.create!()

Accepts a map or a list of {key, value} pairs; both are stringified. The variables are merged over the image's own config, so a key the image already defines is replaced rather than duplicated.

with_env(b, key, value)

Set one environment variable — see with_env/2.

with_mem(b, mb)

Set the guest RAM, in MiB.

with_mount(b, spec)

Add a host<->guest mount (repeatable) — "~/project:/src" or "~/data:/data:ro".

with_mounts(b, specs)

Add several mounts at once — see with_mount/2.

with_name(b, name)

Set the machine's name.

with_network(b, network)

Join a global network on boot (like --network; see Bsdkrun.Networks).

with_opt(b, key, value)

Set an arbitrary create/1 option — the escape hatch for anything not wrapped above.

with_port(b, port)

Add a host<->guest port forward (repeatable) — "8080:80", {2222, 22}, or %{host: 2222, guest: 22}.

with_ports(b, ports)

@spec with_ports(Bsdkrun.Sandbox.Builder.t(), [
  String.t() | {integer(), integer()} | map()
]) ::
  Bsdkrun.Sandbox.Builder.t()

Add several port forwards at once — see with_port/2.

with_volume(b, name)

Set the persistent volume to boot from/into (-v).