How Easegress Supports WebAssembly for Custom Extensions: WasmHost Filter Architecture

Easegress supports WebAssembly extensions through the WasmHost filter, which embeds a wasmtime-go runtime to execute Wasm modules for request processing while exposing host functions for HTTP context manipulation, logging, and distributed KV storage.

Easegress treats WebAssembly (Wasm) as a first-class extension mechanism, allowing developers to write custom request-processing logic in any language that compiles to Wasm. When compiled with the wasmhost build tag, the server registers the WasmHost filter kind, enabling dynamic loading and execution of Wasm bytecode within a managed VM pool. This architecture isolates custom business logic from the proxy core while maintaining production performance through VM reuse, hot-reload capabilities, and deep integration with Easegress's distributed storage.

Enabling WebAssembly Support at Build Time

To activate WebAssembly capabilities, compile Easegress with the wasmhost build tag. This conditional compilation includes the wasmtime-go runtime and registers the WasmHost filter type.

make build_server GOTAGS=wasmhost

For a complete build including client tools, use:

make wasm

According to the installation documentation in docs/01.Getting-Started/1.2.Install.md, this tag is required because Wasm support is optional to keep binary sizes minimal for deployments that do not require custom extensions.

WasmHost Filter Architecture

The WebAssembly integration in Easegress consists of several coordinated components that manage the full lifecycle of Wasm execution.

Component Role Key Source File
WasmHost filter Manages filter specs, loads Wasm bytecode, watches for updates, and executes code for each request. pkg/filters/wasmhost/wasmhost.go
Wasm VM pool Reuses wasmtime virtual machines to avoid per-request VM creation overhead. pkg/filters/wasmhost/vm.go
Host functions Exposes Go functions for request/response access, logging, and KV store operations via import objects. pkg/filters/wasmhost/hostfunc.go
Cluster integration Persists Wasm code and shared data in the Easegress KV store using dedicated paths (/wasm/code and /wasm/data/...). pkg/cluster/layout.go
HTTP API Provides admin endpoints for reloading code and managing shared data without server restarts. pkg/api/wasm.go
CLI (egctl) Mirrors HTTP API functionality with sub-commands like wasm reload-code and wasm apply-data. cmd/client/commandv2/wasm.go

Filter Specification and Initialization

In pkg/filters/wasmhost/wasmhost.go, the WasmHost struct implements the filter interface. During pipeline initialization, the Init() method reads the code field—which accepts a file path, URL, or base64-encoded string—and loads the Wasm module into memory.

VM Pool Lifecycle Management

The NewWasmVMPool function in pkg/filters/wasmhost/vm.go creates a wasmtime.Engine and compiles the module once. It then instantiates a configurable pool of WasmVM instances based on the MaxConcurrency parameter, ensuring that request handling does not incur the overhead of VM creation per request.

Request Processing Lifecycle

The execution flow for Wasm extensions follows six distinct phases:

  1. Initialization: When a pipeline containing a WasmHost filter is created, Init() in pkg/filters/wasmhost/wasmhost.go validates the specification and loads the Wasm bytecode from the configured source.

  2. VM Pool Creation: NewWasmVMPool initializes a wasmtime.Engine, compiles the Wasm module into machine code once, and pre-allocates VM instances. This pool approach minimizes latency by reusing VMs across requests.

  3. Request Handling: For each request, the Handle() method obtains a VM from the pool, injects host functions via importHostFuncs, and passes the HTTP context (method, headers, body) along with shared data (key/value pairs from /wasm/data/<pipeline>/<filter>/) into the Wasm module. The runtime then calls the exported wasm_run function.

  4. Result Mapping: The Wasm function returns an int32 status code. The wasmResultToFilterResult function converts 0 to an empty string (indicating no jump) and positive integers to wasmResultN labels. These results drive conditional routing in pipeline jumpIf clauses.

  5. Dynamic Reload: A background watcher monitors the KV path /wasm/code. When updated bytecode is detected, the reload() method discards the old VM pool and constructs a new one atomically, without dropping active connections or restarting the server.

  6. Shared Data Updates: A second watcher monitors /wasm/data/<pipeline>/<filter>/ for key/value changes. Updates are stored in the filter's internal data map and become immediately visible to subsequent Wasm executions, enabling dynamic configuration updates.

Configuring WasmHost Filters

Define a WasmHost filter in your pipeline YAML by specifying the code source and timeout:

name: wasm-pipeline
kind: Normal
filters:
  - filter: wasm
    name: wasm
    code: /home/megaease/example/build/optimized.wasm
    timeout: 100ms
    parameters:
      foo: bar

The code field supports absolute file paths, HTTP URLs, or base64-encoded Wasm binaries. Reference the complete specification in docs/07.Reference/7.02.Filters.md.

Building and Hot-Reloading Wasm Modules

Develop Wasm extensions in Rust, Go, or any compatible language. For Rust:

cargo build --target wasm32-unknown-unknown --release
cp target/wasm32-unknown-unknown/release/your_module.wasm /home/megaease/example/build/

Update running code without restarting:

cp new.wasm /home/megaease/example/build/optimized.wasm
egctl wasm reload-code -p wasm-pipeline -f wasm

This command invokes POST /wasm/code in pkg/api/wasm.go, triggering the internal reload() watcher.

Shared Data Management

The WasmHost filter integrates with Easegress's distributed KV store to provide persistent state accessible to Wasm modules.

Set data via CLI:

echo 'counter: 100' | egctl wasm apply-data wasm-pipeline wasm --reload-code

List current data:

egctl wasm list-data wasm-pipeline wasm

Delete data:

egctl wasm delete-data wasm-pipeline wasm

These commands map to the HTTP API endpoints GET/POST/DELETE /wasm/data/{pipeline}/{filter} defined in pkg/api/wasm.go.

Integrating Wasm Results with Pipeline Flow

Use the integer return value from wasm_run to control pipeline execution flow:

filters:
  - filter: wasm
    name: wasm
    code: /home/megaease/example/build/optimized.wasm
  - filter: redirect
    name: redirect
    target: https://example.com
    jumpIf: { wasmResult1: END }

When the Wasm module returns 1, Easegress maps this to wasmResult1, causing the pipeline to jump to the END phase and skip subsequent filters.

Summary

  • Conditional Compilation: WebAssembly support requires building Easegress with the wasmhost tag to include the wasmtime-go runtime.
  • VM Pool Architecture: The WasmHost filter in pkg/filters/wasmhost/wasmhost.go manages a reusable pool of VMs to minimize per-request overhead.
  • Host Function Bridge: pkg/filters/wasmhost/hostfunc.go exposes Go functions for HTTP context access, logging, and KV operations to Wasm modules.
  • Dynamic Lifecycle: The reload() mechanism watches /wasm/code for updates, enabling hot-swapping of Wasm logic without service disruption.
  • Shared State: The /wasm/data/<pipeline>/<filter>/ path provides persistent key-value storage accessible across the cluster.
  • Pipeline Integration: Wasm return codes map to wasmResultN labels for conditional jumpIf routing within pipelines.

Frequently Asked Questions

How do I enable WebAssembly support in Easegress?

Compile the server with the wasmhost build tag using make build_server GOTAGS=wasmhost or make wasm for a full binary. This includes the wasmtime-go runtime and registers the WasmHost filter type. Without this tag, Wasm functionality is excluded to reduce binary size.

What languages can I use to write Wasm extensions?

Any language that compiles to WebAssembly. The Easegress documentation provides examples in Rust, but Go, AssemblyScript, C, and C++ are also compatible. The module must export a wasm_run function that returns an int32 and imports host functions from the Easegress runtime.

How does hot-reloading work without dropping requests?

Easegress maintains a background watcher on the /wasm/code KV path. When new bytecode is detected—via egctl wasm reload-code or direct API calls—the reload() method in pkg/filters/wasmhost/wasmhost.go atomically replaces the old VM pool with a new one. Existing requests complete using the old pool while new requests use the updated code.

Can Wasm extensions access request headers and body?

Yes. Through the host functions defined in pkg/filters/wasmhost/hostfunc.go, Wasm modules can read and modify HTTP request headers, body content, and response attributes. The importHostFuncs mechanism injects these capabilities into the VM instance before executing wasm_run.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →