Files
next.orly.dev/pkg/wasm/shell
mleku 24eef5b5a8
Some checks failed
Go / build (push) Has been cancelled
Go / release (push) Has been cancelled
fix CORS headers and a wasm experiment
2025-11-14 19:15:50 +00:00
..
2025-11-14 19:15:50 +00:00
2025-11-14 19:15:50 +00:00

WASM Shell Runner

Run WebAssembly programs directly in your shell with stdin/stdout support using WASI (WebAssembly System Interface).

Quick Start

# Build all WAT files to WASM
./build.sh

# Run the hello example
./run.sh hello.wasm

# Run the echo example (with stdin)
echo "Hello World" | ./run.sh echo.wasm

# Or interactive
./run.sh echo.wasm

Prerequisites

Install wabt (WebAssembly Binary Toolkit)

# Ubuntu/Debian
sudo apt install wabt

# Provides: wat2wasm, wasm2wat, wasm-objdump, etc.

Install wasmtime (WASM Runtime)

# Install via official installer
curl https://wasmtime.dev/install.sh -sSf | bash

# Add to PATH (add to ~/.bashrc for persistence)
export PATH="$HOME/.wasmtime/bin:$PATH"

Examples

1. Hello World (hello.wat)

Simple example that prints to stdout:

./build.sh
./run.sh hello.wasm

Output:

Hello from WASM shell!

2. Echo Program (echo.wat)

Reads from stdin and echoes back:

./build.sh

# Interactive mode
./run.sh echo.wasm
# Type something and press Enter

# Piped input
echo "Test message" | ./run.sh echo.wasm

# From file
cat somefile.txt | ./run.sh echo.wasm

Output:

Enter text: Test message
You entered: Test message

How It Works

WASI (WebAssembly System Interface)

WASI provides a standard interface for WASM programs to interact with the host system:

  • stdin (fd 0) - Standard input
  • stdout (fd 1) - Standard output
  • stderr (fd 2) - Standard error

Key WASI Functions Used

fd_write - Write to file descriptor

(import "wasi_snapshot_preview1" "fd_write"
  (func $fd_write (param i32 i32 i32 i32) (result i32)))

;; Usage: fd_write(fd, iovs_ptr, iovs_len, nwritten_ptr) -> errno
;; fd: File descriptor (1 = stdout)
;; iovs_ptr: Pointer to iovec array
;; iovs_len: Number of iovecs
;; nwritten_ptr: Where to store bytes written

fd_read - Read from file descriptor

(import "wasi_snapshot_preview1" "fd_read"
  (func $fd_read (param i32 i32 i32 i32) (result i32)))

;; Usage: fd_read(fd, iovs_ptr, iovs_len, nread_ptr) -> errno
;; fd: File descriptor (0 = stdin)
;; iovs_ptr: Pointer to iovec array
;; iovs_len: Number of iovecs
;; nread_ptr: Where to store bytes read

iovec Structure

Both functions use an iovec (I/O vector) structure:

struct iovec {
    u32 buf;      // Pointer to buffer in WASM memory
    u32 buf_len;  // Length of buffer
}

Writing Your Own WASM Programs

Basic Template

(module
  ;; Import WASI functions you need
  (import "wasi_snapshot_preview1" "fd_write"
    (func $fd_write (param i32 i32 i32 i32) (result i32)))

  ;; Allocate memory
  (memory 1)
  (export "memory" (memory 0))

  ;; Store your strings in memory
  (data (i32.const 0) "Your message here\n")

  ;; Main function (entry point)
  (func $main (export "_start")
    ;; Setup iovec at some offset (e.g., 100)
    (i32.store (i32.const 100) (i32.const 0))   ;; buf pointer
    (i32.store (i32.const 104) (i32.const 18))  ;; buf length

    ;; Write to stdout
    (call $fd_write
      (i32.const 1)   ;; stdout
      (i32.const 100) ;; iovec pointer
      (i32.const 1)   ;; number of iovecs
      (i32.const 200) ;; nwritten pointer
    )
    drop ;; drop return value
  )
)

Build and Run

# Compile WAT to WASM
wat2wasm yourprogram.wat -o yourprogram.wasm

# Run it
./run.sh yourprogram.wasm

# Or directly with wasmtime
wasmtime yourprogram.wasm

Advanced Usage

Pass Arguments

# WASM programs can receive command-line arguments
./run.sh program.wasm arg1 arg2 arg3

Environment Variables

# Set environment variables (wasmtime flag)
wasmtime --env KEY=value program.wasm

Mount Directories

# Give WASM access to directories (wasmtime flag)
wasmtime --dir=/tmp program.wasm

Call Specific Functions

# Instead of _start, call a specific exported function
wasmtime --invoke my_function program.wasm

Compiling from High-Level Languages

From Go (using TinyGo)

# Install TinyGo
wget https://github.com/tinygo-org/tinygo/releases/download/v0.31.0/tinygo_0.31.0_amd64.deb
sudo dpkg -i tinygo_0.31.0_amd64.deb

# Write Go program
cat > main.go << 'EOF'
package main

import "fmt"

func main() {
    fmt.Println("Hello from Go WASM!")
}
EOF

# Compile to WASM with WASI
tinygo build -o program.wasm -target=wasi main.go

# Run
./run.sh program.wasm

From Rust

# Add WASI target
rustup target add wasm32-wasi

# Create project
cargo new --bin myprogram
cd myprogram

# Build for WASI
cargo build --target wasm32-wasi --release

# Run
wasmtime target/wasm32-wasi/release/myprogram.wasm

From C/C++ (using wasi-sdk)

# Download wasi-sdk
wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-21/wasi-sdk-21.0-linux.tar.gz
tar xf wasi-sdk-21.0-linux.tar.gz

# Compile C program
cat > hello.c << 'EOF'
#include <stdio.h>

int main() {
    printf("Hello from C WASM!\n");
    return 0;
}
EOF

# Compile to WASM
./wasi-sdk-21.0/bin/clang hello.c -o hello.wasm

# Run
wasmtime hello.wasm

Debugging

Inspect WASM Module

# Disassemble WASM to WAT
wasm2wat program.wasm -o program.wat

# Show module structure
wasm-objdump -x program.wasm

# Show imports
wasm-objdump -x program.wasm | grep -A 10 "Import"

# Show exports
wasm-objdump -x program.wasm | grep -A 10 "Export"

Verbose Execution

# Run with logging
WASMTIME_LOG=wasmtime=trace wasmtime program.wasm

# Enable debug info
wasmtime -g program.wasm

Use Cases for ORLY

WASM with WASI is perfect for ORLY's policy system:

Sandboxed Policy Scripts

# Write policy in any language that compiles to WASM
# Run it safely in a sandbox with controlled stdin/stdout
./run.sh policy.wasm < event.json

Benefits:

  • Security: Sandboxed execution, no system access unless granted
  • Portability: Same WASM runs on any platform
  • Performance: Near-native speed with wasmtime's JIT
  • Language Choice: Write policies in Go, Rust, C, JavaScript, etc.
  • Deterministic: Same input always produces same output

Example Policy Flow

# Event comes in via stdin (JSON)
echo '{"kind":1,"content":"hello"}' | ./run.sh filter-policy.wasm

# Policy outputs "accept" or "reject" to stdout
# ORLY reads the decision and acts accordingly

Scripts Reference

build.sh

Compiles all .wat files to .wasm using wat2wasm

run.sh [wasm-file] [args...]

Runs a WASM file with wasmtime, defaults to hello.wasm

Files

  • hello.wat - Simple stdout example
  • echo.wat - stdin/stdout interactive example
  • build.sh - Build all WAT files
  • run.sh - Run WASM files with wasmtime

Resources