Quickstart
This page is the shape of getting started, end to end. Read Build an extension first for the model behind it.
1. Depend on the SDK and an async runtime
Section titled “1. Depend on the SDK and an async runtime”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.
2. Connect, then say hello
Section titled “2. Connect, then say hello”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.
3. Write the manifest
Section titled “3. Write the manifest”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 Linuxrestart = "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.
4. Install it as a folder
Section titled “4. Install it as a folder”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.slint5. Enable it
Section titled “5. Enable it”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.
Things that bite
Section titled “Things that bite”- 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.
Not published yet
Section titled “Not published yet”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.