gowasmrunner is an isolated execution environment that allows you to run WebAssembly functions locally via the CLI or expose them instantly as a serverless HTTP API. It solves portability and security issues when running third-party code by enforcing strict memory and execution time boundaries, without relying on CGO dependencies.
Demonstration
Running a plugin via CLI:
$ gowasmrunner -mode plugin plugin-add.wasm add 10 20
Plugin plugin-add.wasm [add] result: [30]
Running as a Serverless Gateway:
$ gowasmrunner -mode server -port 8080
2026/04/04 19:30:00 🚀 gowasmrunner HTTP serverless gateway running on port 8080
# In another terminal window:
$ curl -X POST "http://localhost:8080/execute/plugin-greet.wasm?func=greet" -d "World"
Hello, World

Technologies & Frameworks
Prerequisites
- Go >= 1.21
- Docker (optional, for running as a container)
Installation and Usage
As a binary
go install github.com/ESousa97/gowasmrunner/cmd/runner@latest
From source
git clone https://github.com/ESousa97/gowasmrunner.git
cd gowasmrunner
make build
make run
With Docker
docker build -t gowasmrunner .
docker run -p 8080:8080 gowasmrunner
Makefile Targets
| Target |
Description |
build |
Compiles the CLI binary into the bin/ folder |
test |
Runs all integration and unit tests |
gen-example |
Generates example .wasm modules in the examples/ folder |
run |
Builds, generates examples, and executes a simple addition test |
clean |
Removes build artifacts and temporary compiled modules |
Architecture
The project follows a modular architecture focused on isolation between the host runtime and guest modules.
graph TD
CLI[CLI / HTTP Client] --> Runner[cmd/runner]
subgraph "Execution Layer"
Runner --> Engine[internal/engine]
Engine --> Store[PluginStore Cache]
Engine --> Limits[Memory + Timeout Limits]
Engine --> WASI[WASI Stdout Bridge]
end
subgraph "Plugin Layer"
Store --> WasmGo[Go / TinyGo Plugin]
Store --> WasmRust[Rust Plugin]
Store --> WasmCustom[Any .wasm Plugin]
end
subgraph "HTTP Gateway"
Runner --> HTTP[net/http Server :8080]
HTTP --> Execute["POST /execute/{plugin}"]
Execute --> Engine
end
style Engine fill:#2da44e,stroke:#fff,stroke-width:1px,color:#fff
style HTTP fill:#3498db,stroke:#fff,stroke-width:1px,color:#fff
style Store fill:#8e44ad,stroke:#fff,stroke-width:1px,color:#fff
cmd/runner: Entry point that manages the CLI and the HTTP Server.
internal/engine: The core system. Manages the wazero lifecycle, resource limits (memory/timeout), WASI integration, and the compiled modules cache (PluginStore).
plugins/: Default directory scanned by the system for module pre-warming.
See docs/architecture.md for more details on technical decisions.
API Reference
Execute Plugin
POST /execute/{plugin_name}
Executes an exported function from a cached Wasm module.
Query Parameters:
func (optional): Name of the function to execute. Default: greet.
Body:
Raw payload (text/plain, application/json, etc) that will be passed to the Wasm module's linear memory.
Response:
The result returned by the Wasm function, text-encoded in the response body.
Configuration
Current configurations are managed via command line flags:
| Flag |
Description |
Type |
Default |
-mode |
Operation mode (numeric, string, plugin, server) |
string |
numeric |
-wasm |
Direct path to a Wasm file (num/str modes) |
string |
"" |
-plugins |
Directory to load cached plugins from |
string |
./plugins |
-func |
Default function to be executed |
string |
add |
-port |
Port for the HTTP server |
string |
8080 |
Roadmap (Implemented Phases)
-
Phase 1: The Host (Basic Wasm Runtime)
- Objective: Configure the runtime and execute a simple arithmetic function compiled in Wasm.
- What was done: Utilized the Wazero library (100% Go, no CGO dependency) to load a
.wasm file and call an exported function via command line arguments.
-
Phase 2: The Bridge (Memory and Data Exchange)
- Objective: Overcome Wasm's limitation of only handling numbers by enabling the passing of strings and complex objects.
- What was done: Implemented memory allocation logic in the guest (Wasm) and buffer read/write operations in the host (Go) to pass and return greeting strings.
-
Phase 3: The Warden (Sandboxing and Resources)
- Objective: Ensure the Wasm module does not consume all server resources, which is essential for the serverless model.
- What was done: Configured memory limits (
MaxMemoryPages) and execution timeouts (context.WithTimeout) for the Wasm instance, alongside basic WASI support for secure host console logging.
-
Phase 4: The Registry (Dynamic Plugin System)
- Objective: Transform the executor into a platform that loads and manages multiple modules "on-the-fly".
- What was done: Created a
PluginStore that monitors the /plugins folder, pre-compiles the modules (CompiledModule), and caches them in memory for ultra-fast invocations via CLI or server.
-
Phase 5: The Gateway (Serverless HTTP Interface)
- Objective: Expose Wasm modules through an HTTP API, simulating the behavior of AWS Lambda or Cloudflare Workers.
- What was done: Developed an HTTP server (port 8080) where the
/execute/{plugin_name} path routes the request (POST body) to the respective Wasm plugin. Included a lean Dockerfile (Alpine) ready for deployment.
-
The Cherry on Top (Language Agnosticism)
- The environment proves to be language-agnostic, allowing functions written in languages like Rust or TinyGo to be easily compiled to
.wasm and placed in the examples/plugins folder for identical execution.
Contributing
Check our CONTRIBUTING.md to learn how to set up your environment, run tests, and submit Pull Requests.
License
Distributed under the MIT License. See LICENSE for more information.