# "Running Rust on Wippy" _Path: en/tutorials/rust-wasm_ > "Build a Rust WebAssembly component and run it as functions, CLI commands, and HTTP endpoints." ## Table of Contents - Running Rust on Wippy ## Content # Running Rust on Wippy Build a Rust WebAssembly component, register it with Wippy, and expose it through function, CLI, and HTTP entries. **Classification:** Runnable tutorial with an external Rust component toolchain. The page supplies the WIT, Rust implementation, Wippy registry, integrity-hash workflow, commands, expected results, and failure checks. ## What We're Building A Rust component with four exported functions: - **greet** — Accepts a name and returns a greeting - **add** — Adds two integers - **fibonacci** — Computes the nth Fibonacci number - **list-files** — Lists files in a mounted directory The Wippy application registers these exports as callable functions, CLI commands, and an HTTP endpoint. ## Prerequisites - Wippy runtime `v0.3.32a`. - [Rust toolchain](https://rustup.rs/) with the `wasm32-wasip1` target. - A working C toolchain. On Linux, `cargo-component` also requires OpenSSL development libraries. - [cargo-component 0.21.1](https://github.com/bytecodealliance/cargo-component/releases/tag/v0.21.1), the release used by this tutorial. ```bash rustup target add wasm32-wasip1 cargo install cargo-component --version 0.21.1 --locked ``` Create the generated component scaffold and Wippy directories: ```bash mkdir rust-wasm-demo cd rust-wasm-demo cargo component new --lib demo mkdir -p app/src/demo/wasm ``` In PowerShell: ```powershell New-Item -ItemType Directory -Path rust-wasm-demo Set-Location rust-wasm-demo cargo component new --lib demo New-Item -ItemType Directory -Path app\src\demo\wasm -Force ``` `cargo component new` writes a compatible `Cargo.toml`, `src/lib.rs`, WIT file, and later regenerates `src/bindings.rs`. Keep the generated `wit-bindgen-rt` version paired with the installed `cargo-component`; the tool describes that interface as experimental and does not guarantee generated-code compatibility across versions. ## Project Structure ``` rust-wasm-demo/ ├── demo/ # Rust component │ ├── Cargo.toml │ ├── wit/ │ │ └── world.wit # WIT interface │ └── src/ │ ├── bindings.rs # generated by cargo-component │ └── 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 WebAssembly Interface Types (WIT) defines the contract between the host and guest component. Create `demo/wit/world.wit`: ```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 Keep the generated `demo/Cargo.toml`. Its package metadata must target `component:demo`, matching the WIT package, and its library crate type must remain `cdylib`. Create `demo/src/lib.rs`: ```rust #[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 ```bash cd demo cargo component build --release ``` This produces `target/wasm32-wasip1/release/demo.wasm`. Copy it to your Wippy app: ```bash mkdir -p ../app/src/demo/wasm cp target/wasm32-wasip1/release/demo.wasm ../app/src/demo/wasm/demo_component.wasm ``` In PowerShell: ```powershell New-Item -ItemType Directory -Path ..\app\src\demo\wasm -Force Copy-Item -LiteralPath target\wasm32-wasip1\release\demo.wasm ` -Destination ..\app\src\demo\wasm\demo_component.wasm ``` Get the SHA-256 hash for integrity verification: ```bash sha256sum ../app/src/demo/wasm/demo_component.wasm ``` On PowerShell, use: ```powershell (Get-FileHash ..\app\src\demo\wasm\demo_component.wasm -Algorithm SHA256).Hash.ToLowerInvariant() ``` Copy the 64 lowercase hexadecimal characters into every `YOUR_HASH_HERE` below. The final field must have the form `sha256:<64-hex-characters>`; it is the hash of the copied binary, not the Rust source or the original build path. ### Infrastructure Create `app/src/_index.yaml`: ```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: demo:gateway prefix: / - name: processes kind: process.host lifecycle: auto_start: true - name: terminal kind: terminal.host lifecycle: auto_start: true - name: policy kind: security.policy meta: comment: Grants access to mounted filesystems and WASM functions policy: actions: - fs.get - funcs.call resources: "*" effect: allow ``` Mounting a filesystem into a WASM module and calling a WASM function are both guarded actions. The policy grants them; entries that need them reference it. ### WASM Functions Create `app/src/demo/wasm/_index.yaml`: ```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.directory` entry provides the WASM binary. - Multiple functions reference the same binary with different `method` values. - The `hash` field verifies binary integrity at load time. - The `inline` pool serializes calls through one warm instance. It resets per-call execution state between synchronous calls; use another pool type when you need concurrent workers. ### Functions with WASI The `list-files` function accesses the filesystem, so it needs WASI imports: ```yaml - 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`: ```yaml version: "1.0" namespace: demo.cli entries: - name: wasm_cli_policy kind: security.policy policy: actions: - fs.get resources: - demo.wasm:assets effect: allow - name: ls kind: process.wasm meta: comment: List files from mounted WASI filesystem command: name: ls short: List files from mounted directory security: actor: {id: demo.cli:ls} policies: [demo:policy] 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, so it also carries the security context that grants the mount. ### HTTP Endpoint Add to `app/src/demo/wasm/_index.yaml`: ```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 ```bash cd app wippy init ``` ### Run CLI Commands ```bash # List available commands wippy run list ``` ``` Available commands: greet Greet someone via WASM (demo.cli:greet) ls List files from mounted directory (demo.cli:ls) Run with: wippy run ``` Arguments after the command name are passed to the exported function as string parameters, so each command takes exactly the arguments its WIT signature declares: ```bash # Run greet wippy run greet World ``` ``` Hello, World! ``` ```bash # Run ls to list mounted directory wippy run ls /data ``` The command should print at least `demo_component.wasm` with its file size and exit with status 0. Wippy does not print arbitrary `process.wasm` return payloads, which is why the CLI example uses the Rust function that writes to WASI stdout. ### Run as a Service ```bash wippy run ``` This starts the HTTP server on port 8090. The `wasi-http` transport passes the request body as the function's single string argument: ```bash curl -X POST http://localhost:8090/greet -d 'World' ``` ``` Hello, World! ``` ### Call from Lua WASM functions are called the same way as Lua functions. The calling process needs `funcs.call` on the target, which `demo:policy` grants: ```lua 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 ``` ## Troubleshooting and Cleanup - If `cargo component` is unknown, install it and rerun `cargo component build`; plain `cargo build` does not generate the same bindings/component output for this setup. - A missing `src/bindings.rs` before the first build is expected. A missing file after `cargo component build` indicates the WIT package or component metadata could not be resolved; fix that build error before copying a binary. - `WASM hash mismatch` means the binary changed after the documented digest was calculated or one placeholder remains. Recopy the release binary, recompute the digest, and update every entry that references it. - An import-instantiation error means the component imports a host profile omitted by the entry. Keep the documented `wasi:cli`, `wasi:io`, `wasi:clocks`, and `wasi:filesystem` imports on the filesystem examples. - `cannot read /data` means the `wasi.mounts` guest path or its filesystem entry does not match the registry. - Stop the HTTP runtime with Ctrl+C. Rust build output remains under `demo/target/`; remove that directory and the copied `.wasm` file to clean generated artifacts. ## Next Steps - [WASM Overview](wasm/overview.md) — WebAssembly runtime overview - [WASM Functions](wasm/functions.md) — Function configuration reference - [WASM Processes](wasm/processes.md) — Process configuration reference - [Host Functions](wasm/hosts.md) — Available WASI imports - [CLI Reference](guides/cli.md) — CLI command documentation ## Navigation Previous: "Network Overlays" (tutorials/network) Next: "LLM Agent" (tutorials/llm-agent)