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-pyv41+ (best security, performance, WASI 0.3 async support) - Tauri Rust (all platforms):
wasmer5.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
| Aspect | wasmtime-py ✅ | wasmer-python | wasm3 |
|---|---|---|---|
| Performance | 88% native (JIT) | 80-85% native (JIT) | 65% native (interpreter) |
| Cold Start | 5-15ms | Similar | Fastest (<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 Case | Production backend | Cloud/edge | Embedded/mobile |
Verdict: Use wasmtime-py for Morphee Python backend. Best security, performance, WASI 0.3 async.
Tauri Rust (Desktop + Mobile)
| Aspect | wasmer 5.0+ ✅ | wasmtime | wasm3 |
|---|---|---|---|
| iOS JIT | ✅ Wasmi interpreter | ❌ JIT blocked by Apple | ✅ Interpreter |
| Desktop perf | 80-88% native (Cranelift) | 88% native | 65% native |
| iOS perf | 50-60% native (Wasmi) | N/A (JIT blocked) | 65% native |
| Android perf | 80-88% native (Cranelift) | 88% native | 65% 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
| Metric | Value | Notes |
|---|---|---|
| Compilation | 5-15ms | Cache compiled modules! |
| Instantiation | <1ms | Cheap, do per-request |
| Execution | 88% native | Wasmtime JIT |
| Memory Overhead | 10-50MB | Per instance |
| Binary Size | 200KB (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)
| Runtime | Cold Start | Steady-State | Memory | Use Case |
|---|---|---|---|---|
| Wasmtime | 5-15ms | 88% native | 10-50MB | Production (Python + Rust) |
| Wasmer | 5-15ms | 80% native | 18MB | Cloud/edge |
| wasm3 | <5ms | 65% native | 10MB | Embedded/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)
- Foundation (2 weeks): ExtensionManager, WIT interface, host functions, Rust SDK
- Security (2 weeks): Permissions, resource limits, code signing, audit logs
- Distribution (1 week): OCI registry, manifest schema, CLI tools
- Developer Experience (2 weeks): SDK docs, examples, templates, local testing
- Built-in Extensions (3 weeks): JIRA, GitHub, Notion, Linear, Slack
- 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 restriction — wasmtime 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 BaseInterface
✅ JSRuntime 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)
| Runtime | Desktop (JIT) | iOS (Interpreter) | Android (JIT) | Binary Size |
|---|---|---|---|---|
| wasmer (Cranelift) | 80-88% native | — | 80-88% native | ~12MB |
| wasmer (Wasmi) | — | 50-60% native | — | ~12MB |
| wasmtime | 88% native | ❌ JIT blocked | 88% native | ~15MB |
| wasm3 | — | 65% native | 65% native | ~200KB |
Source: wasmRuntime.com benchmarks, iOS constraint: Apple App Store Review Guidelines §2.5.2
References
- Full Research: 2026-02-16-wasm-extension-research.md
- Design Document: 2026-02-16-wasm-extension-system.md
- wasmtime-py (Python backend): https://github.com/bytecodealliance/wasmtime-py
- wasmer (Tauri Rust): https://github.com/wasmerio/wasmer
- wasmer iOS support: https://wasmer.io/posts/wasmer-5
- wasm3 (future ultra-minimal): https://github.com/wasm3/wasm3
- Component Model: https://component-model.bytecodealliance.org/
- WASI: https://wasi.dev/
- WIT Spec: https://component-model.bytecodealliance.org/design/wit.html