aegis-wasm

Crates.ioaegis-wasm
lib.rsaegis-wasm
version0.1.1
created_at2026-01-11 18:15:11.522494+00
updated_at2026-01-11 18:20:59.084217+00
descriptionAegis - A local-first WebAssembly sandbox runtime
homepagehttps://github.com/aayushadhikari7/aegis
repositoryhttps://github.com/aayushadhikari7/aegis
max_upload_size
id2036151
size77,182
Aayush Adhikari (aayushadhikari7)

documentation

README

Aegis

Run untrusted WebAssembly code safely

Rust License Crates.io

Installation | CLI Usage | Library Usage | Features


What is Aegis?

Aegis is a WebAssembly sandbox that lets you run untrusted code without risk. The code:

  • Cannot access your filesystem (unless you allow it)
  • Cannot access the network (unless you allow it)
  • Cannot use unlimited memory (you set the limit)
  • Cannot run forever (you set the timeout)
  • Cannot crash your application

Use cases: Plugin systems, serverless functions, game mods, user-submitted code, CI/CD isolation, safe scripting.


Installation

CLI Tool

# 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

As a Library

Add to your Cargo.toml:

[dependencies]
aegis = { git = "https://github.com/aayushadhikari7/aegis" }

CLI Usage

Running WebAssembly

# 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

Granting Permissions

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

Validating Modules

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: ...

Inspecting Modules

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) -> ()

Output Formats

# 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

All CLI Options

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

Library Usage

Basic Example

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(())
}

Loading WASM from Different Sources

// 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
        )
    )
"#)?;

Granting Capabilities

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()?;

Registering Host Functions

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")?;

Getting Execution Metrics

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);

Handling Errors

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),
}

Reusing Sandboxes

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)?;

Features

Resource Limits

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

Capabilities (Permissions)

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.

Supported Value Types

The CLI and library support these WASM types:

Type Example
i32 42, -17
i64 9999999999
f32 3.14
f64 3.141592653589793

Security Model

┌─────────────────────────────────────┐
│         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:

  1. Code cannot access anything you don't explicitly allow
  2. Code cannot use more resources than you allocate
  3. Code cannot crash your application
  4. Code cannot escape the sandbox

Project Structure

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

Requirements

  • Rust 1.85+
  • Works on Linux, macOS, and Windows

License

Dual-licensed under:

Choose whichever you prefer.


Contributing

Contributions welcome! Feel free to:

  • Open issues for bugs or feature requests
  • Submit pull requests
  • Improve documentation

GitHub | Issues

Commit count: 10

cargo fmt