Skip to content

Creating an App

Every VyomaOS app is a standard Rust crate that compiles to wasm32-wasip2. Here’s how to create one.

Terminal window
mkdir apps/my-app
cd apps/my-app
[package]
name = "my-app"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "my-app"
path = "src/main.rs"
[dependencies]
# Keep minimal for small binary size

Create src/main.rs:

fn main() {
println!("Hello from my-app!");
}

For a GUI app using the VYOMA_DRAW protocol:

const WHITE: u32 = 0xFFFFFFFF;
const BG: u32 = 0x1E1E2EFF;
const PURPLE: u32 = 0x7C3AEDFF;
fn main() {
// Clear background
println!("VYOMA_DRAW:fill_rect:0,0,960,700,{BG}");
// Draw title
println!("VYOMA_DRAW:draw_text:20,30,{WHITE},m,My App");
// Draw a colored box
println!("VYOMA_DRAW:fill_rect:20,60,200,100,{PURPLE}");
println!("VYOMA_DRAW:draw_text:30,100,{WHITE},m,Hello World!");
// Commit frame to display
println!("VYOMA_DRAW:flush");
// Keep running to receive input
loop {
let mut line = String::new();
if std::io::stdin().read_line(&mut line).is_ok() {
let input = line.trim();
if input.starts_with("VYOMA_INPUT:key:") {
let key = &input[16..];
eprintln!("[my-app] key pressed: {key}");
}
}
}
}

Create vyoma.toml:

[app]
name = "my-app"
version = "0.1.0"
wasm = "my-app.wasm"
[capabilities]
stdio = true # Required for console output
display = true # Required for VYOMA_DRAW
# filesystem = true # Mount /data for persistent storage
# network = true # TCP sockets
# shell = true # @supervisor: commands
# mouse = true # Mouse input events

Only declare the capabilities your app needs. Undeclared capabilities are never wired up.

Terminal window
cargo build --target wasm32-wasip2 --release

The output is at target/wasm32-wasip2/release/my-app.wasm (typically 1-10 KB).

Update base/rootfs.sh to include your app in the initramfs. Follow the pattern used by existing apps like hello-world.

Then rebuild and boot:

Terminal window
make rootfs && make run
CapabilityWhat it enables
stdio = truestdin/stdout/stderr (console I/O, keyboard input)
filesystem = trueMount /data (9P, persistent across reboots)
network = trueWASI sockets (TCP, default port 8080)
display = trueFramebuffer access + VYOMA_DRAW protocol
shell = trueIssue @supervisor: process management commands
mouse = trueReceive VYOMA_INPUT:mouse: events
watchdog_secs = NSupervisor kills app if silent for N seconds

Apps can send messages to other apps via stdout:

// Send to a specific app
println!("@other-app: hello!");
// Broadcast to all apps
println!("@broadcast: system event");
// Reply to last sender
println!("@reply: got it");
// Supervisor commands
println!("@supervisor: list");
println!("@supervisor: ping");
  • Keep dependencies minimal — every dependency increases binary size
  • Use eprintln! for debug logging (goes to supervisor’s stderr log)
  • Test with cargo build --target wasm32-wasip2 before integrating
  • The 500-line limit applies to all .rs files — split into modules early