--- title: WASM Plugins --- # WASM Plugins WebAssembly (WASM) plugins enable custom middleware logic in SMG's request pipeline without recompiling or restarting the gateway. --- ## Overview
### :material-puzzle: Dynamic Extension Deploy custom logic at runtime via REST API. Add or remove plugins without restarting the gateway; to update one, remove it and add the new build.
### :material-shield-lock: Sandboxed Execution Plugins run in isolated WASM environments with strict resource limits. No access to host memory, filesystem, or network.
### :material-translate: Language Agnostic Write plugins in Rust, Go, C, or any language that compiles to WebAssembly Component Model.
### :material-lightning-bolt: High Performance Compiled components are cached per runtime thread, so only a component's first run on each thread pays for compilation.
--- ## How It Works WASM plugins execute in SMG's **middleware layer**, intercepting requests before they reach workers and responses before they return to clients. The middleware runs on the inference routes: `/v1/chat/completions`, `/v1/completions`, `/generate`, `/v1/responses` and `/v1/conversations` (with their sub-paths), `/v1/messages` and `/v1/messages/count_tokens`, `/v1/embeddings`, `/v1/rerank` and `/rerank`, `/v1/classify`, `/v1/interactions`, `/v1/tokenize`, `/v1/detokenize`, and the Realtime REST endpoints. It does not run on `/v1/audio/transcriptions`, the Realtime WebSocket and WebRTC routes, or the public, admin, and worker-management routes. ### Plugin Chain Execution SMG runs every plugin attached at a point, in no defined order: don't rely on deployment order. Each plugin receives the request/response and returns an action: 1. **Continue**: Pass through to the next plugin (or worker) 2. **Reject(status)**: Stop processing and return an error response immediately 3. **Modify(changes)**: Apply transformations and continue If any plugin returns `Reject`, subsequent plugins are skipped and the error response is returned to the client: on a request, the status with an empty body; on a response, the status with the response body so far. A plugin that fails or runs past its time limit is skipped, and processing continues with the next one. !!! warning "Authentication plugins fail open on errors" An `OnRequest` authentication plugin must not be the only authentication control: if it fails or times out, an unauthenticated request can continue to a worker. Configure independent [gateway authentication](../security/authentication.md), such as `--api-key`, so access remains protected when the plugin is skipped. Returning `Reject` for invalid credentials does not protect against plugin failures or timeouts. --- ## Attach Points Plugins register at specific points in the request lifecycle: | Attach Point | When | Use Cases | |--------------|------|-----------| | **OnRequest** | Before forwarding to worker | Authentication, rate limiting, validation, header injection | | **OnResponse** | After receiving worker response | Response transformation, error normalization, logging | !!! warning "Streaming responses bypass OnResponse" To avoid buffering entire streams into memory, SMG skips the **OnResponse** phase when the upstream reply is streaming — that is, when the response uses `Content-Type: text/event-stream`, `Content-Type: application/x-ndjson`, or a chunked `Transfer-Encoding`. The gateway logs a warning and passes the streaming response through untouched, so plugins attached only at OnResponse will not observe streaming traffic. OnRequest runs normally for streaming endpoints. --- ## Example Plugins SMG includes ready-to-use example plugins demonstrating common middleware patterns:
### :material-key: auth-middleware API key authentication for `/api` and `/v1` routes. - Validates `Authorization` or `x-api-key` header - Returns **401 Unauthorized** on failure - Attach point: **OnRequest**
### :material-speedometer: ratelimit-middleware Per-identifier rate limiting with configurable thresholds. - 60 requests/minute default - Tracks by API key, IP, or request ID - Returns **429 Too Many Requests** when exceeded - Attach point: **OnRequest** - SMG runs each invocation in a fresh component instance, so the example's in-memory counters don't carry over between requests; treat it as a template
### :material-file-document: logging-middleware Request tracking and response transformation. - Adds `x-request-id`, `x-wasm-processed`, `x-processed-at`, and `x-api-route` (on `/api` and `/v1` paths) - Converts 500 → 503 for better client handling - Attach points: **OnRequest** and **OnResponse**
Find complete source code and build instructions in [`examples/wasm/`](https://github.com/smg-project/smg/tree/main/examples/wasm). --- ## Plugin Development ### Interface Plugins implement the SMG middleware interface using the WebAssembly Component Model: ```rust // OnRequest: Called before forwarding to worker fn on_request(req: Request) -> Action { // Validate, modify, or reject the request Action::Continue } // OnResponse: Called after receiving worker response fn on_response(resp: Response) -> Action { // Transform or log the response Action::Continue } ``` ### Actions | Action | Effect | Example | |--------|--------|---------| | `Action::Continue` | Pass through unmodified | Logging, metrics | | `Action::Reject(401)` | Return error immediately | Auth failure | | `Action::Modify(changes)` | Apply transformations | Add headers, rewrite body | ### Request Context Plugins receive rich context for decision-making: | Field | Description | |-------|-------------| | `method` | HTTP method (GET, POST, etc.) | | `path` | Request path | | `query` | URL query string | | `headers` | All request headers | | `body` | Request body (if present) | | `request_id` | Unique request identifier | | `now_epoch_ms` | Current timestamp | --- ## Configuration ### Enabling WASM Support ```bash smg --enable-wasm --worker-urls http://worker:8000 ``` ### Runtime Settings | Setting | Value | Description | |---------|-------|-------------| | `max_memory_pages` | 1024 | Maximum memory (64KB per page = 64MB) | | `max_execution_time_ms` | 1000 | Execution timeout per invocation | | `max_stack_size` | 1 MB | Stack size per invocation | | `module_cache_size` | 10 | Compiled components cached per runtime thread | | `max_body_size` | 10 MB | Largest request or response body a plugin can receive | These values are fixed in v1.12.0; no flag changes them. Plugins run on a pool of runtime threads, one per CPU core up to 4. With an **OnRequest** plugin attached, SMG reads the whole request body before running it, and answers `400` when the body exceeds `max_body_size`. With an **OnResponse** plugin attached, it reads each non-streaming response body the same way, and answers `500` when the body is too large. --- ## Module Management Plugins are managed via the Admin API at runtime. These routes need `--enable-wasm` (without it they answer `500`) and are protected like the other admin routes. ### Deploy Plugins ```bash curl -X POST http://localhost:30000/wasm \ -H "Content-Type: application/json" \ -d '{ "modules": [ { "name": "auth-middleware", "file_path": "/plugins/auth.component.wasm", "module_type": "Middleware", "attach_points": [{"Middleware": "OnRequest"}] }, { "name": "logging-middleware", "file_path": "/plugins/logging.component.wasm", "module_type": "Middleware", "attach_points": [ {"Middleware": "OnRequest"}, {"Middleware": "OnResponse"} ] } ] }' ``` SMG loads each module before it answers. The response lists every module with an `add_result`: `{"Success": ""}`, or `{"Error": ""}` for a module that failed, in which case the status is `400`. ### List Plugins ```bash curl http://localhost:30000/wasm ``` ### Remove Plugin ```bash curl -X DELETE http://localhost:30000/wasm/{uuid} ``` --- ## Security Model WASM plugins execute in a sandboxed environment with multiple protection layers: | Layer | Protection | |-------|------------| | **Path Validation** | Only absolute paths without `.` or `..` segments, `.wasm` extension required, system directories blocked (checked again after resolving symlinks) | | **Runtime Sandboxing** | No access to host memory, filesystem, network, or system calls | | **Resource Limits** | Memory caps, execution timeouts, stack size limits | | **Deduplication** | A module whose SHA-256 hash matches a loaded module is rejected | **Blocked directories**: `/etc/`, `/proc/`, `/sys/`, `/dev/`, `/boot/`, `/root/`, `/var/log/`, `/var/run/` --- ## Performance The first run of a component on each runtime thread compiles it, and later runs on that thread reuse the compiled component. Every invocation still instantiates the component fresh. `GET /wasm` reports execution counts (total, successful, failed) and the total, average, and maximum execution time, so you can measure your plugins' overhead. --- ## What's Next?
### :material-github: Example Plugins Complete source code with build instructions. [View Examples →](https://github.com/smg-project/smg/tree/main/examples/wasm)
### :material-tools: MCP Integration Connect models to external tools. [Model Context Protocol →](mcp.md)