Solari

VMs

solari-sandbox (library solari) creates GUI VMs with Client::create_desktop() and hands back the same Sandbox handle a sandbox uses, plus stream_url() for the live view. Driving the GUI is not part of this surface. See the Rust SDK hub for install and configuration.

solari-sandbox = "0.1"
Published on crates.io
solari-sandbox is published to crates.io (source in the solari-sdk GitHub org).
Lifecycle and compute, not GUI control
You can create a VM, reach its stream URL, and use commands, files, code.run, and git on it. Mouse, keyboard, screenshot, display, and clipboard have no Rust binding. See What you cannot do.

Contents

Client

The same Client that creates sandboxes. Only the VM-specific calls are documented here.

Methods

create_desktop()

async fn create_desktop(&self, opts: CreateOptions) -> Result<Sandbox, SolariError>

Creates a GUI VM (POST /sandboxes with kind: "desktop") and returns a live handle. Retry-safe, since it sends an idempotency key.

Parameters:

  • opts CreateOptions: the same struct create() takes. It carries no kind field; the flavour is chosen by which method you call.

Returns: Result<Sandbox, SolariError>, with kind() of "desktop" and stream_url() set.

Errors: SolariError::Plan (402 FeatureRequiresPlan) when the org lacks the desktop entitlement; also Auth, ConcurrencyLimit, NoCapacity, or Gateway.

Example:

use solari::{Client, ClientOptions, CreateOptions};

let client = Client::new(ClientOptions::new(&api_key, "https://api.getsolari.com"))?;

let vm = client
    .create_desktop(CreateOptions {
        template: Some("office".into()),
        cpu: Some(2),
        mem_mb: Some(4096),
        ..Default::default()
    })
    .await?;

assert_eq!(vm.kind(), "desktop");
println!("{}", vm.stream_url().unwrap()); // wss://.../stream/<id>

// Core namespaces work on a VM exactly as on a sandbox.
let out = vm.commands().run("xdotool", RunOptions::new().args(["getactivewindow"])).await?;
println!("{}", out.stdout);

vm.kill().await?;
create() is unchanged
create() still sends kind: "sandbox", and From<CreateOptions> still delegates to the sandbox flavour. Existing code keeps its behaviour.

connect()

async fn connect(&self, sandbox_id: &str) -> Result<Sandbox, SolariError>

Re-attaches to a running VM by id. Reads its view, then derives both the control URL and the stream URL from the gateway origin.

Parameters:

  • sandbox_id &str: the session id.

Returns: Result<Sandbox, SolariError>. stream_url() is Some when the session is a desktop, None otherwise.

Errors: SolariError::Gateway if the id is unknown.

Example:

let vm = client.connect("vm_abc123").await?;
match vm.stream_url() {
    Some(url) => println!("live view: {url}"),
    None => println!("headless sandbox, nothing to stream"),
}
The stream URL is derived by design
SandboxView has no stream_url field because the gateway’s toSandboxView never emits one. So connect() builds it as ws_origin() + "/stream/" + id, the same way control_url has always been derived, and the result is identical to what create_desktop() reads off the wire. This is intentional: adding a struct field the wire never populates would always deserialize to None. Do not "fix" it.

Sandbox

A live session, GUI or headless. Construct via create_desktop() or connect().

Accessors

kind()

fn kind(&self) -> &str

The session flavour: "desktop" or "sandbox".

Returns: &str

Example:

if vm.kind() == "desktop" { /* has a display */ }

stream_url()

fn stream_url(&self) -> Option<&str>

The wss:// RFB URL of the live view. Hand it to a noVNC client to render or embed the screen.

Returns: Option<&str>: Some for a desktop, None for a sandbox. A headless sandbox has no display, so None is the correct answer, not a gap.

Example:

let url = vm.stream_url().ok_or("not a desktop")?;

Inherited members

Everything on the Sandbox page works unchanged on a VM: connect(), close(), kill(), id(), control_url(), expires_at(), connected(), and the commands(), files(), code(), and git() namespaces.

What you cannot do

There is no Rust GUI surface. These have no binding and no planned one on this page:

  • mouse.*, keyboard.*: pointer and key input.
  • screenshot(): capture the screen as bytes.
  • display.*, clipboard.*, open(): resolution, cursor, guest clipboard, launching apps.
  • record.*, pause()/resume(): in-guest capture, snapshot and restore.

To drive the GUI, use the TypeScript SDK (the complete reference surface) or the VM HTTP API. Mixing is supported: create and drive from TypeScript, then connect() a Rust handle to the same id for the compute work. One API key, one session, two clients.