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