Compile Rust scripts fast. Run them safely.
Grunt is a Rust runtime for generated or user-authored Rust scripts. It compiles scripts to
wasm32-wasip2, returns structured diagnostics on compile errors, and executes successful builds
inside a bounded Wasm sandbox.
It is built for tight loops:
- Write or generate script code.
- Compile and read machine-friendly diagnostics.
- Retry quickly.
- Execute with strict runtime limits.
- Fast rebuild loop with persistent artifact caching.
- Structured compile errors (
line/column, error codes, warnings, raw output). - Strong default limits (timeout, memory cap, deny-all WASI policy).
- Typed host/guest contract with
serde+postcard. - Concurrency support for parallel script preparation.
Rust source
|
v
Forge: template workspace + cargo build + diagnostics + artifact cache
|
v
Wasm component
|
v
Vault: Wasmtime engine + limits + timeout + host dispatch
|
v
Typed Rust result (or structured error)
For the full design, see docs/spec.md.
- Rust toolchain with Cargo.
wasm32-wasip2target installed:
rustup target add wasm32-wasip2From the repository root:
cargo run -p demo -- run demo/examples/success.rsTry failure paths:
cargo run -p demo -- run demo/examples/compile_error.rs
cargo run -p demo -- run demo/examples/trap.rsInspect or reset demo cache:
cargo run -p demo -- cache info
cargo run -p demo -- cache resetNotes:
- Demo state defaults to
./demo_data. - Use
--root-dir <path>to isolate state. - Use
--no-warmupto skip startup warmup build.
use std::path::PathBuf;
use std::time::Duration;
use grunt::{Config, Grunt, TemplateSpec, WasiPolicy};
#[tokio::main]
async fn main() -> Result<(), grunt::Error> {
let mut config = Config::new(
PathBuf::from("./grunt_data"),
TemplateSpec::default(),
Box::new(()),
);
config.wasi_policy = WasiPolicy::deny_all();
config.execution_timeout = Duration::from_secs(5);
let grunt = Grunt::new(config).await?;
let source = r#"
use prelude::*;
#[grunt_guest::main]
fn execute(req: u32) -> Result<u32, String> {
Ok(req * 2)
}
"#;
let compiled = grunt.prepare(source).await?;
let prepared = compiled.bind::<u32, u32>();
let value = prepared.execute(21, ()).await?;
assert_eq!(value, 42);
Ok(())
}Define host functions in the runtime with register_host_fn!, then call them from scripts with
grunt_guest::declare_host_fns!.
See a full end-to-end example in crates/grunt/tests/db_example.rs.
MIT.