| Crates.io | aegis-wasm |
| lib.rs | aegis-wasm |
| version | 0.1.1 |
| created_at | 2026-01-11 18:15:11.522494+00 |
| updated_at | 2026-01-11 18:20:59.084217+00 |
| description | Aegis - A local-first WebAssembly sandbox runtime |
| homepage | https://github.com/aayushadhikari7/aegis |
| repository | https://github.com/aayushadhikari7/aegis |
| max_upload_size | |
| id | 2036151 |
| size | 77,182 |
Aegis is a WebAssembly sandbox that lets you run untrusted code without risk. The code:
Use cases: Plugin systems, serverless functions, game mods, user-submitted code, CI/CD isolation, safe scripting.
# From source
git clone https://github.com/aayushadhikari7/aegis
cd aegis
cargo install --path crates/aegis-cli
# Or directly (once published)
cargo install aegis-cli
Add to your Cargo.toml:
[dependencies]
aegis = { git = "https://github.com/aayushadhikari7/aegis" }
# Run a function with arguments
aegis run module.wasm --function add -- 5 10
# Output: Result: 15
# Run the default function (_start or main)
aegis run module.wasm
# Run with resource limits
aegis run module.wasm --function process \
--memory-limit 33554432 \
--fuel-limit 1000000 \
--timeout 10
# Show execution metrics
aegis run module.wasm --function main --metrics
By default, WASM code has zero permissions. You grant what it needs:
# Allow reading from /data directory
aegis run module.wasm --allow-read /data
# Allow reading and writing to /tmp
aegis run module.wasm --allow-write /tmp
# Allow logging output
aegis run module.wasm --allow-logging
# Allow access to system clock
aegis run module.wasm --allow-clock
# Combine permissions
aegis run module.wasm \
--allow-read /data \
--allow-write /tmp \
--allow-logging
Check if a WASM file is valid before running:
aegis validate module.wasm
# Output: Module is valid
aegis validate corrupt.wasm
# Output: Module is invalid: ...
See what a WASM module contains:
# Show everything
aegis inspect module.wasm --all
# Show only exports (functions you can call)
aegis inspect module.wasm --exports
# Show only imports (what the module needs)
aegis inspect module.wasm --imports
Example output:
Module: plugin.wasm
Exports (3):
add [function]: (i32, i32) -> (i32)
multiply [function]: (i32, i32) -> (i32)
memory [memory]: 1 pages
Imports (1):
env.log [function]: (i32) -> ()
# Human-readable (default)
aegis run module.wasm --function add -- 2 3
# JSON output
aegis run module.wasm --function add --format json -- 2 3
# Compact JSON (single line)
aegis run module.wasm --function add --format json-compact -- 2 3
aegis run <MODULE> [OPTIONS] [-- ARGS...]
Options:
-e, --function <NAME> Function to call (default: _start or main)
--memory-limit <BYTES> Max memory in bytes (default: 64MB)
--fuel-limit <UNITS> Max CPU fuel units (default: 1B)
--timeout <SECONDS> Max execution time (default: 30s)
--allow-read <PATH> Grant read access to path
--allow-write <PATH> Grant read/write access to path
--allow-logging Enable logging output
--allow-clock Enable clock/time access
--metrics Show execution metrics
-f, --format <FORMAT> Output format: human, json, json-compact
-v, --verbose Increase verbosity (-v, -vv, -vvv)
-q, --quiet Suppress non-essential output
use aegis::prelude::*;
use std::time::Duration;
fn main() -> Result<(), AegisError> {
// Create a sandboxed runtime
let runtime = Aegis::builder()
.with_memory_limit(64 * 1024 * 1024) // 64 MB max
.with_fuel_limit(1_000_000_000) // 1 billion instructions max
.with_timeout(Duration::from_secs(30)) // 30 second timeout
.build()?;
// Load a WASM module
let module = runtime.load_file("plugin.wasm")?;
// Create a sandbox and run code
let mut sandbox = runtime.sandbox().build()?;
sandbox.load_module(&module)?;
// Call a function
let result: i32 = sandbox.call("add", (2i32, 3i32))?;
println!("2 + 3 = {}", result);
Ok(())
}
// From a file
let module = runtime.load_file("plugin.wasm")?;
// From bytes (e.g., uploaded by user)
let wasm_bytes: Vec<u8> = receive_upload();
let module = runtime.load_bytes(&wasm_bytes)?;
// From WAT text format (useful for testing)
let module = runtime.load_wat(r#"
(module
(func (export "double") (param i32) (result i32)
local.get 0
i32.const 2
i32.mul
)
)
"#)?;
use aegis::prelude::*;
let runtime = Aegis::builder()
.with_memory_limit(64 * 1024 * 1024)
.with_fuel_limit(1_000_000_000)
// Allow reading from specific paths
.with_filesystem(FilesystemCapability::read_only(&["/data", "/config"]))
// Allow reading AND writing to specific paths
.with_filesystem(FilesystemCapability::read_write(&["/tmp/sandbox"]))
// Allow logging
.with_logging(LoggingCapability::all())
// Allow clock access
.with_clock(ClockCapability::monotonic_only())
.build()?;
Let WASM code call your Rust functions:
let mut sandbox = runtime.sandbox().build()?;
// Register a function that WASM can call
sandbox.register_func("env", "print_number", |value: i32| {
println!("WASM says: {}", value);
})?;
sandbox.register_func("env", "add_numbers", |a: i32, b: i32| -> i32 {
a + b
})?;
sandbox.load_module(&module)?;
sandbox.call_void("main")?;
let mut sandbox = runtime.sandbox().build()?;
sandbox.load_module(&module)?;
let result: i32 = sandbox.call("compute", (input,))?;
// Check how much resources were used
let metrics = sandbox.metrics();
println!("Execution time: {:?}", metrics.duration());
println!("Fuel consumed: {}", metrics.fuel_consumed);
println!("Peak memory: {} bytes", metrics.peak_memory);
use aegis::prelude::*;
match sandbox.call::<(i32,), i32>("process", (input,)) {
Ok(result) => println!("Success: {}", result),
Err(ExecutionError::OutOfFuel { consumed, limit }) => {
println!("Code used too much CPU: {} / {}", consumed, limit);
}
Err(ExecutionError::Timeout(duration)) => {
println!("Code took too long: {:?}", duration);
}
Err(ExecutionError::MemoryExceeded { used, limit }) => {
println!("Code used too much memory: {} / {}", used, limit);
}
Err(ExecutionError::Trap(info)) => {
println!("Code crashed: {}", info.message);
}
Err(e) => println!("Other error: {}", e),
}
let mut sandbox = runtime.sandbox().build()?;
sandbox.load_module(&module)?;
// Process multiple inputs with the same sandbox
for input in inputs {
let result: i32 = sandbox.call("process", (input,))?;
println!("Result: {}", result);
}
// Or reset and reuse
sandbox.reset();
sandbox.load_module(&another_module)?;
| Resource | What it Limits | Default |
|---|---|---|
| Memory | Max RAM the code can use | 64 MB |
| Fuel | Max CPU instructions (deterministic) | 1 billion |
| Timeout | Max wall-clock time | 30 seconds |
| Stack | Max call stack depth | 512 KB |
| Capability | What it Allows |
|---|---|
| Filesystem | Read/write specific directories |
| Network | Connect to specific hosts |
| Logging | Print output |
| Clock | Access system time |
Principle: Code has zero permissions by default. You explicitly grant what it needs.
The CLI and library support these WASM types:
| Type | Example |
|---|---|
i32 |
42, -17 |
i64 |
9999999999 |
f32 |
3.14 |
f64 |
3.141592653589793 |
┌─────────────────────────────────────┐
│ Untrusted WASM Code │
├─────────────────────────────────────┤
│ Capability Layer (Permissions) │ ← Can it access this resource?
├─────────────────────────────────────┤
│ Resource Limiter (Memory/CPU) │ ← Has it exceeded limits?
├─────────────────────────────────────┤
│ Wasmtime Sandbox (Memory Safety) │ ← Is the code valid?
└─────────────────────────────────────┘
Guarantees:
| Crate | Description |
|---|---|
aegis |
Main library - start here |
aegis-core |
Low-level engine and sandbox |
aegis-capability |
Permission system |
aegis-resource |
Memory/CPU/time limits |
aegis-host |
Host function registration |
aegis-observe |
Metrics and monitoring |
aegis-cli |
Command-line tool |
Dual-licensed under:
Choose whichever you prefer.
Contributions welcome! Feel free to: