Skip to main content
DeprecatedWASM custom plugins are deprecated. Existing WASM plugins can continue to run where supported, but new plugin development should use native Go plugins. We are adding support for webhook-based plugins as the next cross-language extension path.

Overview

WebAssembly (WASM) plugins offer a powerful alternative to native Go plugins, providing cross-platform compatibility and sandboxed execution. Unlike native .so plugins, WASM plugins:
  • Run anywhere - Single .wasm binary works on any OS/architecture
  • No version matching - No need to match Go versions or dependency versions
  • Sandboxed execution - WASM provides memory-safe, isolated execution
  • Multi-language support - Write plugins in TypeScript, Go, Rust, or any WASM-compatible language

Plugin Interface

All WASM plugins must export these functions:

Return Value Format

Functions returning data use a packed u64 format:
  • Upper 32 bits: pointer to data in WASM memory
  • Lower 32 bits: length of data

Data Exchange

All complex data is exchanged as JSON strings. The host allocates memory using malloc, writes JSON data, and passes pointers to the plugin functions.

Getting Started

Choose your preferred language:

Prerequisites

Install Node.js (v18+) for AssemblyScript compilation:macOS:
Linux (Ubuntu/Debian):

Project Structure

Step 1: Initialize Project

Step 2: Implement the Plugin

Create assembly/index.ts:

Step 3: Build

Add to package.json:
Build:
Output: build/plugin.wasm

Hook Input/Output Structures

http_pre_hook

Header and Query Parameter Handling: Headers and query parameters in request.headers and request.query preserve the original casing sent by the client. When looking up headers/query params, you should perform case-insensitive comparisons in your WASM plugin code to handle various casing (e.g., Content-Type, content-type, CONTENT-TYPE).For Go native plugins, use the built-in CaseInsensitiveHeaderLookup() and CaseInsensitiveQueryLookup() helper methods.
Input:
Output:
To short-circuit with a response:

http_post_hook

Called after the response is received from the LLM provider. Receives both the original request and the response. Input:
Output:
The http_post_hook is called in reverse order of http_pre_hook. Context values set in http_pre_hook are available in http_post_hook.
http_post_hook is NOT called for streaming responses. Use http_stream_chunk_hook instead.

http_stream_chunk_hook

Called for each chunk during streaming responses, BEFORE the chunk is written to the client. This hook allows plugins to modify or filter streaming chunks in real-time. Input:
The chunk field contains a BifrostStreamChunk struct serialized as JSON. It will contain the data from whichever response type is active:
  • Chat completion streaming: {"id":"...","object":"chat.completion.chunk","choices":[...],"model":"..."}
  • Text completion streaming: {"id":"...","choices":[...]}
  • Responses API streaming: {"type":"...","item":...}
  • Speech/Transcription/Image streaming: respective response fields
  • Error: {"error":{"type":"...","message":"..."}}
It does NOT include SSE framing (no data: prefix or \n\n suffix).
Go Native vs WASM Plugins: In Go native plugins (.so), you work directly with *schemas.BifrostStreamChunk typed structs. In WASM plugins, this struct is serialized to JSON for crossing the WASM boundary. The underlying data structure is the same.
Output (pass through unchanged):
Output (skip/filter chunk):
Output (modify chunk):
The http_stream_chunk_hook is called in reverse order of http_pre_hook, same as other post-hooks.

pre_hook

Input:
Output:
To short-circuit with a response:

post_hook

Input:
Output:

Configuration

Configure your WASM plugin in Bifrost’s config.json:
You can also load plugins from URLs:

Limitations vs Native Plugins

WASM plugins have some trade-offs compared to native Go plugins:

Source Code Reference

Complete hello-world examples are available in the Bifrost repository:

Troubleshooting

Module fails to load

Error: failed to instantiate WASM module Solution: Ensure all required exports are present. Use a WASM inspection tool:

Memory allocation errors

Error: out of memory or invalid memory access Solution:
  • Increase heap size in your allocator
  • Ensure you’re freeing memory after use
  • Check for memory leaks in long-running plugins

JSON parsing errors

Error: failed to parse input JSON Solution:
  • Validate your JSON structures match expected schemas
  • Handle optional/nullable fields properly
  • Add error logging to identify malformed data

Build errors (TinyGo)

Error: package not supported by TinyGo Solution: TinyGo doesn’t support all Go standard library packages. Avoid:
  • reflect (limited support)
  • net/http (use raw JSON instead)
  • Complex generics

Build errors (Rust)

Error: cannot find -lc Solution: For wasm32-unknown-unknown target, don’t link to libc. Ensure your Cargo.toml doesn’t require native dependencies.

Need Help?