WASM Plugin Development

WASM (WebAssembly) plugins compile to .wasm bytecode and run in a sandboxed runtime. They offer the highest performance and strongest security isolation, making them ideal for CPU-intensive or security-sensitive operations.

Overview

WASM plugins export functions using the WASM ABI that the CortexPrism WASM runtime can call. The runtime handles memory management, type marshalling, and sandboxing.

Supported Languages

LanguageToolchainStatus
Rustwasm-pack / wasm32-wasiRecommended
GoGOOS=wasip1 GOARCH=wasmSupported
C/C++clang with wasm32-wasi targetSupported
AssemblyScriptas-compilerSupported
Zigzig build-exe -target wasm32-wasiSupported

Rust WASM Plugin (Recommended)

Setup

cargo new --lib my-wasm-plugin
cd my-wasm-plugin

Add to Cargo.toml:

[package]
name = "my-wasm-plugin"
version = "1.0.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
cortexprism-wasm-sdk = "0.1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

[profile.release]
opt-level = "s"
lto = true

Implementing Capabilities

use cortexprism_wasm_sdk::{register_capability, CapabilityContext};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct TransformInput {
    text: String,
    mode: String,
}

#[derive(Serialize)]
struct TransformOutput {
    result: String,
    length: usize,
    hash: u64,
}

fn transform_text(input: TransformInput, _ctx: CapabilityContext) -> TransformOutput {
    let result = match input.mode.as_str() {
        "reverse" => input.text.chars().rev().collect(),
        "uppercase" => input.text.to_uppercase(),
        "lowercase" => input.text.to_lowercycle(),
        "rot13" => input.text.chars().map(rot13_char).collect(),
        _ => input.text,
    };

    TransformOutput {
        length: result.len(),
        hash: simple_hash(&result),
        result,
    }
}

register_capability!("transform", transform_text);

fn simple_hash(s: &str) -> u64 {
    s.bytes().fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64))
}

Building

cargo build --release --target wasm32-wasi

The output is at target/wasm32-wasi/release/my_wasm_plugin.wasm.

Plugin Manifest

{
  "name": "text-processor",
  "version": "1.0.0",
  "type": "wasm",
  "entryPoint": "text_processor.wasm",
  "description": "High-performance text transformation plugin",
  "wasm": {
    "memoryPages": 10,
    "maxMemoryPages": 50,
    "allocator": "wasi"
  }
}

Go WASM Plugin

package main

import (
    "strings"
    "unicode/utf8"
)

//export transform
func transform(ptr uint32, len uint32) uint32 {
    input := readMemory(ptr, len)
    result := strings.ToUpper(input)
    return writeMemory(result)
}

//export count_tokens
func countTokens(ptr uint32, len uint32) uint32 {
    input := readMemory(ptr, len)
    count := utf8.RuneCountInString(input) / 4
    if count < 1 {
        count = 1
    }
    return count
}

func main() {}

Build:

GOOS=wasip1 GOARCH=wasm go build -o plugin.wasm main.go

Capability ABI

Each exported WASM function follows this signature convention:

(func (param i32 i32) (result i32))
  • Input pointer (i32): Pointer to JSON-encoded input arguments in shared memory
  • Input length (i32): Length of the input buffer
  • Return value (i32): Pointer to JSON-encoded result in shared memory

The runtime handles memory allocation and deallocation.

Memory Model

  • WASM plugins operate on a linear memory space
  • The runtime allocates an initial number of pages (64KB each)
  • The plugin can request additional pages up to the configured maximum
  • String data is passed through shared memory using the WASI allocator or a custom allocator
  • Memory is cleared between capability calls for isolation

Performance Characteristics

OperationESMWASM
Cold start~10ms~1ms
Text processing (1MB)~50ms~5ms
JSON parse (100KB)~2ms~0.3ms
Memory overhead~30MB~5MB

Security

  • WASM runs in a sandboxed runtime with no access to the host system
  • Memory isolation: each call gets a fresh memory space
  • Resource limits: configurable memory pages and execution time
  • No network access by default (can be enabled via permissions)
  • System call filtering via WASI preview 1

Testing

import { testWasmPlugin } from "@cortexprism/plugin-sdk/testing";

const plugin = await testWasmPlugin("text_processor.wasm");

const result = await plugin.call("transform", {
  text: "hello world",
  mode: "uppercase",
});

console.assert(result.result === "HELLO WORLD");

Debugging

Enable WASM runtime logging:

cortex --verbose plugin call text-processor transform '{"text":"hello","mode":"reverse"}'

For Rust plugins, use wasm32-wasi debugging tools:

wasm2wat plugin.wasm | less    # Inspect the WASM module
wasmtime run plugin.wasm       # Test outside CortexPrism