Running Rust on Wippy
Build a Rust WebAssembly component and run it as functions, CLI commands, and HTTP endpoints.
What We're Building
A Rust component with four exported functions:
- greet - Takes a name, returns a greeting
- add - Adds two integers
- fibonacci - Computes the nth Fibonacci number
- list-files - Lists files in a mounted directory
We'll expose these as callable functions, a CLI command, and an HTTP endpoint.
Prerequisites
- Rust toolchain with
wasm32-wasip1target - cargo-component
rustup target add wasm32-wasip1
cargo install cargo-component
Project Structure
rust-wasm-demo/
├── demo/ # Rust component
│ ├── Cargo.toml
│ ├── wit/
│ │ └── world.wit # WIT interface
│ └── src/
│ └── lib.rs # Implementation
└── app/ # Wippy application
├── wippy.lock
└── src/
├── _index.yaml # Infrastructure
└── demo/
├── _index.yaml # CLI processes
└── wasm/
├── _index.yaml # WASM entries
└── demo_component.wasm # Compiled binary
Step 1: Create the WIT Interface
WIT (WebAssembly Interface Types) defines the contract between host and guest:
Create demo/wit/world.wit:
package component:demo;
world demo {
export greet: func(name: string) -> string;
export add: func(a: s32, b: s32) -> s32;
export fibonacci: func(n: u32) -> u64;
export list-files: func(path: string) -> string;
}
Each export becomes a function that Wippy can call.
Step 2: Implement in Rust
Create demo/Cargo.toml:
[package]
name = "demo"
version = "0.1.0"
edition = "2024"
[dependencies]
wit-bindgen-rt = { version = "0.44.0", features = ["bitflags"] }
[lib]
crate-type = ["cdylib"]
[profile.release]
opt-level = "s"
lto = true
[package.metadata.component]
package = "component:demo"
Create demo/src/lib.rs:
#[allow(warnings)]
mod bindings;
use bindings::Guest;
struct Component;
impl Guest for Component {
fn greet(name: String) -> String {
format!("Hello, {}!", name)
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn fibonacci(n: u32) -> u64 {
if n <= 1 {
return n as u64;
}
let (mut a, mut b) = (0u64, 1u64);
for _ in 2..=n {
let next = a + b;
a = b;
b = next;
}
b
}
fn list_files(path: String) -> String {
let mut result = String::new();
match std::fs::read_dir(&path) {
Ok(entries) => {
for entry in entries {
match entry {
Ok(e) => {
let name = e.file_name().to_string_lossy().to_string();
let meta = e.metadata();
let (kind, size) = match meta {
Ok(m) => {
let kind = if m.is_dir() { "dir" } else { "file" };
(kind, m.len())
}
Err(_) => ("?", 0),
};
let line = format!("{:<6} {:>8} {}", kind, size, name);
println!("{}", line);
result.push_str(&line);
result.push('\n');
}
Err(e) => {
let line = format!("error: {}", e);
eprintln!("{}", line);
result.push_str(&line);
result.push('\n');
}
}
}
}
Err(e) => {
let line = format!("cannot read {}: {}", path, e);
eprintln!("{}", line);
result.push_str(&line);
result.push('\n');
}
}
result
}
}
bindings::export!(Component with_types_in bindings);
The bindings module is generated by cargo-component from the WIT definition.
Step 3: Build the Component
cd demo
cargo component build --release
This produces target/wasm32-wasip1/release/demo.wasm. Copy it to your Wippy app:
mkdir -p ../app/src/demo/wasm
cp target/wasm32-wasip1/release/demo.wasm ../app/src/demo/wasm/demo_component.wasm
Get the SHA-256 hash for integrity verification:
sha256sum ../app/src/demo/wasm/demo_component.wasm
Step 4: Wippy Application
Infrastructure
Create app/src/_index.yaml:
version: "1.0"
namespace: demo
entries:
- name: gateway
kind: http.service
meta:
comment: HTTP server
addr: ":8090"
lifecycle:
auto_start: true
- name: api
kind: http.router
meta:
comment: Public API router
server: gateway
prefix: /
- name: processes
kind: process.host
lifecycle:
auto_start: true
- name: terminal
kind: terminal.host
lifecycle:
auto_start: true
WASM Functions
Create app/src/demo/wasm/_index.yaml:
version: "1.0"
namespace: demo.wasm
entries:
- name: assets
kind: fs.directory
meta:
comment: Filesystem with WASM binaries
directory: ./src/demo/wasm
- name: greet_function
kind: function.wasm
meta:
comment: Greet function via payload transport
fs: demo.wasm:assets
path: /demo_component.wasm
hash: sha256:YOUR_HASH_HERE
method: greet
pool:
type: inline
- name: add_function
kind: function.wasm
meta:
comment: Add function via payload transport
fs: demo.wasm:assets
path: /demo_component.wasm
hash: sha256:YOUR_HASH_HERE
method: add
pool:
type: inline
- name: fibonacci_function
kind: function.wasm
meta:
comment: Fibonacci function via payload transport
fs: demo.wasm:assets
path: /demo_component.wasm
hash: sha256:YOUR_HASH_HERE
method: fibonacci
pool:
type: inline
Key points:
- A single
fs.directoryentry provides the WASM binary - Multiple functions reference the same binary with different
methodvalues - The
hashfield verifies binary integrity at load time inlinepool creates a fresh instance per call
Functions with WASI
The list-files function accesses the filesystem, so it needs WASI imports:
- name: list_files_function
kind: function.wasm
meta:
comment: Filesystem listing with WASI mounts
fs: demo.wasm:assets
path: /demo_component.wasm
hash: sha256:YOUR_HASH_HERE
method: list-files
imports:
- wasi:cli
- wasi:io
- wasi:clocks
- wasi:filesystem
wasi:
mounts:
- fs: demo.wasm:assets
guest: /data
pool:
type: inline
The wasi.mounts section maps a Wippy filesystem entry to a guest path. Inside the WASM module, /data points to the demo.wasm:assets directory.
CLI Commands
Create app/src/demo/_index.yaml:
version: "1.0"
namespace: demo.cli
entries:
- name: greet
kind: process.wasm
meta:
comment: Greet someone via WASM
command:
name: greet
short: Greet someone via WASM
fs: demo.wasm:assets
path: /demo_component.wasm
hash: sha256:YOUR_HASH_HERE
method: greet
- name: ls
kind: process.wasm
meta:
comment: List files from mounted WASI filesystem
command:
name: ls
short: List files from mounted directory
fs: demo.wasm:assets
path: /demo_component.wasm
hash: sha256:YOUR_HASH_HERE
method: list-files
imports:
- wasi:cli
- wasi:io
- wasi:clocks
- wasi:filesystem
wasi:
mounts:
- fs: demo.wasm:assets
guest: /data
The meta.command block registers the process as a named CLI command. The greet command needs no WASI imports since it only uses string operations. The ls command needs filesystem access.
HTTP Endpoint
Add to app/src/demo/wasm/_index.yaml:
- name: http_greet
kind: function.wasm
meta:
comment: Greet exposed via wasi-http transport
fs: demo.wasm:assets
path: /demo_component.wasm
hash: sha256:YOUR_HASH_HERE
method: greet
transport: wasi-http
pool:
type: inline
- name: http_greet_endpoint
kind: http.endpoint
meta:
comment: HTTP POST endpoint for WASM greet
router: demo:api
method: POST
path: /greet
func: http_greet
The wasi-http transport maps HTTP request/response context to WASM arguments and results.
Step 5: Initialize and Run
cd app
wippy init
Run CLI Commands
# List available commands
wippy run list
Available commands:
greet Greet someone via WASM
ls List files from mounted directory
# Run greet
wippy run greet
# Run ls to list mounted directory
wippy run ls
Run as a Service
wippy run
This starts the HTTP server on port 8090. Test the endpoint:
curl -X POST http://localhost:8090/greet
Call from Lua
WASM functions are called the same way as Lua functions:
local funcs = require("funcs")
local greeting, err = funcs.call("demo.wasm:greet_function", "World")
-- greeting: "Hello, World!"
local sum, err = funcs.call("demo.wasm:add_function", 6, 7)
-- sum: 13
local fib, err = funcs.call("demo.wasm:fibonacci_function", 10)
-- fib: 55
Three Ways to Expose WASM
| Approach | Entry Kind | Use Case |
|---|---|---|
| Function | function.wasm |
Called from Lua or other WASM via funcs.call() |
| CLI Command | process.wasm + meta.command |
Terminal commands via wippy run <name> |
| HTTP Endpoint | function.wasm + http.endpoint |
REST API via wasi-http transport |
All three use the same compiled .wasm binary and reference the same methods.
Building for Other Languages
Any language that compiles to the WebAssembly Component Model works with Wippy. Define your WIT interface, implement the exports, compile to .wasm, and configure entries in _index.yaml.
See Also
- WASM Overview - WebAssembly runtime overview
- WASM Functions - Function configuration reference
- WASM Processes - Process configuration reference
- Host Functions - Available WASI imports
- CLI Reference - CLI command documentation