Skip to content

Quickstart

This page is the shape of getting started, end to end. Read Build an extension first for the model behind it.

An extension needs two things: the SDK crate and something to run async code on. Nothing else.

[dependencies]
vros-ext = "2.0"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }

The crate version is vrOS’s own version, not the protocol’s. The wire version is a separate constant, vxp::VXP_VERSION, currently 1.1, and the host accepts the current VXP major and the one before it — so an SDK a version behind the installed vrOS keeps working.

vrOS launches your binary with the endpoint, a per-spawn token, and your extension id in the environment. connect_from_env reads them and connects, retrying for a few seconds to cover the spawn race.

Then say hello. You have five seconds from launch, and nothing else is answered until you do. Say hello first, initialize after.

use vros_ext::conn::{Client, SocketConnection};
use vros_ext::event::VxpEvent;
use vros_ext::hello::{HelloReply, HelloRequest};
use vros_ext::vxp;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let conn = SocketConnection::connect_from_env().await?;
let client = Client::new(conn);
let reply = client
.hello(&HelloRequest {
id: std::env::var(vxp::ENV_EXT_ID)?,
vxp_version: vxp::VXP_VERSION.into(),
token: std::env::var(vxp::ENV_TOKEN).ok(),
})
.await?;
// The welcome carries what the user actually granted, and their locale.
let (granted, locale) = match reply {
HelloReply::Welcome { granted, locale, .. } => (granted, locale),
HelloReply::Rejected { code, message } => {
return Err(format!("hello rejected ({code:?}): {message}").into());
}
other => return Err(format!("unknown hello reply: {other:?}").into()),
};
// Do your setup here — read KV, subscribe to what you were granted,
// push your panel's words. Then park on the event stream.
let mut events = client.take_events().ok_or("no event stream")?;
while let Some(event) = events.recv().await {
match event {
VxpEvent::ShuttingDown => break,
_ => {}
}
}
Ok(())
}

Check granted before you use a capability rather than assuming your manifest was accepted whole — the user consents per capability, and a missing grant is permanent for that session.

vros-ext.toml sits beside your binary and describes it. This is what the consent screen reads, and it is read while your extension is stopped.

[extension]
id = "com.example.hello"
name = "Hello"
version = "0.1.0"
author = "Your name"
entry = "hello.exe" # the binary name; drop the .exe on Linux
restart = "on-crash"
[capabilities]
bus = ["vr.lifecycle"]
[panels]
overlay = "ui/hello.slint"

The id is lowercase reverse-DNS drawn from [a-z0-9.-], conventionally tld.author.name. It is also your install folder name, your KV namespace, and your bus prefix (ext.<id>.*), so pick it once and keep it.

An install is a folder holding the manifest, the binary it names, and any panel sources.

%LOCALAPPDATA%\vrOS\extensions\com.example.hello\ (Windows)
~/.local/share/vros/extensions/com.example.hello/ (Linux)
├─ vros-ext.toml
├─ hello.exe
└─ ui/hello.slint

Open Settings → Extensions and hit REFRESH. Your extension appears disabled, with the capabilities it asked for listed. Enable it, consent, and vrOS spawns it. The same page shows its status, its restart count, and its last error, and it is where the user removes it again.

  • Hello is a deadline, not a formality. Miss five seconds and the host kills you and applies your restart policy.
  • Panel paths are checked at parse. Relative, forward slashes, components limited to letters, digits, dot, underscore and hyphen, ending in .slint. Traversal, absolute paths, drive letters and backslashes are refused before anything reads the disk.
  • A [[settings]] block targets a VXP minor. A manifest carrying one fails to parse on a host older than VXP 1.1, because unknown manifest fields are an error rather than a shrug.
  • One JSON object per line. Batch arrays are refused, and a frame over 1 MiB is treated as corruption.
  • KV is your only durable state. Assume you will be restarted mid-anything and rebuild the rest.

The SDK crate is not on crates.io, and the repository holding it — the crate, the reference hello extension, the @vros/theme.slint module, and the authoring contract — is private today. The code is licensed MIT or Apache-2.0 at your option, and this page will link the crate and the example the moment they are public. If you want to build against it before then, say so in Discord.