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"solari-sandbox is published to crates.io (source in the solari-sdk GitHub org).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: create_desktop(), connect()Sandbox: kind(), stream_url(), inherited members- What you cannot do
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:
optsCreateOptions: the same struct create() takes. It carries nokindfield; 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?;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"),
}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) -> &strThe 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.
