bsdkrun/client

A client for a remote bsdkrund daemon’s GraphQL API — the network counterpart to bsdkrun/sandbox, which shells out to a local bsdkrun binary. Point one at a daemon (client.new/client.from_env) and get the same SandboxInfo records back over HTTP + WebSocket instead of a subprocess.

import bsdkrun/client
import gleam/option.{None}

let assert Ok(c) = client.from_env()
let assert Ok(machines) = client.list(c, all: True)
let assert Ok(res) = client.exec(c, id: "abc123", command: ["uname", "-a"], env: [])

Every function returns Result(_, GraphqlError) — an alias for bsdkrun/error’s Error type (see that module), extended with the two variants this client can produce: GraphqlError (a resolver error, a malformed response, or the daemon being unreachable) and AuthError (the daemon rejected the bearer token). Pattern-match on error.GraphqlError/error.AuthError.

Design notes

Types

BsdOs (daemon/src/graphql.rs ~line 324): which BSD to boot.

pub type BsdOs {
  Freebsd
  Netbsd
}

Constructors

  • Freebsd
  • Netbsd

A handle to a daemon’s GraphQL endpoint: its URL and bearer token. Constructing one does not connect to anything — the HTTP transport is a plain request per call, and the WebSocket connection (only needed for exec/shell/follow_logs/subscribe) is opened lazily on first use.

pub opaque type Client

A bsdkrun/client error — every variant bsdkrun/error.Error has, but only GraphqlError and AuthError are ones this module itself produces.

pub type GraphqlError =
  error.Error

The net field shared by every run* mutation except runFlavor (daemon/src/graphql.rs’s NetInput, ~line 360).

pub type NetOptions {
  NetOptions(
    no_net: Bool,
    ports: List(String),
    mac: option.Option(String),
    network: option.Option(String),
    name: option.Option(String),
  )
}

Constructors

RunBsdInput (daemon/src/graphql.rs ~line 406).

pub type RunBsdOptions {
  RunBsdOptions(
    os: BsdOs,
    version: option.Option(String),
    cpus: option.Option(Int),
    mem: option.Option(Int),
    net: option.Option(NetOptions),
    volume: option.Option(String),
    persist: Bool,
    force: Bool,
    firmware: option.Option(String),
    attach_disk: List(String),
    disk_size: option.Option(String),
    repo: option.Option(String),
    command: List(String),
  )
}

Constructors

RunFlavorInput (daemon/src/graphql.rs ~line 506).

pub type RunFlavorOptions {
  RunFlavorOptions(
    name: String,
    cpus: option.Option(Int),
    mem: option.Option(Int),
    ports: List(String),
    volume: option.Option(String),
    repo: option.Option(String),
  )
}

Constructors

RunLinuxInput (daemon/src/graphql.rs ~line 384).

pub type RunLinuxOptions {
  RunLinuxOptions(
    image: String,
    cpus: option.Option(Int),
    mem: option.Option(Int),
    net: option.Option(NetOptions),
    volume: option.Option(String),
    mounts: List(String),
    attach_disk: List(String),
    env: List(String),
    entrypoint: option.Option(String),
    initramfs: Bool,
    kernel: option.Option(String),
    kernel_version: option.Option(String),
    console: option.Option(String),
    repo: option.Option(String),
    command: List(String),
  )
}

Constructors

RunNanosInput (daemon/src/graphql.rs ~line 428). Nanos has no agent (no exec/shell/commit), but does have a root disk, so persist is the one disk option it takes.

pub type RunNanosOptions {
  RunNanosOptions(
    image: String,
    cpus: option.Option(Int),
    mem: option.Option(Int),
    net: option.Option(NetOptions),
    kernel: option.Option(String),
    cmdline: option.Option(String),
    persist: Bool,
  )
}

Constructors

RunOsvInput (daemon/src/graphql.rs ~line 467). Like Nanos, no agent — but it does have a root filesystem, so unlike Unikraft it takes the disk options.

pub type RunOsvOptions {
  RunOsvOptions(
    image: String,
    cpus: option.Option(Int),
    mem: option.Option(Int),
    net: option.Option(NetOptions),
    cmdline: option.Option(String),
    disk: option.Option(String),
    no_disk: Bool,
    attach_disk: List(String),
    gic: option.Option(String),
    persist: Bool,
    volume: option.Option(String),
  )
}

Constructors

RunSolo5Input (daemon/src/graphql.rs). Solo5 (MirageOS) runs under the solo5-hvt tender rather than libkrun; the unikernel declares its own network and block devices in its MFT1 manifest note, so only what the host alone can know is carried: block backing files ("NAME=FILE") and the args handed to the unikernel itself. Always a single vCPU — cpus above 1 is warned about and ignored. No disk, no agent.

pub type RunSolo5Options {
  RunSolo5Options(
    path: option.Option(String),
    cpus: option.Option(Int),
    mem: option.Option(Int),
    net: option.Option(NetOptions),
    block: List(String),
    args: List(String),
  )
}

Constructors

RunUnikraftInput (daemon/src/graphql.rs ~line 449). A unikernel has no disk and no agent, so this carries none of the volume/persist/ repo/command fields the other guests take.

pub type RunUnikraftOptions {
  RunUnikraftOptions(
    path: option.Option(String),
    cpus: option.Option(Int),
    mem: option.Option(Int),
    net: option.Option(NetOptions),
    cmdline: option.Option(String),
    initramfs: option.Option(String),
    mounts: List(String),
  )
}

Constructors

A live, interactive shell session opened with shell. Output streams to shell_output’s Subject; shell_send/shell_resize/shell_close drive it — these four function names (there is no single “shell handle” API in the daemon’s schema to mirror 1:1) are this SDK’s own choice of shape for the “session ID + a way to write/resize/close it” the design note asked for.

pub opaque type ShellSession

Values

pub fn ai_agents(
  client: Client,
) -> Result(List(types.AiAgent), error.Error)

The coding agents, and whether each one’s sandbox image is built.

pub fn ai_remove(
  client: Client,
  agent agent: String,
  keep_home keep_home: Bool,
) -> Result(types.CommandResult, error.Error)

Remove an agent’s sandboxes, and unless keep_home its saved login too.

pub fn ai_sessions(
  client: Client,
) -> Result(List(types.AiSession), error.Error)

Agent sandboxes, newest first.

pub fn ai_shell_command(
  client: Client,
  agent agent: String,
  machine_id machine_id: String,
) -> Result(List(String), error.Error)

The argv that starts the agent’s TUI — pass it to the shell.

pub fn ai_start(
  client: Client,
  agent agent: String,
  cpus cpus: option.Option(Int),
  mem mem: option.Option(Int),
  workspace workspace: option.Option(String),
  new new: Bool,
) -> Result(String, error.Error)

Start (or reuse) a sandbox; returns its machine id.

workspace is a path on the engine’s host — a remote daemon cannot see your own filesystem. new boots a second sandbox against the same login.

pub fn ai_stop(
  client: Client,
  agent agent: String,
) -> Result(types.CommandResult, error.Error)

Stop an agent’s sandboxes. Its saved login survives.

pub fn branch(
  client: Client,
  snapshot snapshot: String,
  name name: option.Option(String),
  cpus cpus: option.Option(Int),
  mem mem: option.Option(Int),
  ports ports: List(String),
  no_ports no_ports: Bool,
) -> Result(String, error.Error)

Boot a NEW machine from a snapshot — or from a machine, which is snapshotted first — and return the new machine’s id.

The state is cloned, never booted in place, so the source is untouched and one snapshot can be branched any number of times. An empty ports inherits the snapshot’s own forwards, with any host port that is already taken swapped for a free one; no_ports drops them instead.

pub fn commit(
  client: Client,
  id id: String,
  name name: String,
  description description: String,
) -> Result(types.CommandResult, error.Error)

Snapshot a machine into a named flavor, like docker commit.

pub fn docker_container(
  client: Client,
  action action: String,
  ids ids: List(String),
) -> Result(types.CommandResult, error.Error)

Act on containers: start / stop / restart / kill / pause / unpause / rm.

pub fn docker_containers(
  client: Client,
  all all: Bool,
) -> Result(List(types.DockerContainer), error.Error)

Containers in the engine. all: False lists only running ones.

pub fn docker_logs(
  client: Client,
  id id: String,
  tail tail: Int,
) -> Result(String, error.Error)

One container’s logs (stdout+stderr, most recent tail lines).

pub fn docker_start(
  client: Client,
  cpus cpus: option.Option(Int),
  mem mem: option.Option(Int),
  mounts mounts: List(String),
  no_home no_home: Bool,
  publish_bind publish_bind: option.Option(String),
  disk_size disk_size: option.Option(String),
) -> Result(types.DockerStatus, error.Error)

Start (or resume) the engine, returning its status once it answers.

Idempotent: the VM has a fixed name, so this resumes the existing one rather than creating a second.

pub fn docker_status(
  client: Client,
) -> Result(types.DockerStatus, error.Error)

Is the Docker engine up, and where is its socket?

pub fn docker_stop(
  client: Client,
) -> Result(types.CommandResult, error.Error)

Stop the engine. Images and containers stay on its disk.

pub fn exec(
  client: Client,
  id id: String,
  command command: List(String),
  env env: List(String),
) -> Result(types.ExecResult, error.Error)

One-shot command execution: openShell (with command, so it runs that instead of a login shell) + shellOutput + closeShell, exactly the sequence daemon/README.md’s “Interactive shells over GraphQL” section describes (open, then subscribe, then wait for exit — no input to send). Blocks the calling process until the command exits.

pub fn follow_logs(
  client: Client,
  id id: String,
  follow follow: Bool,
  boot boot: Bool,
) -> Result(subject.Subject(types.ShellEvent), error.Error)

Follow a machine’s console log live — everything buffered since the subscription started, then new lines as they’re written. Ends (ShellClosed) when the underlying bsdkrun logs -f exits, which with follow: True is when the machine stops.

pub fn from_env() -> Result(Client, String)

Build a client from BSDKRUN_URL/BSDKRUN_TOKEN. BSDKRUN_URL unset is Error (nothing to connect to). BSDKRUN_URL set without BSDKRUN_TOKEN is also Error, deliberately — not a silent fallback to running unauthenticated — mirroring the same rule daemon/src/client.rs’s RemoteConfig::from_env applies to the gRPC BSDKRUN_HOST/BSDKRUN_TOKEN pair (a different pair of variables: this is the GraphQL port, not the gRPC one, so it gets its own).

pub fn get(
  client: Client,
  id id: String,
) -> Result(option.Option(types.SandboxInfo), error.Error)

A single machine by id, name, or unique id prefix — or None if there is no such machine.

pub fn list(
  client: Client,
  all all: Bool,
) -> Result(List(types.SandboxInfo), error.Error)

Machines. all: True includes stopped ones, like bsdkrun ps -a.

pub fn logs(
  client: Client,
  id id: String,
  boot boot: Bool,
) -> Result(String, error.Error)

A machine’s console log as a single string, as it stands right now. Use follow_logs to watch it live.

pub fn net_options() -> NetOptions

Default networking: attached, no forwards, no mac/network/name override.

pub fn new(url url: String, token token: String) -> Client

Build a client explicitly. url is normalized exactly like bsdkrun/graphql_transport.normalize_url (and the web UI’s connection setup): a scheme is assumed if missing, trailing slashes are stripped, and /graphql is appended if the path doesn’t already end with it.

pub fn remove(
  client: Client,
  ids ids: List(String),
  force force: Bool,
) -> Result(types.CommandResult, error.Error)

Remove one or more machines. force stops any that are still running first.

pub fn remove_snapshots(
  client: Client,
  names names: List(String),
) -> Result(types.CommandResult, error.Error)

Delete snapshots and their data. Machines already branched from them are unaffected.

pub fn request(
  client: Client,
  query query: String,
  variables variables: dynamic.Dynamic,
) -> Result(dynamic.Dynamic, error.Error)

Run any query or mutation bsdkrun/client’s typed API does not cover. variables is a Dynamic — build one from a Dict/List/literal via gleam/dynamic.from; see bsdkrun_remote_ffi.erl’s dynamic_to_json/1 doc comment for exactly which shapes it understands. Returns the data field, for the caller to decode the same way bsdkrun/types’s decoders do (gleam/dynamic/decode).

pub fn restore(
  client: Client,
  id id: String,
  snapshot snapshot: String,
  force force: Bool,
  backup backup: Bool,
) -> Result(types.CommandResult, error.Error)

Put a machine’s disk state back to one of its snapshots.

force stops the machine first — it holds the very files being replaced. backup snapshots the state being overwritten, which is a CoW clone and therefore free. The machine is left stopped.

pub fn rollback(
  client: Client,
  id id: String,
  force force: Bool,
  backup backup: Bool,
) -> Result(types.CommandResult, error.Error)

Restore a machine to its most recent snapshot.

pub fn run_bsd(
  client: Client,
  opts opts: RunBsdOptions,
) -> Result(String, error.Error)

Boot a FreeBSD/NetBSD machine, detached. Returns the new machine’s id.

pub fn run_bsd_options(os: BsdOs) -> RunBsdOptions

Defaults for RunBsdOptions: everything unset/empty/false except os.

pub fn run_flavor(
  client: Client,
  opts opts: RunFlavorOptions,
) -> Result(String, error.Error)

Boot a saved flavor, detached. Returns the new machine’s id.

pub fn run_flavor_options(name: String) -> RunFlavorOptions

Defaults for RunFlavorOptions: everything unset/empty except name.

pub fn run_linux(
  client: Client,
  opts opts: RunLinuxOptions,
) -> Result(String, error.Error)

Boot a Linux (OCI) machine, detached. Returns the new machine’s id.

pub fn run_linux_options(image: String) -> RunLinuxOptions

Defaults for RunLinuxOptions: everything unset/empty except image.

pub fn run_nanos(
  client: Client,
  opts opts: RunNanosOptions,
) -> Result(String, error.Error)

Boot a Nanos unikernel, detached. Returns the new machine’s id.

pub fn run_nanos_options(image: String) -> RunNanosOptions

Defaults for RunNanosOptions: everything unset/false except image.

pub fn run_osv(
  client: Client,
  opts opts: RunOsvOptions,
) -> Result(String, error.Error)

Boot an OSv unikernel, detached. Returns the new machine’s id.

pub fn run_osv_options(image: String) -> RunOsvOptions

Defaults for RunOsvOptions: everything unset/empty/false except image.

pub fn run_solo5(
  client: Client,
  opts opts: RunSolo5Options,
) -> Result(String, error.Error)

Boot a Solo5 (MirageOS) unikernel, detached. Returns the new machine’s id.

pub fn run_solo5_options() -> RunSolo5Options

Defaults for RunSolo5Options: everything unset/empty (path defaults to "." daemon-side when left None).

pub fn run_unikraft(
  client: Client,
  opts opts: RunUnikraftOptions,
) -> Result(String, error.Error)

Boot a Unikraft unikernel, detached. Returns the new machine’s id.

pub fn run_unikraft_options() -> RunUnikraftOptions

Defaults for RunUnikraftOptions: everything unset/empty (path defaults to "." daemon-side when left None).

pub fn shell(
  client: Client,
  id id: String,
  command command: option.Option(List(String)),
  env env: List(String),
  rows rows: Int,
  cols cols: Int,
) -> Result(ShellSession, error.Error)

Open an interactive shell (or, with command, run that command with a live, writable session rather than blocking — the non-blocking sibling of exec). Output arrives on shell_output(session) as it’s produced; this function itself returns as soon as the session and its subscription are set up.

pub fn shell_close(session: ShellSession) -> Nil

Close the session and kill its command. Idempotent.

pub fn shell_id(session: ShellSession) -> String

The daemon-side session id, if you need it for client.request.

pub fn shell_output(
  session: ShellSession,
) -> subject.Subject(types.ShellEvent)

This session’s live output Subjectsubject.receive it in a loop.

pub fn shell_resize(
  session: ShellSession,
  rows rows: Int,
  cols cols: Int,
) -> Result(Nil, error.Error)

Apply a terminal resize, so full-screen programs in the guest redraw.

pub fn shell_send(
  session: ShellSession,
  data: BitArray,
) -> Result(Nil, error.Error)

Send keystrokes/input to the session.

pub fn snapshot(
  client: Client,
  id id: String,
  name name: option.Option(String),
  description description: String,
) -> Result(types.SnapshotInfo, error.Error)

Capture a machine’s disk state. name of None yields <machine>-<n>.

A BSD guest is powered off first — a mounted UFS cannot be cloned consistently — so the machine is left stopped; start brings it back.

pub fn snapshots(
  client: Client,
  machine machine: option.Option(String),
) -> Result(List(types.SnapshotInfo), error.Error)

Snapshots, newest first. machine narrows the list to one machine’s.

pub fn start(
  client: Client,
  id id: String,
) -> Result(types.CommandResult, error.Error)

Restart a stopped machine in place.

pub fn stop(
  client: Client,
  id id: String,
) -> Result(types.CommandResult, error.Error)

Stop a machine.

pub fn subscribe(
  client: Client,
  query query: String,
  variables variables: dynamic.Dynamic,
) -> Result(subject.Subject(types.SubscriptionEvent), error.Error)

Subscribe to any subscription bsdkrun/client’s typed API does not cover. Each next payload’s data arrives as SubNext(Dynamic) on the returned Subject, terminated by SubError/SubComplete — decode it the same way request’s result.

pub fn update(
  client: Client,
  id id: String,
  cpus cpus: option.Option(Int),
  mem mem: option.Option(Int),
) -> Result(types.CommandResult, error.Error)

Change a machine’s recorded vCPU/RAM. Applies on the next start.

Search Document