A client that talks to a remote bsdkrund daemon's GraphQL API directly —
java.net.http.HttpClient for queries/mutations, its built-in
java.net.http.WebSocket speaking graphql-transport-ws for subscriptions
— instead of shelling out to a local bsdkrun binary the way
bsdkrun.sandbox does.
The wire contract (URL/header shape, error mapping, subscription protocol,
field names) is locked to match the other bsdkrun SDKs
(TypeScript/Python/Ruby/Elixir/Gleam) and the web frontend's
web/src/lib/graphql.ts — see that file for the reference implementation
this one mirrors. Unlike every other SDK here, this one needs no
hand-rolled WebSocket framing: java.net.http.WebSocket is a core JDK API
(Java 11+).
A client is a plain map, matching every other namespace's convention —
{:url ... :token ... :http-client ... :ws-state (atom ...)}. Build one
with new-client or client-from-env; every function below takes it
first.
Example:
(require '[bsdkrun.client :as client])
(def c (client/client-from-env))
(doseq [m (client/list-machines c)] (println (:id m)))
(def result (client/exec! c "abc123" ["uname" "-a"]))
(println (String. (:output result)))
A client that talks to a remote `bsdkrund` daemon's GraphQL API directly —
`java.net.http.HttpClient` for queries/mutations, its built-in
`java.net.http.WebSocket` speaking `graphql-transport-ws` for subscriptions
— instead of shelling out to a local `bsdkrun` binary the way
`bsdkrun.sandbox` does.
The wire contract (URL/header shape, error mapping, subscription protocol,
field names) is locked to match the other bsdkrun SDKs
(TypeScript/Python/Ruby/Elixir/Gleam) and the web frontend's
`web/src/lib/graphql.ts` — see that file for the reference implementation
this one mirrors. Unlike every other SDK here, this one needs no
hand-rolled WebSocket framing: `java.net.http.WebSocket` is a core JDK API
(Java 11+).
A `client` is a plain map, matching every other namespace's convention —
`{:url ... :token ... :http-client ... :ws-state (atom ...)}`. Build one
with [[new-client]] or [[client-from-env]]; every function below takes it
first.
Example:
```clojure
(require '[bsdkrun.client :as client])
(def c (client/client-from-env))
(doseq [m (client/list-machines c)] (println (:id m)))
(def result (client/exec! c "abc123" ["uname" "-a"]))
(println (String. (:output result)))
```(ai-agents client)The coding agents, and whether each one's sandbox image is built.
The coding agents, and whether each one's sandbox image is built.
(ai-remove! client agent)(ai-remove! client agent {:keys [keep-home]})Remove an agent's sandboxes, and unless :keep-home its saved login too.
Remove an agent's sandboxes, and unless `:keep-home` its saved login too.
(ai-sessions client)Agent sandboxes, newest first.
Agent sandboxes, newest first.
(ai-shell-command client agent machine-id)The argv that starts the agent's TUI — pass it to the shell.
The argv that starts the agent's TUI — pass it to the shell.
(ai-start! client agent)(ai-start! client agent {:keys [cpus mem workspace new]})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.
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.
(ai-stop! client agent)Stop an agent's sandboxes. Its saved login survives.
Stop an agent's sandboxes. Its saved login survives.
(branch! client snapshot)(branch! client snapshot {:keys [name cpus mem ports no-ports]})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. With no :ports, the
snapshot's own forwards are inherited, with any host port that is already
taken swapped for a free one.
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. With no `:ports`, the snapshot's own forwards are inherited, with any host port that is already taken swapped for a free one.
(client-from-env)(client-from-env env)Build a client from BSDKRUN_URL / BSDKRUN_TOKEN.
A URL set without a token is an error, not a silent unauthenticated
fallback — mirrors daemon/src/client.rs's RemoteConfig::from_env (which
uses BSDKRUN_HOST/BSDKRUN_TOKEN for the gRPC client; these are
GraphQL-specific env vars with a different URL shape, not aliases).
The 1-arity form takes an explicit {String -> String} env map instead of
reading System/getenv — dependency injection for tests, the same
approach bsdkrun.binary uses, since the JVM offers no supported way to
mutate real process environment variables at runtime.
Throws errors/missing-config if BSDKRUN_URL is unset, or set without
BSDKRUN_TOKEN.
Build a client from `BSDKRUN_URL` / `BSDKRUN_TOKEN`.
A URL set without a token is an error, not a silent unauthenticated
fallback — mirrors `daemon/src/client.rs`'s `RemoteConfig::from_env` (which
uses `BSDKRUN_HOST`/`BSDKRUN_TOKEN` for the gRPC client; these are
GraphQL-specific env vars with a different URL shape, not aliases).
The 1-arity form takes an explicit `{String -> String}` env map instead of
reading `System/getenv` — dependency injection for tests, the same
approach `bsdkrun.binary` uses, since the JVM offers no supported way to
mutate real process environment variables at runtime.
Throws `errors/missing-config` if `BSDKRUN_URL` is unset, or set without
`BSDKRUN_TOKEN`.(commit! client id name)(commit! client id name description)Snapshot a machine into a named flavor, like docker commit.
Snapshot a machine into a named flavor, like `docker commit`.
(docker-container! client action ids)start | stop | restart | kill | pause | unpause | rm, on one or more ids.
start | stop | restart | kill | pause | unpause | rm, on one or more ids.
(docker-containers client)(docker-containers client {:keys [all] :or {all true}})Containers in the engine. {:all false} lists only running ones.
Containers in the engine. `{:all false}` lists only running ones.
(docker-logs client id)(docker-logs client id {:keys [tail] :or {tail 200}})One container's logs (stdout+stderr, most recent tail lines).
One container's logs (stdout+stderr, most recent `tail` lines).
(docker-start! client)(docker-start! client {:keys [cpus mem mounts no-home publish-bind disk-size]})Start (or resume) the engine; returns its status once it answers.
Idempotent: the VM has a fixed name, so this resumes the existing one rather than creating a second.
Start (or resume) the engine; returns its status once it answers. Idempotent: the VM has a fixed name, so this resumes the existing one rather than creating a second.
(docker-status client)Is the Docker engine up, and where is its socket?
Is the Docker engine up, and where is its socket?
(docker-stop! client)Stop the engine. Images and containers stay on its disk.
Stop the engine. Images and containers stay on its disk.
(exec! client id command)(exec! client id command {:keys [env]})Run a command to completion and collect its output.
Implemented as the three-operation sequence daemon/README.md documents:
openShell (with a command:, so the session runs it instead of a login
shell), THEN subscribe to shellOutput (so nothing written in between is
lost), THEN wait for an exit code. closeShell always runs, whether the
wait succeeded, failed, or timed out.
opts: :env — a map of K -> V, or a ["K=V" ...] vector.
Returns {:exit-code ... :output <byte[]>}. Blocks the calling thread.
Run a command to completion and collect its output.
Implemented as the three-operation sequence `daemon/README.md` documents:
`openShell` (with a `command:`, so the session runs it instead of a login
shell), THEN subscribe to `shellOutput` (so nothing written in between is
lost), THEN wait for an exit code. `closeShell` always runs, whether the
wait succeeded, failed, or timed out.
`opts`: `:env` — a map of `K -> V`, or a `["K=V" ...]` vector.
Returns `{:exit-code ... :output <byte[]>}`. Blocks the calling thread.(follow-logs client id handlers)Stream a machine's console log live over a subscription.
handlers is {:follow :boot :on-data :on-error :on-complete} — :follow
defaults true, :boot defaults false; :on-data receives binary-safe
decoded chunks (a byte[]).
Returns a zero-arg function that stops following.
Stream a machine's console log live over a subscription.
`handlers` is `{:follow :boot :on-data :on-error :on-complete}` — `:follow`
defaults true, `:boot` defaults false; `:on-data` receives binary-safe
decoded chunks (a `byte[]`).
Returns a zero-arg function that stops following.(get-machine client id)A machine by id, name, or unique id prefix. Returns nil if no such machine exists.
A machine by id, name, or unique id prefix. Returns nil if no such machine exists.
(list-machines client)(list-machines client {:keys [all]}){:all true} includes stopped machines too (default running only).
Returns a vector of sandbox-info maps (see
bsdkrun.types/sandbox-info-from-graphql) — the same shape
bsdkrun.sandbox/list returns.
`{:all true}` includes stopped machines too (default running only).
Returns a vector of sandbox-info maps (see
`bsdkrun.types/sandbox-info-from-graphql`) — the same shape
`bsdkrun.sandbox/list` returns.(logs client id)(logs client id {:keys [boot]})One-shot console log fetch. {:boot true} shows bsdkrun's own boot log
instead of the guest console. Use follow-logs to stream instead.
One-shot console log fetch. `{:boot true}` shows bsdkrun's own boot log
instead of the guest console. Use [[follow-logs]] to stream instead.(new-client {:keys [url token]})Build a client from an explicit {:url ... :token ...} map. Does not
connect yet — the HTTP client is cheap to hold, and the websocket is opened
lazily on first subscribe.
Build a client from an explicit `{:url ... :token ...}` map. Does not
connect yet — the HTTP client is cheap to hold, and the websocket is opened
lazily on first [[subscribe]].(normalize-url input)Trim, add http:// if no scheme was given, strip trailing slashes, append
/graphql unless the path already ends with it. Mirrors
web/src/lib/connection.ts's normalizeUrl exactly.
Trim, add `http://` if no scheme was given, strip trailing slashes, append `/graphql` unless the path already ends with it. Mirrors `web/src/lib/connection.ts`'s `normalizeUrl` exactly.
(remove! client ids)(remove! client ids {:keys [force]})Remove one or more machines. ids is a single id or a collection of
them. {:force true} stops them first if running.
Remove one or more machines. `ids` is a single id or a collection of
them. `{:force true}` stops them first if running.(remove-snapshots! client names)Delete snapshots and their data. Machines branched from them are
unaffected. names is a single name or a collection of them.
Delete snapshots and their data. Machines branched from them are unaffected. `names` is a single name or a collection of them.
(request client query)(request client query variables)Run an arbitrary query or mutation. Every typed method in this namespace is implemented in terms of this — it exists as a public escape hatch for documents this SDK has no typed wrapper for yet.
Returns body["data"] (String-keyed, as parsed by clojure.data.json).
Throws errors/auth-error on HTTP 401, or a GraphQL error with
extensions.code == "UNAUTHENTICATED". Throws errors/graphql-error on
transport failure, a non-JSON response, or any other GraphQL error.
Run an arbitrary query or mutation. Every typed method in this namespace is implemented in terms of this — it exists as a public escape hatch for documents this SDK has no typed wrapper for yet. Returns `body["data"]` (String-keyed, as parsed by `clojure.data.json`). Throws `errors/auth-error` on HTTP 401, or a GraphQL error with `extensions.code == "UNAUTHENTICATED"`. Throws `errors/graphql-error` on transport failure, a non-JSON response, or any other GraphQL error.
(restore! client id snapshot)(restore! client
id
snapshot
{:keys [force backup] :or {force true backup true}})Put a machine's disk state back to one of its snapshots.
:force (default true) stops the machine first — it holds the very files
being replaced. :backup (default true) snapshots the state being
overwritten, which is a CoW clone and therefore free. The machine is left
stopped.
Put a machine's disk state back to one of its snapshots. `:force` (default true) stops the machine first — it holds the very files being replaced. `:backup` (default true) snapshots the state being overwritten, which is a CoW clone and therefore free. The machine is left stopped.
(rollback! client id)(rollback! client id {:keys [force backup] :or {force true backup true}})Restore a machine to its most recent snapshot.
Restore a machine to its most recent snapshot.
(run-bsd! client opts)Boot a FreeBSD/NetBSD guest. :os is :freebsd/:netbsd (a keyword, or
the equivalent string). Returns the new machine's id.
Boot a FreeBSD/NetBSD guest. `:os` is `:freebsd`/`:netbsd` (a keyword, or the equivalent string). Returns the new machine's id.
(run-flavor! client opts)Boot a saved flavor by name. Returns the new machine's id.
Boot a saved flavor by name. Returns the new machine's id.
(run-linux! client opts)Boot a Linux OCI image. Returns the new machine's id.
Boot a Linux OCI image. Returns the new machine's id.
(run-nanos! client opts)Boot a Nanos unikernel. No agent (no exec!/shell!), but it does have a
root disk, so :persist is the one disk option it takes. Returns the new
machine's id.
Boot a Nanos unikernel. No agent (no exec!/shell!), but it does have a root disk, so `:persist` is the one disk option it takes. Returns the new machine's id.
(run-osv! client opts)Boot an OSv unikernel. Like Nanos, no agent, but it does have a root
filesystem, so the disk options apply — :disk in particular, how an
x86_64 guest gets a filesystem (its loader ELF is kernel only). Returns the
new machine's id.
Boot an OSv unikernel. Like Nanos, no agent, but it does have a root filesystem, so the disk options apply — `:disk` in particular, how an x86_64 guest gets a filesystem (its loader ELF is kernel only). Returns the new machine's id.
(run-solo5! client opts)Boot a Solo5 (MirageOS) unikernel. 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 host-side facts cross: :path
(a .hvt binary or a project dir whose dist/ holds one, default .),
:block backing files ("NAME=FILE"), and :args handed to the unikernel
itself. Single vCPU always — :cpus above 1 is warned about and ignored.
No disk, no agent. Returns the new machine's id.
Boot a Solo5 (MirageOS) unikernel. 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 host-side facts cross: `:path`
(a `.hvt` binary or a project dir whose `dist/` holds one, default `.`),
`:block` backing files ("NAME=FILE"), and `:args` handed to the unikernel
itself. Single vCPU always — `:cpus` above 1 is warned about and ignored.
No disk, no agent. Returns the new machine's id.(run-unikraft! client opts)Boot a Unikraft unikernel. No disk and no agent, so no
volume/persist/repo/command options — :mounts (virtio-fs shares) is the
exception, needing neither. Returns the new machine's id.
Boot a Unikraft unikernel. No disk and no agent, so no volume/persist/repo/command options — `:mounts` (virtio-fs shares) is the exception, needing neither. Returns the new machine's id.
(shell! client id)(shell! client id {:keys [command env rows cols] :or {rows 24 cols 80}})Open a live interactive session. Unlike exec!, this returns
immediately with a handle whose :on-output!/:on-exit! callbacks fire as
output arrives.
The shellOutput subscription starts as soon as this function sets it up
— necessarily before the caller can register a callback on the returned
handle — so any output/exit that arrives in that window is buffered and
replayed to a callback the moment one is registered, never dropped. (This
exact race was found and fixed in two other SDKs' shell() during
review.)
opts: :command (nil opens a login shell), :env, :rows (default 24),
:cols (default 80).
Returns a handle map: {:id :write! :resize! :close! :on-output! :on-exit!}.
Open a live interactive session. Unlike [[exec!]], this returns
immediately with a handle whose `:on-output!`/`:on-exit!` callbacks fire as
output arrives.
The `shellOutput` subscription starts as soon as this function sets it up
— necessarily before the caller can register a callback on the returned
handle — so any output/exit that arrives in that window is buffered and
replayed to a callback the moment one is registered, never dropped. (This
exact race was found and fixed in two other SDKs' `shell()` during
review.)
`opts`: `:command` (nil opens a login shell), `:env`, `:rows` (default 24),
`:cols` (default 80).
Returns a handle map: `{:id :write! :resize! :close! :on-output!
:on-exit!}`.(snapshot! client id)(snapshot! client id {:keys [name description]})Capture a machine's disk state; returns the snapshot map.
{:name ...} defaults to <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.
Capture a machine's disk state; returns the snapshot map.
`{:name ...}` defaults to `<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.(snapshots client)(snapshots client {:keys [machine]})Snapshots, newest first. {:machine "web"} narrows to one machine's.
Snapshots, newest first. `{:machine "web"}` narrows to one machine's.
(start! client id)Restart a stopped machine in place. Returns a command-result map.
Restart a stopped machine in place. Returns a command-result map.
(stop! client id)Stop the machine. Returns a command-result map.
Stop the machine. Returns a command-result map.
(subscribe client query variables handlers)Start a subscription over the shared websocket (opened lazily on first
use). handlers is {:on-next :on-error :on-complete}, all optional.
A subscribe message is queued until connection_ack arrives if the
socket has not acked yet, exactly like web/src/lib/graphql.ts's
subscribe.
Returns a zero-arg function that unsubscribes (sends complete, then
closes the socket if this was the last live subscription).
Start a subscription over the shared websocket (opened lazily on first
use). `handlers` is `{:on-next :on-error :on-complete}`, all optional.
A `subscribe` message is queued until `connection_ack` arrives if the
socket has not acked yet, exactly like `web/src/lib/graphql.ts`'s
`subscribe`.
Returns a zero-arg function that unsubscribes (sends `complete`, then
closes the socket if this was the last live subscription).(update! client id)(update! client id {:keys [cpus mem]})Change the recorded vCPU / RAM. Applies on the next start!.
Change the recorded vCPU / RAM. Applies on the next [[start!]].
(ws-url http-url)Derive the websocket endpoint from the HTTP one: http:// -> ws://,
https:// -> wss://, trailing slashes on the path stripped, /ws
appended. Mirrors web/src/lib/graphql.ts's wsUrl.
Derive the websocket endpoint from the HTTP one: `http://` -> `ws://`, `https://` -> `wss://`, trailing slashes on the path stripped, `/ws` appended. Mirrors `web/src/lib/graphql.ts`'s `wsUrl`.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |