Skip to content

WASM Plugins

scarecr0w12 edited this page Jun 23, 2026 · 1 revision

WASM Plugins

CortexPrism's WASM plugin system lets you write high-performance plugins in any language that compiles to WebAssembly — C, Rust, Zig, Go, AssemblyScript, and more.

WASM plugins run in a dedicated sandbox with synchronous host functions. The host provides logging, HTTP requests, state persistence, configuration, time, and randomness. All host ↔ plugin communication uses compiler-agnostic C-compatible function signatures over linear memory.

Quick Start

# Install a WASM plugin from marketplace
cortex plugins install @acme/wasm-plugin

# Or from a local .wasm file
cortex plugins install ./my-plugin/manifest.json

# List to verify it loaded
cortex plugins list

Manifest for a WASM plugin:

{
  "name": "my-wasm-plugin",
  "version": "1.0.0",
  "description": "A WASM plugin",
  "kind": "wasm",
  "entryPoint": "./plugin.wasm",
  "runtime": "wasm",
  "capabilities": ["tools", "network:fetch"]
}

ABI Versioning

Every WASM plugin must declare its ABI version. The host checks this on load and rejects incompatible versions. This ensures forward/backward compatibility as the host API evolves.

Current host ABI version: 1. All plugins must match.

int32_t plugin_get_abi_version(void) { return 1; }

The host also exports host_get_abi_version() so plugins can verify at runtime.

Memory Model

Address          | Size   | Owner  | Purpose
─────────────────┼────────┼────────┼────────────────────────────────────
0x000000–0x00FFFF | 64 KB  | Host   | Scratch area — temporary buffers
0x010000–0x01FFFF | 64 KB  | Host   | Allocator metadata
0x020000–0x0FFFFF | 896 KB | Host   | Managed heap (host_alloc/host_free)
0x100000+          | varies | Plugin | Your data — __heap_base starts here

Default allocation: 256 pages initial (16 MiB), 512 pages max (32 MiB). Grow automatically on demand.

Host Functions

All imported as env.*. Strings are UTF-8 passed as (pointer, length) pairs. Return 0 on success.

Memory Management

Function Signature Description
host_alloc (size: i32) → i32 Bump-allocate from host-managed heap. Returns 8-byte aligned pointer or 0 on failure.
host_free (ptr: i32) → void Free allocation (current no-op; call for forward compatibility).

Logging & Diagnostics

Function Signature Description
host_log (ptr: i32, len: i32) → void Log a message to the CortexPrism log system. Appears in server logs and Lens audit trail.
host_get_abi_version () → i32 Returns host ABI version (currently 1).
host_get_time_ms () → i64 Current time in milliseconds since Unix epoch.
host_random (outPtr: i32, len: i32) → void Fill len bytes with cryptographically random data.

Configuration & State

Function Signature Description
host_get_config (keyPtr, keyLen, outPtr, outLenPtr) → i32 Read config. Priority: CORTEX_PLUGIN_{NAME}_{KEY}CORTEX_WASM_{KEY}. Returns -1 if not found.
host_set_state (keyPtr, keyLen, valPtr, valLen) → void Persist key-value state. In-memory cache + async SQLite flush. Survives reloads.
host_get_state (keyPtr, keyLen, outPtr, outLenPtr) → i32 Read persisted state. Returns -1 if not found.

HTTP Requests

Function Signature Description
host_http_request (methodPtr, methodLen, urlPtr, urlLen, bodyPtr, bodyLen, headersPtr, headersLen, outStatusPtr, outBodyPtr, outBodyLenPtr) → i32 Synchronous HTTP request. Headers are a JSON object string. 30 s timeout. Requires network:fetch or net:outbound capability. Returns status code and body.

host_http_request is the most powerful host function. It uses a dedicated Worker thread + SharedArrayBuffer + Atomics.wait to provide truly synchronous HTTP while keeping the main thread responsive. The 30-second timeout prevents hung requests from blocking the agent loop.

Headers format: '{"Content-Type":"application/json","Authorization":"Bearer ..."}'

Plugin Exports

Required

Export Signature Purpose
plugin_get_abi_version () → i32 Return ABI version (must be 1)
plugin_get_capabilities (outPtr, outLenPtr) → i32 Return capabilities JSON
plugin_execute_tool (namePtr, nameLen, argsPtr, argsLen, outPtr, outLenPtr) → i32 Execute a tool
memory WebAssembly.Memory Linear memory

Optional

Export Signature Purpose
plugin_init () → void Called once after instantiation
plugin_destroy () → void Called on unload — free resources, flush buffers

Capabilities JSON

plugin_get_capabilities must write a JSON object to outPtr and its length to outLenPtr. Include full parameter schemas so LLMs understand what arguments your tools expect:

{
  "abi_version": 1,
  "tools": [
    {
      "name": "search_docs",
      "description": "Search documentation by keyword",
      "params": [
        { "name": "query", "type": "string", "description": "Search query", "required": true },
        { "name": "limit",  "type": "number", "description": "Max results (1-100)", "required": false }
      ]
    }
  ]
}

Parameter types: string | number | boolean | object | array

Lifecycle

Install:     fetch .wasm → instantiate → plugin_get_abi_version → plugin_init → plugin_get_capabilities
Enable:      (same as install if not already loaded)
Execute:     plugin_execute_tool (called per agent tool invocation, up to 120 s)
Disable:     plugin_destroy → free memory → clear state cache
Remove:      plugin_destroy → delete from plugins.db → remove plugin dir

Permission Model

WASM plugins have no direct filesystem, shell, or database access. Available operations are limited to the host functions listed above. host_http_request is gated on the network:fetch or net:outbound capability declared in the manifest.

If a WASM plugin attempts host_http_request without the required capability, it receives HTTP 403. All other host functions are always available.

Supply-Chain Verification

WASM binaries undergo specialized scanning during plugin installation:

Check Description
Magic bytes Must start with \x00asm
WASM version Must be version 1
Suspicious imports Blocks wasi_snapshot_preview1.proc_exit, args_get, environ_get, sock_open, sock_connect
Unknown env imports Warns on imports not recognized as host functions
Memory request Blocks > 4 GB; warns on > 64 MB
Binary size Warns if > 100 MB

The standard text-based malware scan (eval, child_process, rm -rf /) is skipped for WASM binaries since those patterns are meaningless in binary format.

SDK

C Header (src/plugins/sdk/wasm-plugin.h)

#include "wasm-plugin.h"

int32_t plugin_get_abi_version(void) { return 1; }

void plugin_init(void) {
  host_log("Ready", 5);
}

int32_t plugin_get_capabilities(char* out, uint32_t* outLen) {
  const char* json = "{\"abi_version\":1,\"tools\":[{\"name\":\"my_tool\","
    "\"description\":\"Example\",\"params\":[{\"name\":\"input\","
    "\"type\":\"string\",\"description\":\"Input\",\"required\":true}]}]}";
  uint32_t len = strlen(json);
  memcpy(out, json, len);
  *outLen = len;
  return 0;
}

int32_t plugin_execute_tool(
    const char* namePtr, uint32_t nameLen,
    const char* argsPtr, uint32_t argsLen,
    char* outPtr, uint32_t* outLen) {
  // Parse argsJson, dispatch to tool, write result
  const char* result = "{\"ok\":true}";
  uint32_t len = strlen(result);
  memcpy(outPtr, result, len);
  *outLen = len;
  return 0;
}

void plugin_destroy(void) { /* cleanup */ }

Compile with any WASM toolchain:

# C with clang
clang --target=wasm32 -nostdlib -o plugin.wasm plugin.c

# Rust
cargo build --target wasm32-unknown-unknown --release

# Zig
zig build-exe plugin.zig -target wasm32-freestanding

TypeScript Library (src/plugins/sdk/client.ts)

import { definePlugin, defineTool, generateCapabilitiesJson } from './sdk/client.ts';

const echo = defineTool({
  name: 'echo',
  description: 'Echoes input back',
  params: [{ name: 'message', type: 'string', description: 'The message', required: true }],
  execute(args) {
    return { echoed: args.message };
  },
});

export default definePlugin({ name: 'my-plugin', version: '1.0.0', tools: [echo] });

Debugging

Symptom Likely Cause
Status: error on load ABI version mismatch, missing required export, invalid WASM binary
host_http_request returns 403 network:fetch capability not declared in manifest
Tool returns "WASM trap" Plugin crashed — check for null pointer dereference, stack overflow, or infinite recursion
State lost after restart Call host_set_state to persist; in-memory-only data is not saved
Install blocked Supply-chain scan detected suspicious imports or excessive memory request

Get runtime diagnostics:

import { getWasmDiagnostics } from './wasm-runtime.ts';
const diag = getWasmDiagnostics('my-wasm-plugin');
// { memoryBytes, heapPtr, abiVersion, toolCount }

See Also

Clone this wiki locally