Skip to content

Wasm Plugins

Wasm plugins let platform teams add custom request, response, or stream processing without rebuilding the data plane. Plugins are declared with WasmPlugin resources and executed inside the Rust data plane runtime.

A WasmPlugin has four main parts:

PartFieldsPurpose
Module sourcewasm.url, wasm.configMap, wasm.inline, wasm.sha256Locates and verifies the .wasm module.
Target bindingtargetRefsBinds the plugin to local Gateway API target resources.
Hook selectionhooksSelects onRequest, onResponse, or onStreamChunk.
Sandbox limitssandboxBounds memory, execution time, network, and filesystem access.
apiVersion: gateway.nantian.dev/v1alpha1
kind: WasmPlugin
metadata:
name: request-audit
namespace: nantian-demo
spec:
wasm:
url: https://example.com/plugins/request-audit.wasm
sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: echo
hooks:
- onRequest
- onResponse
config: |
{"mode":"audit"}
sandbox:
maxMemoryBytes: 67108864
maxExecutionTimeMs: 100
allowNetwork: false
allowFileSystem: false

Use wasm.url when modules are stored in an artifact repository, wasm.configMap when you want Kubernetes to own the module bytes, and wasm.inline only for small test modules. Set wasm.sha256 for supply-chain integrity whenever the source is external.

targetRefs selects where the plugin runs. Start with a narrow target such as a single HTTPRoute, validate behavior, and then expand scope. If a target is missing or not accepted, inspect the control plane logs and the resource status.

  • onRequest runs before upstream proxying and is useful for custom authentication, request validation, and request enrichment.
  • onResponse runs before the response returns to the client and is useful for response headers, audit metadata, and response validation.
  • onStreamChunk runs for streaming chunks and should keep execution time low.

Plugins should return quickly. Long-running or blocking behavior will increase route latency and can trip sandbox deadlines.

Set explicit sandbox values for production:

  • maxMemoryBytes limits plugin heap usage.
  • maxExecutionTimeMs limits hook execution time.
  • allowNetwork should stay false unless the plugin explicitly requires network access.
  • allowFileSystem should stay false unless the plugin explicitly requires filesystem access.

The data plane runtime enforces plugin loading and execution boundaries, but sandbox settings are still part of your production risk model.

  • Enable experimental features and verify wasmplugins.gateway.nantian.dev.
  • Test the plugin against a non-critical route first.
  • Pin external modules with sha256.
  • Use the smallest practical targetRefs scope.
  • Keep sandbox network and filesystem access disabled by default.
  • Watch data plane logs and metrics for plugin load failures, rejections, and latency.

Wasm plugins follow the standard WebAssembly module format and must export specific symbols that the data plane runtime invokes:

Every plugin must export these WebAssembly symbols:

  • alloc(size: i32) -> i32: Allocates memory for data passed between the host and plugin. Returns a pointer to the allocated region.
  • memory: The plugin’s linear memory instance, through which the host reads and writes data.
  • At least one hook function: on_request, on_response, or on_stream_chunk.

Each hook function receives request metadata and headers from the host:

on_request(context_ptr: i32, context_len: i32) -> i32
on_response(context_ptr: i32, context_len: i32) -> i32
on_stream_chunk(context_ptr: i32, context_len: i32) -> i32

The context data is a JSON-encoded structure containing request or response headers, the backend target, and plugin configuration.

  • Return 0 to allow the request to continue normally.
  • Return a non-zero HTTP status code to reject the request with that status. For example, returning 403 rejects with HTTP 403 Forbidden. The rejection message can be set via a header in the context response.

The plugin reads request headers and body through the context structure passed by the host. Response hooks receive the upstream response headers and status code. Stream chunk hooks receive individual chunk data for SSE-based streaming.

The config field on the WasmPlugin resource is passed to the plugin as a JSON string at load time:

spec:
config: |
{"mode":"audit","log_level":"debug","allowed_roles":["admin","viewer"]}

The plugin parses this JSON during initialization and can use it to control behavior without recompilation.

Executes before the request is proxied to the upstream backend. Use this hook for:

  • Custom authentication and authorization checks.
  • Request validation against a schema or policy.
  • Header injection or modification (add tracing headers, tenant IDs, etc.).
  • Early rejection of malformed or unauthorized requests.

Executes after receiving the upstream response, before it returns to the client. Use this hook for:

  • Response header manipulation (strip internal headers, add security headers).
  • Audit metadata attachment.
  • Response body validation or transformation.
  • Rate-limit accounting and logging.

Executes on each chunk of an SSE (Server-Sent Events) streaming response. Use this hook for:

  • Streaming content inspection and filtering.
  • Per-token processing for LLM streaming responses.
  • Chunk-level metadata injection.

Keep chunk processing extremely fast. Streaming latency is amplified across every chunk, so even small delays per chunk add up quickly.

When multiple hooks are specified on a single plugin, they execute in this order:

  1. onRequest (before upstream)
  2. onResponse (after upstream, before client)
  3. onStreamChunk (per chunk, only for streaming responses)

If multiple plugins are bound to the same target, each hook type runs across all matching plugins before the next hook type begins.

The sandbox enforces resource limits on each plugin to protect the data plane from runaway or malicious modules:

FieldTypeDescription
maxMemoryBytesintegerMaximum heap memory the plugin can allocate. Example: 67108864 for 64 MiB.
maxExecutionTimeMsintegerMaximum wall-clock time a single hook invocation may run. Example: 100 for 100 ms.
allowNetworkboolWhether the plugin can make outbound network calls. Default: false.
allowFileSystemboolWhether the plugin can read or write to the filesystem. Default: false.
spec:
sandbox:
maxMemoryBytes: 67108864
maxExecutionTimeMs: 100
allowNetwork: false
allowFileSystem: false
  • Start with conservative limits and relax only when profiling shows need.
  • Keep allowNetwork and allowFileSystem disabled unless the plugin’s documented requirements explicitly call for them.
  • Monitor plugin execution times through Wasm metrics and increase maxExecutionTimeMs if near-limit warnings appear in logs.

The WasmPlugin custom resource is defined under gateway.nantian.dev/v1alpha1. After installing the CRD, verify it is registered:

Terminal window
kubectl get crd wasmplugins.gateway.nantian.dev

Choose a module source based on your deployment workflow:

  • wasm.url: Pull the module from an HTTP/HTTPS artifact repository. Recommended for production when combined with sha256 verification.
  • wasm.configMap: Store the module bytes inline in a Kubernetes ConfigMap. Useful for GitOps workflows where modules are committed alongside manifests.
  • wasm.inline: Embed the module bytes directly in the WasmPlugin resource. Use only for small test modules due to resource size limits.
spec:
wasm:
url: https://artifacts.example.com/plugins/request-audit-v1.2.0.wasm
sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef

targetRefs determines which Gateway API resources execute the plugin. Supported target kinds include HTTPRoute, GRPCRoute, and Gateway. Start with a single HTTPRoute, validate behavior, then expand scope.

  • Pin external modules with sha256 to ensure the exact module version is loaded.
  • Updating a plugin creates a new deployment cycle in the data plane. Existing in-flight requests continue with the old module until they complete.
  • Test plugin updates against a canary route before rolling out to production routes.
ErrorCauseFix
Missing exportsModule does not export alloc, memory, or hook functions.Verify the module was compiled for the Nantian Wasm ABI.
Execution timeoutHook exceeded maxExecutionTimeMs.Increase the timeout or optimize the hook code.
Memory exceededPlugin exceeded maxMemoryBytes.Increase the limit or reduce allocations.
Module load failureInvalid .wasm bytes or broken URL.Verify module source and sha256 checksum.
Target not foundtargetRefs points to a missing resource.Confirm the target resource exists and is in the correct namespace.

Plugins can write diagnostic output through the data plane’s logging infrastructure. Messages appear in the data plane pod logs tagged with the plugin name and hook:

[wasm:request-audit][onRequest] processing request for /v1/chat/completions
[wasm:request-audit][onRequest] audit mode: request accepted

Increase log verbosity in the data plane configuration to see plugin-level debug messages.

To test plugins before cluster deployment:

  1. Use the Nantian CLI to build a plugin skeleton with the required exports.
  2. Compile the plugin with the wasm32-wasi target.
  3. Run the data plane locally in development mode with the plugin loaded via a test configuration.
  4. Send test requests and inspect log output and behavior.

For a full walkthrough, see the WASM Plugin Development Guide.