Skip to main content

WASM Plugins

Overview

Otoroshi ships with a large library of built-in plugins, but there are always cases where you need custom logic that does not exist out of the box: a proprietary authentication check, a domain-specific request transformation, a compliance rule unique to your organization. Traditionally, extending a JVM-based gateway meant writing Scala code, rebuilding the project, and redeploying. WASM plugins remove that constraint entirely.

A WASM plugin lets you write gateway extension logic in any language that compiles to WebAssembly -- Rust, TinyGo, JavaScript, or AssemblyScript -- and run it inside Otoroshi without modifying or recompiling the gateway itself. The compiled .wasm binary is loaded into a sandboxed runtime (powered by Extism) that executes at near-native speed while remaining fully isolated from the host process: a plugin cannot access the filesystem, the network, or Otoroshi internals unless explicitly authorized through a fine-grained permission model.

Why a dedicated entity

WASM configuration can be embedded inline in a route's plugin chain, but that approach becomes hard to maintain when the same logic is used in many places. The WASM plugin entity solves this by letting you declare a WASM module with its configuration -- source location, memory limits, authorizations, concurrency settings -- once, under a stable ID. Any number of routes can then reference that ID instead of duplicating the configuration. Updating the entity automatically propagates the change everywhere it is used.

Key capabilities

  • Multi-language support -- Write plugins in Rust, TinyGo, JavaScript, or AssemblyScript. The companion tool Wasmo provides an in-browser editor that compiles your code to WASM so you do not need a local toolchain.
  • Sandboxed execution -- Each WASM VM runs in its own memory space. Network access, file access, and datastore access are all denied by default and must be granted through explicit authorizations.
  • Host functions -- Plugins communicate with Otoroshi through a set of host functions that give controlled access to HTTP calls, the proxy state, persistent and in-memory data stores, request/response attributes, logging, and the cluster configuration.
  • Flexible sourcing -- Load WASM binaries from a base64 string, a local file, an HTTP URL, a Wasmo instance, or by referencing another WASM plugin entity.
  • Hot reload -- WASM binaries are cached and automatically refreshed. You can update a module at its source and Otoroshi will pick up the new version without a restart.
  • Multiple instances -- Configure the number of concurrent WASM VM instances per plugin to match your throughput requirements.
  • OPA integration -- Use Open Policy Agent WASM modules directly for policy evaluation, without a separate OPA server.

When to use WASM plugins

ScenarioRecommended approach
Standard functionality (rate limiting, headers, auth delegation, ...)Use a built-in plugin -- no custom code required.
Custom logic that needs deep JVM or Otoroshi API accessWrite a Scala plugin compiled into the gateway.
Custom logic in your team's preferred language, deployed without gateway rebuildWrite a WASM plugin. Best when you want language flexibility, safe sandboxing, and independent deployment.

How it works at runtime

When a request reaches a route that references a WASM plugin, Otoroshi acquires a pre-warmed VM instance from a pool, passes the request context as a JSON structure, calls the designated function, reads back the JSON result, and applies it to the request or response. The VM instance is then returned to the pool for reuse. Data is exchanged between the JVM and the WASM VM as JSON strings through the Extism SDK, so all plugin input and output follows a well-defined schema documented in the WASM usage topic.

UI page

You can find all WASM plugins here

Properties

PropertyTypeDescription
idstringUnique identifier of the WASM plugin
namestringDisplay name of the plugin
descriptionstringDescription of the plugin
tagsarray of stringTags associated to the plugin
metadataobjectKey/value metadata associated to the plugin
stepsarray of stringThe list of proxy engine steps where this plugin can be used
configobjectThe WASM plugin configuration (see below)

Steps

The steps field defines at which points in the proxy engine pipeline this WASM plugin can be used:

StepDescription
MatchRouteCustom route matching logic
RouterCustom routing decision
SinkHandle requests that don't match any route
PreRouteRequest enrichment before routing
ValidateAccessAccess control validation
TransformRequestModify the request before forwarding to backend
TransformResponseModify the response before sending to client
HandlesRequestHandle the complete request/response lifecycle (acts as a backend)
CallBackendCustom backend call logic
HandlesTunnelHandle tunnel connections
JobPeriodic job execution

Configuration

PropertyTypeDefaultDescription
sourceobjectThe WASM source definition (see source types below)
memoryPagesnumber50Number of memory pages allocated to the WASM VM
functionNamestringName of the function to invoke (optional, defaults to step-specific name)
configobject{}Key-value configuration passed to the WASM plugin at runtime
instancesnumber1Number of concurrent WASM VM instances
wasibooleanfalseEnable WebAssembly System Interface support
opabooleanfalseEnable Open Policy Agent mode for policy evaluation
httpWasmbooleanfalseEnable HTTP WASM mode
allowedHostsarray of string[]Hostnames the WASM plugin is allowed to call via HTTP
allowedPathsobject{}File paths the WASM plugin is allowed to access (path -> permissions)
killOptionsobjectVM termination options for resource management
authorizationsobjectFine-grained access control for the WASM plugin (see below)

Source types

The source field defines where the WASM module is loaded from:

TypeDescription
base64The WASM binary encoded as a base64 string
fileA local file path pointing to a .wasm file
httpAn HTTP/HTTPS URL to download the WASM module from
wasmoA reference to a WASM module compiled and managed by a Wasmo instance
localA reference to another WASM plugin entity by ID

Authorizations

The authorizations field controls what the WASM plugin is allowed to access at runtime:

PropertyTypeDefaultDescription
httpAccessbooleanfalseAllow the plugin to make outbound HTTP calls
proxyHttpCallTimeoutnumber5000Timeout in milliseconds for HTTP calls made by the plugin
globalDataStoreAccessobject{"read": false, "write": false}Read/write access to the global persistent key-value store
pluginDataStoreAccessobject{"read": false, "write": false}Read/write access to the plugin-scoped persistent key-value store
globalMapAccessobject{"read": false, "write": false}Read/write access to the global in-memory store
pluginMapAccessobject{"read": false, "write": false}Read/write access to the plugin-scoped in-memory store
proxyStateAccessbooleanfalseAllow reading the current Otoroshi proxy state
configurationAccessbooleanfalseAllow reading the Otoroshi configuration

JSON example

A WASM plugin loaded from an HTTP URL with access to the proxy state:

{
"id": "wasm-plugin_request_validator",
"name": "Request validator",
"description": "Validates incoming requests using custom WASM logic",
"tags": ["security"],
"metadata": {},
"steps": ["ValidateAccess", "TransformRequest"],
"config": {
"source": {
"kind": "http",
"path": "https://wasm-registry.internal/plugins/request-validator.wasm"
},
"memoryPages": 50,
"functionName": null,
"config": {
"max_body_size": "1048576",
"allowed_content_types": "application/json,text/plain"
},
"instances": 2,
"wasi": false,
"opa": false,
"httpWasm": false,
"allowedHosts": ["api.internal"],
"allowedPaths": {},
"authorizations": {
"httpAccess": true,
"proxyHttpCallTimeout": 5000,
"globalDataStoreAccess": { "read": true, "write": false },
"pluginDataStoreAccess": { "read": true, "write": true },
"globalMapAccess": { "read": true, "write": false },
"pluginMapAccess": { "read": true, "write": true },
"proxyStateAccess": true,
"configurationAccess": false
}
}
}

An OPA (Open Policy Agent) plugin for policy evaluation:

{
"id": "wasm-plugin_opa_policy",
"name": "OPA access policy",
"description": "Evaluates access policies using OPA WASM module",
"tags": ["policy", "opa"],
"metadata": {},
"steps": ["ValidateAccess"],
"config": {
"source": {
"kind": "file",
"path": "/etc/otoroshi/policies/access-policy.wasm"
},
"memoryPages": 100,
"instances": 4,
"opa": true,
"wasi": false,
"config": {},
"allowedHosts": [],
"allowedPaths": {},
"authorizations": {
"httpAccess": false,
"proxyStateAccess": true,
"configurationAccess": true
}
}
}

Admin API

GET    /api/wasm-plugins           # List all WASM plugins
POST /api/wasm-plugins # Create a WASM plugin
GET /api/wasm-plugins/:id # Get a WASM plugin
PUT /api/wasm-plugins/:id # Update a WASM plugin
DELETE /api/wasm-plugins/:id # Delete a WASM plugin
PATCH /api/wasm-plugins/:id # Partially update a WASM plugin

Learn more

For a detailed overview of WASM in Otoroshi, available plugin types, and how they integrate with the proxy engine, see Otoroshi and WASM.

For practical guides: