Skip to main content

WASM Extension System — Quick Reference

Last Updated: 2026-02-20 (updated for iOS JIT restriction + BaseMorphRuntime concept) Full Research: 2026-02-16-wasm-extension-research.md Design Document: 2026-02-16-wasm-extension-system.md


TL;DR

Recommended Stack:

  • Python Backend: wasmtime-py v41+ (best security, performance, WASI 0.3 async support)
  • Tauri Rust (all platforms): wasmer 5.0+ (unified API — Cranelift JIT on desktop/Android, Wasmi interpreter on iOS)
  • Interface Standard: WebAssembly Component Model + WASI 0.3
  • Distribution: OCI registry (GitHub Container Registry)
  • Security: Capability-based permissions + resource limits + code signing

Why wasmer for Tauri (not wasmtime)? iOS prohibits JIT compilation. Wasmtime's JIT mode doesn't work on iOS at all — it requires interpreter or AOT mode, which wasmtime doesn't optimize for mobile. Wasmer 5.0 solves this with pluggable backends: Cranelift JIT on desktop/Android (80-88% native) and Wasmi interpreter on iOS (50-60% native), all via a single unified API.


Decision Matrix

Python Backend

Aspectwasmtime-py ✅wasmer-pythonwasm3
Performance88% native (JIT)80-85% native (JIT)65% native (interpreter)
Cold Start5-15msSimilarFastest (<5ms)
Security✅ Best (rigorously tested)⚠️ Good✅ Good (simple)
WASI Support✅ 0.2 + 0.3 (async)✅ 0.2⚠️ Limited
Component Model✅ Full support⚠️ Partial❌ No
Binary Size~10MB~8MB~64KB
Use CaseProduction backendCloud/edgeEmbedded/mobile

Verdict: Use wasmtime-py for Morphee Python backend. Best security, performance, WASI 0.3 async.

Tauri Rust (Desktop + Mobile)

Aspectwasmer 5.0+ ✅wasmtimewasm3
iOS JIT✅ Wasmi interpreter❌ JIT blocked by Apple✅ Interpreter
Desktop perf80-88% native (Cranelift)88% native65% native
iOS perf50-60% native (Wasmi)N/A (JIT blocked)65% native
Android perf80-88% native (Cranelift)88% native65% native
Unified API✅ Same API, pluggable backend❌ Needs platform branches❌ Different API
Async support✅ Yes✅ Yes (Tokio)❌ Sync only
Binary size~12MB~15MB~200KB
Future mobile🔲 wasm3 as ultra-minimal option✅ 64KB

Verdict: Use wasmer 5.0 for all Tauri targets. Single API, pluggable backends solve the iOS JIT problem. wasm3 is tracked as a future ultra-minimal mobile option.


Runtime Architecture (BaseMorphRuntime)

The WASM Extension System introduces BaseMorphRuntime — an abstract class that backs dynamic Integrations. This mirrors how Python integrations work (direct Python code), but for any portable runtime.

InterfaceManager
├── Python Integrations (hardcoded, direct Python code)
│ ├── LLMIntegration, TasksIntegration, CronIntegration...
│ └── (14 built-in integrations)

└── BaseMorphRuntime-backed Integrations (dynamic, installed)
├── WasmIntegration("jira.wasm") ← WasmRuntime (wasmer 5.0)
├── WasmIntegration("github.wasm") ← WasmRuntime (wasmer 5.0)
├── JsIntegration("canvas-ui.js") ← JSRuntime (frontend canvas/UI)
└── PythonIntegration("custom.py") ← PythonRuntime [future]

WasmRuntime (wasmer 5.0) — extends BaseMorphRuntime

Backend integrations compiled to .wasm — JIRA, GitHub, Notion, Linear, Slack.

  • Desktop/Android: Cranelift JIT (80-88% native speed)
  • iOS: Wasmi interpreter (50-60% native, JIT not allowed by Apple)
  • Future: wasm3 as ultra-minimal mobile option (64KB, 65% native, no async)

JSRuntime — extends BaseMorphRuntime

Frontend integrations written in JavaScript — dynamic canvas components, custom UI integrations, interactive renderers. These run in the browser/Tauri webview context.

  • Canvas components (V1.0 canvas-first redesign)
  • Dynamic UI integrations (custom renderers)
  • Each integration declares its ComponentSpec actions + UI events

PythonRuntime (future) — extends BaseMorphRuntime

Dynamic Python scripts loaded at runtime without modifying the server — same sandboxed approach as WASM but for Python.


WASM Extension Contract

Each .wasm extension must export the same two functions that mirror Python's BaseInterface:

// The extension declares its Integration definition
export describe: func() -> string; // Returns JSON: name, version, actions[], events[], config_schema

// The extension executes an action (mirrors BaseInterface.execute())
export execute: func(action: string, params: string, context: string) -> result<string, string>;

This is the same contract as BaseInterface.get_actions() + execute() — WASM extensions are just Python integrations compiled to a portable binary.


Installation

Python

pip install wasmtime==41.0.0

Rust (wasmer with platform-adaptive backends)

[dependencies]
# Unified wasmer: Cranelift on desktop/Android, Wasmi on iOS
wasmer = { version = "5", features = ["cranelift", "wasmi"] }
tokio = { version = "1", features = ["full"] }

Minimal Example (Python)

from wasmtime import Store, Module, Instance, Engine

# Compile module
engine = Engine()
module = Module.from_file(engine, "extension.wasm")

# Instantiate
store = Store(engine)
instance = Instance(store, module, [])

# Call function
run = instance.exports(store)["execute"]
result = run(store, "create_issue", [("title", "Bug"), ("description", "Fix it")])

Minimal Example (Rust — wasmer with platform-adaptive backend)

use wasmer::*;

fn run_extension(wasm_bytes: &[u8]) -> Result<()> {
// Platform-adaptive backend selection
#[cfg(target_os = "ios")]
let compiler = wasmer::Singlepass::default(); // Wasmi interpreter — iOS JIT restriction

#[cfg(not(target_os = "ios"))]
let compiler = wasmer::Cranelift::default(); // JIT on desktop/Android

let mut store = Store::new(compiler);
let module = Module::new(&store, wasm_bytes)?;

let import_object = imports! {};
let instance = Instance::new(&mut store, &module, &import_object)?;

let execute = instance.exports.get_function("execute")?;
execute.call(&mut store, &[])?;

Ok(())
}

Performance Characteristics

MetricValueNotes
Compilation5-15msCache compiled modules!
Instantiation<1msCheap, do per-request
Execution88% nativeWasmtime JIT
Memory Overhead10-50MBPer instance
Binary Size200KB (optimized)After wasm-opt -Oz

Key Optimization: Cache compiled modules, instantiate fresh stores per execution.


Security Model

Permission Types

class ExtensionPermission(str, Enum):
HTTP_READ = "http:read"
HTTP_WRITE = "http:write"
VAULT_READ = "vault:read"
VAULT_WRITE = "vault:write"
EVENT_EMIT = "event:emit"
EVENT_LISTEN = "event:listen"
FS_READ = "fs:read"
FS_WRITE = "fs:write"
DATA_READ = "data:read"
DATA_WRITE = "data:write"

Resource Limits

class ResourceLimits(BaseModel):
max_memory_mb: int = 128
max_execution_ms: int = 30_000
max_http_requests: int = 100
max_file_size_mb: int = 10

Enforcement

from wasmtime import Config, ResourceLimiter

config = Config()
config.consume_fuel = True # CPU limits
engine = Engine(config)
store = Store(engine)
store.set_limits(ExtensionResourceLimiter(limits))
store.add_fuel(limits.max_execution_ms * 1000)

WIT Interface (Simplified)

package morphee:extensions@1.0.0;

interface http {
record request {
method: string,
url: string,
headers: list<tuple<string, string>>,
body: option<list<u8>>,
}

record response {
status: u32,
body: list<u8>,
}

fetch: func(req: request) -> result<response, string>;
}

interface vault {
get-secret: func(key: string) -> option<string>;
}

interface events {
emit: func(level: string, message: string);
}

world morphee-extension {
import http;
import vault;
import events;

export execute: func(action: string, params: list<tuple<string, string>>) -> result<string, string>;
}

Extension Manifest

extension_id: "jira-integration"
version: "1.2.0"
schema_version: "1.0"

metadata:
name: "JIRA Integration"
author: "Atlassian"
homepage: "https://morphee.app/extensions/jira"

permissions:
- http:read
- http:write
- vault:read
- event:emit
- data:write

resource_limits:
max_memory_mb: 256
max_execution_ms: 60000
max_http_requests: 500

actions:
- name: "create_issue"
parameters:
- name: "title"
type: "string"
required: true
- name: "description"
type: "string"
ai_access: "execute" # LLM can call without approval

Distribution (OCI Registry)

Push Extension

oras push ghcr.io/morphee/extensions/jira:1.2.0 \
extension.wasm:application/vnd.wasm.module.v1+wasm \
manifest.yaml:application/vnd.morphee.extension.manifest.v1+yaml

Sign Extension

cosign sign --key developer.key ghcr.io/morphee/extensions/jira:1.2.0

Pull & Verify

oras pull ghcr.io/morphee/extensions/jira:1.2.0
cosign verify --key developer.pub ghcr.io/morphee/extensions/jira:1.2.0

SDK Pattern (Rust)

// Extension author's code
use morphee_sdk::{Extension, http, vault, events};

struct JiraExtension;

impl Extension for JiraExtension {
fn execute(&self, action: &str, params: Vec<(String, String)>) -> Result<String, String> {
match action {
"create_issue" => {
let api_token = vault::get_secret("jira_api_token")?;

let resp = http::fetch(http::Request {
method: "POST".to_string(),
url: "https://mycompany.atlassian.net/rest/api/3/issue".to_string(),
headers: vec![("Authorization".to_string(), format!("Bearer {}", api_token))],
body: Some(format!(r#"{{"fields": {{"summary": "..."}}}}"#).into_bytes()),
})?;

events::emit("info", "JIRA issue created");
Ok(String::from_utf8(resp.body).unwrap())
}
_ => Err(format!("Unknown action: {}", action))
}
}
}

export_extension!(JiraExtension);

Build

cargo build --target wasm32-wasi --release
wasm-opt -Oz -o jira.wasm target/wasm32-wasi/release/jira.wasm

Real-World Benchmarks (2026)

RuntimeCold StartSteady-StateMemoryUse Case
Wasmtime5-15ms88% native10-50MBProduction (Python + Rust)
Wasmer5-15ms80% native18MBCloud/edge
wasm3<5ms65% native10MBEmbedded/mobile

Source: wasmRuntime.com benchmarks


Code Caching Strategy

Problem

Compilation is expensive (5-15ms per module).

Solution

# Compile once
module = Module.from_file(engine, "extension.wasm")

# Serialize compiled module
with open("extension.compiled", "wb") as f:
f.write(module.serialize())

# Later: deserialize (instant, no compilation)
with open("extension.compiled", "rb") as f:
module = Module.deserialize(engine, f.read())

Result: <1ms instantiation (vs 5-15ms compilation).


Binary Optimization

# Rust release build
cargo build --target wasm32-wasi --release # ~500KB

# Optimize for size
wasm-opt -Oz input.wasm -o output.wasm # ~200KB (60% smaller)

# Strip debug info
wasm-strip output.wasm # ~180KB

# Gzip for transport
gzip output.wasm # ~80KB (75% smaller than release)

Typical Results:

  • Debug build: 2.5MB
  • Release build: 500KB
  • Optimized: 200KB
  • Gzipped: 80KB

Implementation Roadmap (12 weeks)

  1. Foundation (2 weeks): ExtensionManager, WIT interface, host functions, Rust SDK
  2. Security (2 weeks): Permissions, resource limits, code signing, audit logs
  3. Distribution (1 week): OCI registry, manifest schema, CLI tools
  4. Developer Experience (2 weeks): SDK docs, examples, templates, local testing
  5. Built-in Extensions (3 weeks): JIRA, GitHub, Notion, Linear, Slack
  6. Marketplace (2 weeks): Catalog UI, permission approval, auto-updates, analytics

Key Takeaways

Python backend: wasmtime-py — best security, WASI 0.3 async, unchanged ✅ Tauri Rust: wasmer 5.0 — unified API across desktop/Android/iOS; Cranelift JIT or Wasmi interpreter per platform ✅ iOS JIT restrictionwasmtime JIT is blocked by Apple; wasmer Wasmi interpreter works fine ✅ BaseMorphRuntime — WasmRuntime/JSRuntime/PythonRuntime all extend it; dynamic integrations look identical to built-in Python integrations from InterfaceManager's perspective ✅ WASM = same contract as Python — extensions export describe() + execute() just like BaseInterfaceJSRuntime for frontend — canvas components and dynamic UI integrations run as JavaScript in the webview ✅ Component Model + WASI 0.3 (rich types, async support) ✅ Cache compiled modules (5-15ms compilation → <1ms instantiation) ✅ Capability-based security (deny-by-default, explicit permissions) ✅ OCI registry distribution (versioning, signing, immutability) ✅ Resource limits are critical (memory, CPU, rate limiting) ✅ Code signing is mandatory (verify before loading) 🔲 Future: wasm3 — ultra-minimal (64KB) interpreter for iOS, no async; tracks as possible alternative to wasmer Wasmi


Performance Benchmarks (Updated 2026-02-20)

RuntimeDesktop (JIT)iOS (Interpreter)Android (JIT)Binary Size
wasmer (Cranelift)80-88% native80-88% native~12MB
wasmer (Wasmi)50-60% native~12MB
wasmtime88% native❌ JIT blocked88% native~15MB
wasm365% native65% native~200KB

Source: wasmRuntime.com benchmarks, iOS constraint: Apple App Store Review Guidelines §2.5.2


References