Skip to main content

WASM plugin (L3)

The Launcher (Launcher)runs L3 plugins as WASM modules inside its Rust layer (wasmtime engine) in a Sandbox (Sandbox). They suit agents, protocol parsers, and other complex logic. Unlike L2 (frontend script), L3 runs in an independent runtime and does not depend on a browser environment.

1. How it works

  • When the Launcher starts, the Rust gateway scans plugins/{plugin id}/plugin.wasm (the Manifest layers must include l3)
  • The wasmtime engine loads the module, injects host functions (the qomicex module), and calls the functions exported by the plugin
  • The gateway port is written to plugins/.gateway_port; the backend PluginGatewayClient reads it and proxies calls
  • The frontend calls through the backend with __PLUGIN_API__.callWasm(...)

2. Plugin structure

Example Manifest:
The gateway always loads the fixed file name plugin.wasm (at plugins/{plugin id}/plugin.wasm), so you do not need to declare a path in the Manifest. entry.backend is a reserved field (currently unused); do not rely on it.

3. Plugin exports (WASM → Launcher)

4. Host API (Launcher → WASM)

Plugins reach Launcher capabilities by importing functions from the qomicex module:
  • Strings are passed as (pointer, length); the plugin manages its own wasm linear memory
  • http_fetch / instance_list are reserved today (they return -1). Use the frontend callBackend / proxyFetch for network and Instance access

5. Examples in multiple languages

A WASM plugin is a bare core module (not WASI, not a component). Feasibility by language:

Common conventions (all languages)

  • Export on_load / on_unload (() -> ()) for the gateway to call; custom exports are () -> () or () -> i32
  • Import the host functions of the qomicex module (log / db_set / db_get / get_plugin_id)
  • Strings are passed as (pointer, length); the pointer is a wasm linear memory offset (i32), not a host pointer
  • Linear memory must be exported (conventionally named memory); the gateway reads it with caller.get_export("memory")
  • Function signatures must match the registered host signatures exactly, or instantiation fails

1. Rust (verified)

Cargo.toml:
src/lib.rs:
Build:

2. C / C++ (feasible)

plugin.c:
Build (requires clang + wasm-ld, such as wasi-sdk / LLVM):
  • You must use wasm32-unknown-unknown (not wasm32-wasi, which pulls in WASI imports)
  • With -nostdlib there is no memcpy/memset; implement them yourself if you need them
  • -Wl,--export-memory exports the linear memory as memory

3. AssemblyScript (feasible)

entry.ts:
Build (requires Node.js + assemblyscript):
AssemblyScript exports memory as memory by default, which matches the gateway convention. --exportRuntime exports memory helpers such as __new/__pin; if size matters, use --runtime stub.

4. Zig (feasible)

plugin.zig:
Build:

5. Go (limited, WASI mode)

Go has only two wasm targets, js / wasip1, and no bare core module target. A wasip1 build carries the Go runtime and imports WASI, so the gateway must register WASI before it can run (the current gateway does not, so it does not work). For reference only:

6. C# / Python (not available)

  • C# (.NET): NativeAOT does not support the wasm target; the Mono browser-wasm / wasi-wasm output is the entire .NET runtime (several MB, with WASI symbols) rather than a bare core module, so it cannot meet the gateway conventions.
  • Python: there is no wasm compilation target; approaches such as Pyodide “port the CPython interpreter to wasm” instead of compiling your code into a module.
Alternative: write the WASM plugin logic in Rust / C / AssemblyScript, and use a language you know for the host side (frontend or backend).

6. Calling from a plugin or the frontend

Permission: wasm:execute

7. Backend proxy endpoints

The backend proxies the gateway capabilities as the following HTTP endpoints (the frontend API wraps them for you):

8. Notes

  • A plugin package must contain plugin.wasm and its Manifest layers must include l3 before the gateway loads it
  • plugin.wasm is a core wasm module (core module), not a WASI/component module
  • The data directory matches frontend plugins ({data dir}/plugins/{id}/) and is determined by QOMICEX_HOME or the system data directory
  • Gateway logs go to the Launcher console (prefixed [plugin] / [gateway])
  • Today db_set/db_get are a key-value store inside the gateway process and are cleared on restart; http_fetch/instance_list are reserved