← Back to Engineering Notes

May 21, 2026

Extending MockForge with WASM Plugins

Custom mocks without forking the codebase. Write a response generator in Rust, Go, or C, compile to WebAssembly, and MockForge calls it at request time — sandboxed, language-agnostic, and simple to wire up.

Why WASM, not a scripting language

Three reasons the plugin system chose WebAssembly over embedding a scripting runtime:

Sandboxed by default. A WASM module cannot syscall, open files, or reach the network. It receives only the data the host explicitly passes it — request context, mock state, and plugin inputs. For a tool that teams deploy in CI and internal environments, this matters: a malicious or buggy plugin can't escape its sandbox.

Language-agnostic. Compile to WASM from Rust, Go, C, C++, Zig, AssemblyScript, or any language with a WASM target. Teams already writing their services in Rust can write plugins in the same language.

Predictable performance. WASM runtimes (wasmtime in MockForge's case) compile to native code with predictable overhead — typically microseconds to call a plugin function, no GC pauses.

How plugins work

A plugin is a WASM module that exports a set of lifecycle functions. MockForge discovers plugins from a configured path, loads them at startup, and calls into them at defined extension points:

plugins:
  paths:
    - ./plugins/custom-generator.wasm
    - ./plugins/db-lookup.wasm

Each plugin exports a plugin_metadata function that declares which extension points it hooks — response generation, request validation, data transformation, or custom protocol handling.

Writing a custom response generator

A plugin that generates responses based on the request body doesn't need to parse JSON inside the WASM — MockForge passes the parsed request data as typed values. Here's a Rust plugin skeleton:

use mockforge_plugin_sdk::*;

#[no_mangle]
pub extern "C" fn plugin_metadata() -> Metadata {
    Metadata {
        name: "discount-calculator",
        version: semver!("0.1.0"),
        hooks: &[Hook::ResponseGeneration],
    }
}

#[no_mangle]
pub extern "C" fn generate_response(ctx: &RequestContext) -> Response {
    let tier = ctx.request_body.field("customer_tier").as_str();
    let base_price = ctx.request_body.field("base_price").as_f64();

    let discount = match tier {
        "premium" => 0.20,
        "business" => 0.10,
        _ => 0.0,
    };

    Response::json(serde_json::json!({
        "final_price": base_price * (1.0 - discount),
        "discount_applied": discount > 0.0,
    }))
}

Compile with cargo build --target wasm32-wasip1 --release, place the .wasm in the plugins directory, and the generator is available on every request.

Practical use cases

Database-derived responses. A plugin that connects to a real database (via the host's allowed-socket list, not from inside WASM) to return live reference data within an otherwise mocked environment. This lets you mock the service boundary while keeping the data layer real — useful for integration tests that need to validate against actual reference tables.

Protocol-specific transforms. MockForge's HTTP bridge converts gRPC calls to REST automatically. A plugin can add custom header mapping or content negotiation logic for endpoints where the default conversion doesn't match the real API.

Dynamic scenario selection. Instead of hardcoding test scenarios in the mock config, a plugin can select a scenario based on the authenticated user, the time of day, or a test-run parameter passed as a custom header. The same mock deployment serves different teams with different scenarios.

When to use a plugin vs config

If your custom behavior is expressible as a template expansion or a conditional in the YAML config, do that. The config path is simpler, easier to debug, and doesn't require a separate build step. Use a plugin when:

Growing the system

The plugin SDK is at v0.3 and the hook surface grows with each release. Current extension points cover response generation, request validation, and data transformation. Upcoming releases add plugin access to the mock's stored state and multi-request session tracking, which enables plugin-authored scenario flows.