| Crates.io | cardinal-wasm-plugins |
| lib.rs | cardinal-wasm-plugins |
| version | 0.2.41 |
| created_at | 2025-09-26 03:09:20.610744+00 |
| updated_at | 2025-11-28 16:37:18.623199+00 |
| description | Host runtime for Cardinal's WebAssembly middleware |
| homepage | https://github.com/andrespirela/cardinal |
| repository | https://github.com/andrespirela/cardinal |
| max_upload_size | |
| id | 1855371 |
| size | 119,900 |
cardinal-wasm-plugins is the host runtime that executes WebAssembly middleware inside Cardinal. It is responsible for loading modules, wiring the import surface, and running the guest code in either inbound or outbound mode.
CardinalProxy
│
├─ inbound middleware → WasmRunner (ExecutionType::Inbound)
│ • read-only access to headers/query/body
│ • returns `should_continue` (0/1)
│
└─ outbound middleware → WasmRunner (ExecutionType::Outbound)
• can mutate response headers/status
• observes the request context as well
The canonical entry point exported by a WASM module is handle(ptr: i32, len: i32) -> i32. The return value maps to should_continue (1) or responded (0). __new (AssemblyScript) or a compatible allocator must also be present so the host can write the request body into guest memory when needed.
WasmPlugin: loads bytes from disk (or memory), validates required exports, and remembers the configured memory/handle symbols.WasmInstance: wraps the instantiated module, guest memory, and FunctionEnv<ExecutionContext> so host imports can mutate state.ExecutionContext: enum with Inbound and Outbound variants. Inbound mode surfaces ExecutionRequest (headers, query string, optional body). Outbound mode extends that with ExecutionResponse (mutable resp_headers, status).WasmRunner: orchestrates a run—copying the current context into the guest, invoking handle, and harvesting results.| Import | Mode | Description |
|---|---|---|
get_header(name, out_ptr, out_cap) |
inbound + outbound | copy a header value into guest memory; returns byte count or -1 |
get_query_param(key, out_ptr, out_cap) |
inbound + outbound | similar to get_header, but for query parameters |
set_header(name, value) |
outbound only | stage a response header to be written back to Pingora |
set_status(code) |
outbound only | override the HTTP status sent to the client |
abort(code, msg_ptr, msg_len) |
both | abort execution; surfaces as CardinalError::InternalError(InvalidWasmModule) |
Inbound code is intentionally read-only: it can veto a request by returning 0, but it cannot mutate headers/state on the way to the upstream backend.
The crate’s unit tests load fixtures from tests/wasm-plugins/<case>:
📁 tests/wasm-plugins/
├─ allow/
│ ├─ plugin.ts (AssemblyScript source)
│ ├─ plugin.wasm (compiled)
│ ├─ incoming_request.json
│ └─ expected_response.json
├─ inbound-allow/
├─ inbound-block/
└─ outbound-tag/
incoming_request.json feeds ExecutionContext, while expected_response.json specifies:
{
"execution_type": "outbound", // or "inbound"
"should_continue": true,
"status": 200,
"resp_headers": {
"x-example": "value"
}
}
For inbound tests, status/resp_headers must be omitted; the runner enforces this to keep fixtures honest.
tests/wasm-plugins/compile.sh or equivalent tooling (npx asc plugin.ts -o plugin.wasm --optimize --exportRuntime)..wasm file in cardinal-config:[[plugins]]
wasm = { name = "audit", path = "filters/audit/plugin.wasm" }
During runtime, PluginContainer loads the module, and PluginRunner invokes it in the appropriate phase.
WasmRunner::run returns CardinalError variants when:
InvalidWasmModule)abortCallers should treat these errors as fatal—Cardinal responds with 500 and logs the failure.
src/host/ (mirroring the existing modules). Update make_imports so the appropriate functions are only exposed in the modes that make sense.handle, allocator). The current implementation assumes linear memory via Wasmer 6.