WebAssembly Extension Architecture Research
ARCHIVE — This document is historical reference only. It may contain outdated information. See docs/status.md for current project state.
Date: 2026-02-16 Status: Research Complete — See 2026-02-16-wasm-extension-system.md for the updated design decisions Target: Phase 3l — Extensibility & Partnerships (WASM-based third-party extensions)
⚠️ Updated 2026-02-20: The Tauri Rust runtime recommendation has changed from
wasmtimetowasmer5.0. iOS App Store §2.5.2 prohibits JIT compilation — wasmtime's JIT is blocked on iOS. Wasmer 5.0 provides pluggable backends (Cranelift JIT on desktop/Android, Wasmi interpreter on iOS) via a unified API. See the design document and quick reference for the current decisions.
Executive Summary
This document provides comprehensive research on WebAssembly (WASM) runtimes and extension architectures for Morphee's hybrid Python (FastAPI) + Rust (Tauri) application. The goal is to enable third-party integrations (JIRA, GitHub, Notion, etc.) as sandboxed WASM extensions that can run on both backend and frontend with secure, performant execution.
Key Recommendations (as of 2026-02-20):
- Python Backend: Use
wasmtime-py(superior security, performance, and WASI support) — unchanged - Tauri Rust: Use
wasmer5.0 (unified API across all platforms — Cranelift JIT on desktop/Android, Wasmi on iOS) - Interface Standard: WebAssembly Component Model + WASI 0.3 (async support, rich types)
- Security Model: Capability-based permissions + resource limits + deny-by-default sandboxing
- Distribution: OCI registry-based (versioning + signing), similar to container images
- BaseMorphRuntime: WasmRuntime (wasmer), JSRuntime (frontend canvas/UI), PythonRuntime (future)
1. WASM Runtimes for Python
1.1 wasmtime-py (RECOMMENDED ✅)
Maintainer: Bytecode Alliance (Mozilla, Fastly, Intel, Microsoft) Version: 41.0.0 (Jan 2026), monthly releases License: Apache 2.0
Pros
- Superior Performance: Leads Wasmer for both cold start and steady-state execution (2026 benchmarks)
- Security-First: Most rigorously tested for vulnerabilities, prioritizes security over raw performance
- WASI Support: Full WASI 0.2 and 0.3 support (async, component model)
- Component Model: Native support for WebAssembly components with high-level bindings
- Binary Distribution: Pre-built wheels for Windows, macOS, Linux on x86-64 and ARM64
- No Native Toolchain: Requires only Python interpreter, no C compiler needed
- Active Development: Monthly releases, aligned with upstream Wasmtime
Cons
- Compilation/Instantiation Coupling: Module compilation and instantiation are bound in one Store, requiring re-compilation for each disposable instance (workaround: use
serialize/deserializemethods to detach compiled modules) - Larger Binary: Includes full JIT compiler (~5-10MB per platform)
Code Example
from wasmtime import Store, Module, Instance, Func, FuncType, ValType
# Create engine and store
engine = Engine()
store = Store(engine)
# Compile module (expensive, cache this)
module = Module.from_file(engine, "extension.wasm")
# Define host function (Python → WASM)
def log_message(msg_ptr: int, msg_len: int):
memory = instance.exports(store)["memory"]
msg = memory.read(store, msg_ptr, msg_len).decode('utf-8')
print(f"Extension log: {msg}")
log_func = Func(store, FuncType([ValType.i32(), ValType.i32()], []), log_message)
# Instantiate with imports
instance = Instance(store, module, [log_func])
# Call exported function
run = instance.exports(store)["run"]
run(store)
Component Model Support (High-Level Bindings)
# Generate Python bindings from WIT
# $ python -m wasmtime.bindgen component.wasm --out-dir ./bindings
from bindings import Root
# Use component with rich types
component = Root(store)
result = component.greet("Alice") # Pass string, not raw memory
print(result) # Get string back, not pointer
Source: wasmtime-py GitHub, Wasmtime Python Docs, PyPI wasmtime
1.2 wasmer-python
Maintainer: Wasmer Inc. (commercial entity) License: MIT
Pros
- Cloud Services: Wasmer Edge for distributed Wasm execution
- Better Performance (Sometimes): May outperform Wasmtime in specific workloads
- Multi-Language SDKs: Consistent API across Python, Rust, Go, PHP, Ruby, C, C++
Cons
- Less Secure: Lower security testing rigor compared to Wasmtime
- Commercial Focus: Development prioritizes Wasmer Cloud/Edge products
- Component Model: Less mature component model support
- Documentation: Less comprehensive Python docs
Verdict: Use for cloud-hosted extensions (Wasmer Edge), but prefer wasmtime-py for embedded runtime.
Source: Wasmer vs Wasmtime, Comparing Runtimes
1.3 Other Options
- wasm3-py: Lightweight interpreter (not JIT), good for embedded/constrained environments (IoT), but 30-35% slower than Wasmtime
- py2wasm: Compile Python TO Wasm (inverse direction, not relevant for hosting extensions)
- Pyodide: Full CPython in browser (too heavy for sandboxing third-party code)
Source: WebAssembly Runtimes Compared, Sandboxing Python with WASM
2. WASM Runtimes for Rust
2.1 wasmtime (RECOMMENDED ✅)
Maintainer: Bytecode Alliance Version: Aligned with wasmtime-py (monthly releases) License: Apache 2.0
Pros
- Industry Standard: Used by Shopify, Cloudflare Workers, NGINX Unit, Fastly Compute
- Comprehensive API: Full embedding API with async support (
tokio) - WASI Implementation: Built on
tokioandcap-stdfor native platform equivalents - Component Model: First-class support via
wasmtime-wasicrate - Fast on ARM: Optimized for ARM CPUs (important for mobile/embedded)
- Latest Proposals: Early access to WebAssembly proposal implementations
Cons
- Binary Size: Includes JIT compiler (~10-15MB)
- Compilation Time: Slower compile times for dependencies (large crate graph)
Code Example
use wasmtime::*;
use wasmtime_wasi::{WasiCtx, WasiCtxBuilder};
// Configure engine
let engine = Engine::default();
// Compile module (cache this)
let module = Module::from_file(&engine, "extension.wasm")?;
// Create WASI context with capabilities
let wasi = WasiCtxBuilder::new()
.inherit_stdio()
.preopened_dir(Dir::open_ambient_dir("/data", ambient_authority())?, "/data")?
.build();
// Create store with WASI
let mut store = Store::new(&engine, wasi);
// Add WASI to linker
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |ctx| ctx)?;
// Instantiate
let instance = linker.instantiate(&mut store, &module)?;
// Call function
let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
run.call(&mut store, ())?;
Async Support
// For async operations (e.g., HTTP requests from extensions)
use wasmtime_wasi::add_to_linker_async;
#[tokio::main]
async fn main() -> Result<()> {
let mut linker = Linker::new(&engine);
add_to_linker_async(&mut linker, |ctx| ctx)?;
let instance = linker.instantiate_async(&mut store, &module).await?;
// ... call async functions
}
Source: Wasmtime Rust Docs, WASI Example, wasmtime_wasi crate
2.2 wasmer
Similar to wasmer-python: Commercial focus, good performance, cloud services.
Use Case: If you need Wasmer Edge for distributed execution, use Wasmer runtime. Otherwise, prefer Wasmtime.
2.3 wasm3
Architecture: Pure C interpreter (no JIT), portable, tiny (~64KB code, ~10KB RAM)
Pros
- Ultra-Lightweight: Perfect for IoT, embedded devices, mobile (resource-constrained)
- Fast Cold Start: Fastest startup time (interpreter advantage)
- No Dependencies: Pure C, cross-platform, easy to embed
Cons
- Slow Execution: 65% of native speed vs Wasmtime's 88%
- Limited Features: No component model, limited WASI support
- Not Async: Synchronous execution only
Use Case: Mobile builds where binary size is critical and performance is secondary (e.g., config extensions, simple validators).
Source: Choosing a WebAssembly Runtime, 2026 Benchmarks, Wasm3 Performance
3. WASI and Component Model
3.1 WASI (WebAssembly System Interface)
Current Version: WASI 0.3.0-rc-2026-01-06 (WASI 0.3 releasing Feb 2026)
What is WASI?
WASI is a standardized set of syscalls for WebAssembly outside the browser. It provides:
- Filesystem Access: Capability-based file I/O (no ambient authority)
- Networking: TCP/UDP sockets, HTTP client (WASI 0.3)
- Environment Variables: Read-only access to env vars
- Clock: Monotonic and wall-clock time
- Random: Cryptographically secure random numbers
WASI 0.3 Features (Feb 2026)
- Async Support: Language-integrated concurrency (futures/promises)
- Composable Concurrency: Components written in different languages can share async work
- High-Performance Streaming: Zero-copy I/O for large data
- Resource Types: Borrow/own semantics for handles (files, sockets, etc.)
Example: WASI Filesystem
// Extension requests file access
// Host grants capability to specific directory only
let wasi = WasiCtxBuilder::new()
.preopened_dir(Dir::open_ambient_dir("/extensions/data", ambient_authority())?, "/data")?
.build();
// Extension can ONLY access /extensions/data, nothing else
// No path traversal, no ambient file access
Source: WASI GitHub, WASI Status Feb 2026, WASI 1.0 Article
3.2 Component Model
Status: Stabilizing (WASI 0.3 integrates Component Model)
What is the Component Model?
The Component Model enables modular, language-agnostic composition of WebAssembly modules with rich types (strings, records, enums, resources) instead of just integers and linear memory.
Key Concepts
- WIT (WebAssembly Interface Types): IDL for defining component interfaces
- Interfaces: Collections of functions and types (like Rust traits, TypeScript interfaces)
- Worlds: Complete contract for a component (imports + exports)
- Resources: Handles to entities that can't be copied (files, database connections, etc.)
WIT Example
package morphee:extensions@1.0.0;
// Interface for HTTP requests (extension capability)
interface http {
record request {
method: string,
url: string,
headers: list<tuple<string, string>>,
body: option<list<u8>>,
}
record response {
status: u32,
headers: list<tuple<string, string>>,
body: list<u8>,
}
// Extension can make HTTP requests (requires permission)
fetch: func(req: request) -> result<response, string>;
}
// Interface for vault access (extension capability)
interface vault {
// Get secret (read-only, no list/write)
get-secret: func(key: string) -> option<string>;
}
// Interface for events (extension capability)
interface events {
enum event-type {
info,
warning,
error,
}
// Emit event to Morphee event bus
emit: func(event-type: event-type, message: string);
}
// Extension world (what extension implements)
world jira-extension {
// Extension imports (capabilities granted by host)
import http;
import vault;
import events;
// Extension exports (what host can call)
export create-issue: func(title: string, description: string) -> result<string, string>;
export get-issue: func(issue-id: string) -> result<issue, string>;
}
record issue {
id: string,
title: string,
description: string,
status: string,
assignee: option<string>,
}
Generating Bindings
# Python (host side)
python -m wasmtime.bindgen jira-extension.wasm --out-dir ./bindings
# Rust (guest side, extension author)
wit-bindgen rust ./wit/jira-extension.wit
Source: Component Model Intro, WIT Reference, WIT By Example
4. Extension Security
4.1 Core Security Features
WebAssembly provides defense-in-depth security:
- Sandboxed Execution: Extensions cannot access host memory, filesystem, or network without explicit imports
- Memory Safety: Bounds checking prevents buffer overflows, stack smashing attacks impossible (callstack inaccessible)
- Deny-by-Default: Extensions have zero capabilities unless granted by host
- Capability-Based Security: WASI uses capabilities (not ambient authority) for filesystem/network access
- Type Safety: Component Model enforces type contracts at module boundaries
Source: WebAssembly Security, Wasmtime Security, How WASM Offers Secure Sandboxing
4.2 Permission Model for Morphee Extensions
Three-Tier Permission System (aligned with existing AIAccess model):
from enum import Enum
class ExtensionPermission(str, Enum):
# Network permissions
HTTP_READ = "http:read" # GET/HEAD requests only
HTTP_WRITE = "http:write" # POST/PUT/DELETE requests
# Vault permissions
VAULT_READ = "vault:read" # Read secrets
VAULT_WRITE = "vault:write" # Write secrets (rare, dangerous)
# Event permissions
EVENT_EMIT = "event:emit" # Emit events to event bus
EVENT_LISTEN = "event:listen" # Subscribe to events
# Filesystem permissions (sandboxed)
FS_READ = "fs:read" # Read from extension-specific directory
FS_WRITE = "fs:write" # Write to extension-specific directory
# Data permissions
DATA_READ = "data:read" # Read group data (conversations, tasks)
DATA_WRITE = "data:write" # Write group data (create tasks, etc.)
class ExtensionConfig(BaseModel):
extension_id: str
version: str
permissions: list[ExtensionPermission]
resource_limits: ResourceLimits
approval_required: bool # If true, prompt user before execution
class ResourceLimits(BaseModel):
max_memory_mb: int = 128 # Max heap size
max_execution_ms: int = 30_000 # 30 second timeout
max_http_requests: int = 100 # Rate limit
max_file_size_mb: int = 10 # File I/O limit
Example: JIRA Extension Manifest
extension_id: "jira-integration"
version: "1.2.0"
author: "Atlassian"
permissions:
- http:read
- http:write
- vault:read # For JIRA API token
- event:emit
- data:write # Create tasks from JIRA issues
resource_limits:
max_memory_mb: 256
max_execution_ms: 60000 # 1 minute (syncing issues)
max_http_requests: 500
approval_required: false # User approved during install
Enforcement in Wasmtime
from wasmtime import Store, Config, ResourceLimiter
class ExtensionResourceLimiter(ResourceLimiter):
def __init__(self, limits: ResourceLimits):
self.limits = limits
self.http_requests_count = 0
def memory_growing(self, current, desired, maximum):
# Enforce memory limit
max_bytes = self.limits.max_memory_mb * 1024 * 1024
if desired > max_bytes:
raise RuntimeError(f"Extension exceeded memory limit: {self.limits.max_memory_mb}MB")
return desired
def table_growing(self, current, desired, maximum):
return desired
# Create store with limiter
config = Config()
config.consume_fuel = True # Enable fuel for CPU limits
engine = Engine(config)
store = Store(engine)
store.set_limits(ExtensionResourceLimiter(config.resource_limits))
store.add_fuel(config.resource_limits.max_execution_ms * 1000) # Fuel units ~ microseconds
# Extension exceeding limits will trap (recoverable error)
Source: WASM Security Concerns, WaVe Verifiable Sandboxing, Sandboxing Agentic AI with WASM
4.3 Security Best Practices
- AOT Compilation: Pre-compile extensions to eliminate JIT risks (wasmtime supports AOT)
- Code Signing: Require extensions to be signed with developer key (verify before loading)
- Content Integrity: Use SHA-256 checksums for extension binaries
- HTTPS Distribution: Serve extensions over HTTPS only
- User Approval: Prompt users to review permissions before first run
- Audit Logs: Log all extension actions (HTTP requests, vault access, data mutations)
- Automatic Updates: Auto-update extensions with security patches (versioned OCI registry)
Source: WebAssembly Security Best Practices
5. Real-World Examples
5.1 VS Code Extensions
Architecture: Extensions run in extension host worker (separate process), compile components to WASM, call VS Code API via bindings.
Key Learnings:
- Use SharedArrayBuffer + Atomics for synchronous API over async host
- Worker-based execution prevents blocking main thread
- WIT-generated bindings (
wit2ts) provide type-safe API - Language servers (LSP) implemented in WASM (Rust → WASM)
Example Use Case: Tree-sitter parser (Rust) compiled to WASM, called from TypeScript for syntax highlighting.
Source: Using WASM for VS Code Extensions, WASM Extension Part 2, Building VS Code Extension with Rust+WASM
5.2 Figma Plugins
Architecture: QuickJS (JavaScript engine in C) compiled to WASM, plugin code runs inside QuickJS sandbox, UI in iframe communicates via postMessage.
Security Model:
- WASM itself is sandboxed (no browser APIs)
- Plugin code can only call whitelisted Figma APIs
- User document access requires explicit plugin approval
Challenges:
- Debuggability: QuickJS errors are cryptic
- Performance: Double indirection (WASM → QuickJS → plugin code)
Key Learning: WASM-in-WASM sandboxing (WASM running JS engine running plugin) provides extreme isolation but impacts performance and DX.
Source: How Figma Built Plugin System, Figma + WASM, Figma Plugins Commentary
5.3 Shopify Functions
Architecture: WASM functions compiled from Rust/Go/TinyGo, executed by Wasmtime, input/output via NaN-boxed encoding.
Key Features:
- Language Support: Any language compiling to WASM (Rust preferred)
- Input/Output: 64-bit NaN-boxed values (compact, performant)
- API:
shopify_functioncrate provides typed bindings - Runtime: Wasmtime (server-side)
- Module Linking: Shared runtime module (e.g., JavaScript runtime) + small function module
Performance: NaN-boxing reduces memory allocations, module linking reduces binary size.
Example: Discount calculation function (Rust → WASM) processes cart data, returns discount rules, executes in <10ms.
Source: Shopify WASM Functions, How Shopify Uses WASM, JavaScript in WASM for Shopify
5.4 Cloudflare Workers
Architecture: V8 isolates (not processes), WASM modules executed alongside JavaScript, streaming compilation, shared runtime.
Performance Characteristics:
- Cold Start: Larger WASM modules = slower startup (optimize with
wasm-opt) - Memory Overhead: 48% memory operations (reduced to 26% with 8-bit quantization)
- Binary Size: WASM often larger than equivalent JS (includes runtime dependencies)
- Use Case: CPU-intensive tasks (crypto, image processing), not I/O-heavy code
Key Learnings:
- WASM benefits number-crunching, not object manipulation (memory copy overhead)
- Streaming compilation (
WebAssembly.compileStreaming) essential for large modules - Code caching (V8) improves subsequent loads
Limits:
- 1 CPU-ms per request (CPU time limit)
- 128MB memory per isolate
- 1MB WASM binary size (after compression)
Source: Cloudflare Workers WASM, WASM on Cloudflare Workers, WASM AI on Cloudflare
6. SDK Patterns for Extension Developers
6.1 Rust SDK (Guest Side)
Goal: Make it trivial for developers to write Morphee extensions in Rust.
// morphee-sdk-rust/src/lib.rs
// Auto-generated from WIT via wit-bindgen
wit_bindgen::generate!({
world: "morphee-extension",
exports: {
world: MorpheeExtension,
},
});
use crate::morphee::extensions::*;
// Developer implements this trait
pub trait Extension {
fn init() -> Result<(), String>;
fn execute(&self, action: String, params: Vec<(String, String)>) -> Result<String, String>;
}
// Example: JIRA extension
struct JiraExtension;
impl Extension for JiraExtension {
fn init() -> Result<(), String> {
// Load config, validate API token from vault
let api_token = vault::get_secret("jira_api_token")
.ok_or("JIRA API token not found")?;
Ok(())
}
fn execute(&self, action: String, params: Vec<(String, String)>) -> Result<String, String> {
match action.as_str() {
"create_issue" => {
let title = params.iter().find(|(k, _)| k == "title").map(|(_, v)| v).unwrap();
let description = params.iter().find(|(k, _)| k == "description").map(|(_, v)| v).unwrap();
// Make HTTP request to JIRA API
let req = http::Request {
method: "POST".to_string(),
url: "https://mycompany.atlassian.net/rest/api/3/issue".to_string(),
headers: vec![
("Authorization".to_string(), format!("Bearer {}", vault::get_secret("jira_api_token").unwrap())),
("Content-Type".to_string(), "application/json".to_string()),
],
body: Some(format!(r#"{{"fields": {{"summary": "{}", "description": "{}"}}}}"#, title, description).into_bytes()),
};
let resp = http::fetch(req).map_err(|e| format!("HTTP error: {}", e))?;
if resp.status == 201 {
events::emit(EventType::Info, "JIRA issue created".to_string());
Ok(String::from_utf8(resp.body).unwrap())
} else {
Err(format!("JIRA API error: {}", resp.status))
}
}
_ => Err(format!("Unknown action: {}", action))
}
}
}
export_extension!(JiraExtension);
Build Process:
# Install SDK
cargo add morphee-sdk
# Build extension
cargo build --target wasm32-wasi --release
# Optimize binary
wasm-opt -Oz -o jira-extension.wasm target/wasm32-wasi/release/jira_extension.wasm
# Package extension
morphee-cli package jira-extension.wasm \
--manifest extension.yaml \
--sign-key ~/.morphee/developer.key \
--out jira-extension.morphee
6.2 TypeScript SDK (Guest Side)
Goal: Enable JavaScript/TypeScript developers to write extensions.
Toolchain: AssemblyScript (TypeScript → WASM) or Javy (JavaScript runtime in WASM)
// extension.ts (AssemblyScript)
import { http, vault, events } from "@morphee/sdk";
export function execute(action: string, params: Map<string, string>): string {
if (action === "create_issue") {
const title = params.get("title")!;
const description = params.get("description")!;
const apiToken = vault.getSecret("jira_api_token");
const response = http.fetch({
method: "POST",
url: "https://mycompany.atlassian.net/rest/api/3/issue",
headers: [
["Authorization", `Bearer ${apiToken}`],
["Content-Type", "application/json"]
],
body: JSON.stringify({
fields: { summary: title, description }
})
});
if (response.status === 201) {
events.emit("info", "JIRA issue created");
return response.body;
} else {
throw new Error(`JIRA API error: ${response.status}`);
}
}
throw new Error(`Unknown action: ${action}`);
}
Build:
# AssemblyScript
npm install -g assemblyscript
asc extension.ts -o extension.wasm --optimize --runtime stub
# Javy (full JavaScript)
javy compile extension.js -o extension.wasm
Source: Javy JavaScript Toolchain, AssemblyScript for Shopify
6.3 Python SDK (Host Side)
Goal: Easy integration into Morphee backend.
# backend/extensions/manager.py
from wasmtime import Store, Module, Instance, Linker, Engine, Config
from wasmtime_wasi import WasiCtx, WasiCtxBuilder
import asyncio
class ExtensionManager:
def __init__(self):
config = Config()
config.consume_fuel = True
self.engine = Engine(config)
self.extensions: dict[str, CompiledExtension] = {}
async def load_extension(self, extension_id: str, wasm_path: str, config: ExtensionConfig):
"""Load and compile extension (expensive, cache this)"""
module = Module.from_file(self.engine, wasm_path)
# Verify signature
if not self._verify_signature(wasm_path, config.signature):
raise SecurityError("Invalid extension signature")
self.extensions[extension_id] = CompiledExtension(
module=module,
config=config,
)
async def execute(
self,
extension_id: str,
action: str,
params: dict[str, str],
context: ExecutionContext,
) -> dict:
"""Execute extension action with sandboxing"""
ext = self.extensions.get(extension_id)
if not ext:
raise ValueError(f"Extension not loaded: {extension_id}")
# Create fresh store for each execution (isolation)
store = Store(self.engine)
store.set_limits(ExtensionResourceLimiter(ext.config.resource_limits))
store.add_fuel(ext.config.resource_limits.max_execution_ms * 1000)
# Create WASI context with sandboxed filesystem
wasi = WasiCtxBuilder() \
.inherit_stdio() \
.preopened_dir(f"/data/extensions/{extension_id}", "/data") \
.build()
# Create linker with host functions
linker = Linker(self.engine)
linker.define_wasi()
# Add custom imports (http, vault, events)
self._add_http_import(linker, ext.config.permissions, context)
self._add_vault_import(linker, ext.config.permissions, context)
self._add_events_import(linker, ext.config.permissions, context)
# Instantiate and call
instance = linker.instantiate(store, ext.module)
execute_func = instance.exports(store)["execute"]
try:
result = await asyncio.to_thread(
execute_func,
store,
action,
list(params.items())
)
return {"success": True, "result": result}
except Exception as e:
logger.error(f"Extension {extension_id} failed: {e}")
return {"success": False, "error": str(e)}
def _add_http_import(self, linker: Linker, permissions: list[ExtensionPermission], context: ExecutionContext):
"""Add HTTP capability with permission checks"""
if ExtensionPermission.HTTP_READ not in permissions and ExtensionPermission.HTTP_WRITE not in permissions:
return
async def http_fetch(method: str, url: str, headers: list[tuple[str, str]], body: bytes | None) -> tuple[int, list[tuple[str, str]], bytes]:
# Check permission
if method.upper() in ["POST", "PUT", "DELETE", "PATCH"]:
if ExtensionPermission.HTTP_WRITE not in permissions:
raise PermissionError("Extension lacks http:write permission")
# Rate limiting
# ... (check max_http_requests)
# Make request
async with httpx.AsyncClient() as client:
resp = await client.request(method, url, headers=dict(headers), content=body)
return resp.status_code, list(resp.headers.items()), resp.content
linker.define_async_func("http", "fetch", http_fetch)
7. Performance
7.1 Execution Overhead
Wasmtime Performance (2026 benchmarks):
- Cold Start: 5-15ms (module compilation)
- Warm Start: <1ms (cached compiled module)
- Steady-State Execution: 88% of native speed (JIT)
- Interpreter (wasm3): 65% of native speed
Memory Overhead:
- Compiled module: 5-7x larger than
.wasmfile (JIT code) - Runtime memory: ~10-50MB per instance (depends on module size)
Source: 2026 Benchmarks, Choosing a Runtime
7.2 Optimization Strategies
7.2.1 Module Caching
Problem: Compilation is expensive (5-15ms per module).
Solution: Cache compiled modules, reuse across instances.
# Compile once, instantiate many times
module = Module.from_file(engine, "extension.wasm")
# Serialize compiled module
compiled_bytes = module.serialize()
with open("extension.compiled", "wb") as f:
f.write(compiled_bytes)
# Later: deserialize (instant, no compilation)
with open("extension.compiled", "rb") as f:
module = Module.deserialize(engine, f.read())
Browser Caching: V8/SpiderMonkey automatically cache compiled WASM (150MB cache, 5-7x size of source).
Source: WASM Code Caching, Caching Compiled Modules
7.2.2 Streaming Compilation
Web Only: Use WebAssembly.compileStreaming() to compile while downloading.
// Bad: Download then compile (sequential)
const response = await fetch('extension.wasm');
const buffer = await response.arrayBuffer();
const module = await WebAssembly.compile(buffer);
// Good: Download and compile in parallel
const module = await WebAssembly.compileStreaming(fetch('extension.wasm'));
Source: WASM Performance Patterns
7.2.3 Binary Optimization
Tool: wasm-opt (Binaryen)
# Optimize for size
wasm-opt -Oz input.wasm -o output.wasm # ~30-50% smaller
# Optimize for speed
wasm-opt -O3 input.wasm -o output.wasm
# Strip debug info
wasm-strip extension.wasm
Typical Results:
- Rust Debug Build: 2.5MB
- Rust Release Build: 500KB
- After
wasm-opt -Oz: 200KB - After gzip: 80KB
Source: Optimizing WASM Startup, Cloudflare WASM Limits
7.2.4 AOT Compilation
Ahead-of-Time Compilation: Pre-compile WASM to native code, load instantly.
# Wasmtime CLI: compile to native
wasmtime compile extension.wasm -o extension.cwasm
# Python: load precompiled module
module = Module.deserialize_file(engine, "extension.cwasm")
Benefits:
- Instant loading (no JIT compilation)
- Smaller attack surface (no JIT compiler in production)
- Deterministic performance
Drawbacks:
- Platform-specific (x86-64, ARM64, macOS, Linux, Windows)
- Larger binaries (architecture-specific code)
7.3 Performance Best Practices
- Cache Compiled Modules: Compile once, instantiate many times
- Use AOT in Production: Pre-compile extensions for target platforms
- Optimize Binaries: Always run
wasm-opt -Ozbefore distribution - Minimize Memory Copies: Use linear memory for large data, avoid frequent host ↔ WASM calls
- Batch Operations: Group multiple actions in one call (reduce context switching)
- Async for I/O: Use async WASI for HTTP/filesystem (don't block execution)
- Resource Limits: Set conservative limits, measure actual usage, adjust
Source: WASM Performance Patterns, State of WASM 2025-2026
8. Marketplace and Distribution
8.1 OCI Registry-Based Distribution
Model: Distribute WASM extensions like container images (Docker/OCI registries).
Advantages:
- Versioning: Semantic versioning (1.2.3), tags (latest, stable)
- Signing: Cosign/Notary for code signing
- Immutability: Content-addressable storage (SHA-256)
- CDN: Fast global distribution
- Tooling: Existing tools (docker, crane, oras)
Example:
# Push extension to registry
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 extension
oras pull ghcr.io/morphee/extensions/jira:1.2.0
# Verify signature
cosign verify --key developer.pub ghcr.io/morphee/extensions/jira:1.2.0
Source: Distributing WASM Modules (Istio), DuckDB Extension CI/CD
8.2 Extension Manifest
Format: YAML (embedded in OCI image)
# manifest.yaml
extension_id: "jira-integration"
version: "1.2.0"
schema_version: "1.0"
metadata:
name: "JIRA Integration"
description: "Sync tasks with JIRA issues, create issues from chat"
author: "Atlassian"
homepage: "https://morphee.app/extensions/jira"
license: "Apache-2.0"
icon: "https://cdn.morphee.app/extensions/jira/icon.png"
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
max_file_size_mb: 10
actions:
- name: "create_issue"
description: "Create a JIRA issue"
parameters:
- name: "title"
type: "string"
required: true
- name: "description"
type: "string"
required: true
- name: "project"
type: "string"
required: true
ai_access: "execute" # LLM can call without approval
- name: "get_issue"
description: "Fetch JIRA issue details"
parameters:
- name: "issue_id"
type: "string"
required: true
ai_access: "execute"
config_schema:
type: "object"
properties:
jira_url:
type: "string"
format: "uri"
description: "JIRA instance URL (e.g., https://mycompany.atlassian.net)"
api_token_key:
type: "string"
description: "Vault key for JIRA API token"
default: "jira_api_token"
required: ["jira_url", "api_token_key"]
8.3 Versioning Strategy
Semantic Versioning: MAJOR.MINOR.PATCH
- MAJOR: Breaking changes (manifest schema, removed actions)
- MINOR: New features (new actions, parameters)
- PATCH: Bug fixes, performance improvements
Compatibility:
- Host supports extension API version range (e.g., 1.0-2.0)
- Extension declares minimum host version (e.g.,
min_host_version: "1.5.0") - Automatic updates for PATCH, user approval for MINOR/MAJOR
8.4 Security: Code Signing
Process:
- Developer generates keypair (
openssl genrsa,openssl rsa) - Developer signs extension binary (SHA-256 hash + RSA signature)
- Developer publishes public key (keybase, GitHub, morphee.app/developers)
- Morphee verifies signature before loading extension
Implementation:
import hashlib
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding, rsa
def sign_extension(wasm_path: str, private_key_path: str) -> bytes:
# Hash WASM binary
with open(wasm_path, "rb") as f:
wasm_bytes = f.read()
digest = hashlib.sha256(wasm_bytes).digest()
# Load private key
with open(private_key_path, "rb") as f:
private_key = serialization.load_pem_private_key(f.read(), password=None)
# Sign hash
signature = private_key.sign(
digest,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256()
)
return signature
def verify_extension(wasm_path: str, signature: bytes, public_key_path: str) -> bool:
# Hash WASM binary
with open(wasm_path, "rb") as f:
wasm_bytes = f.read()
digest = hashlib.sha256(wasm_bytes).digest()
# Load public key
with open(public_key_path, "rb") as f:
public_key = serialization.load_pem_public_key(f.read())
# Verify signature
try:
public_key.verify(
signature,
digest,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256()
)
return True
except Exception:
return False
Source: WASM Module Signing, WASM Security
9. Recommendations for Morphee
9.1 Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Morphee Extension System │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ Python Backend │ │ Tauri Rust │ │
│ │ (FastAPI) │ │ (Frontend) │ │
│ ├──────────────────────┤ ├──────────────────────┤ │
│ │ ExtensionManager │ │ ExtensionRunner │ │
│ │ - Load/compile │ │ - Load/compile │ │
│ │ - Permission checks │ │ - Permission checks │ │
│ │ - Resource limits │ │ - Resource limits │ │
│ │ │ │ │ │
│ │ wasmtime-py (v41+) │ │ wasmtime (v41+) │ │
│ │ - WASI 0.3 │ │ - WASI 0.3 │ │
│ │ - Component Model │ │ - Component Model │ │
│ │ - Async support │ │ - Tokio integration │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │ │ │
│ └──────────────┬───────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────┐ │
│ │ Extension Runtime │ │
│ │ (WASM Sandbox) │ │
│ ├────────────────────────┤ │
│ │ Imports (Host APIs): │ │
│ │ - http:fetch │ │
│ │ - vault:get_secret │ │
│ │ - events:emit │ │
│ │ - data:query │ │
│ │ │ │
│ │ Exports (Extension): │ │
│ │ - execute(action) │ │
│ │ - init() │ │
│ └────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────┐ │
│ │ Extension Registry │ │
│ │ (OCI Registry) │ │
│ ├────────────────────────┤ │
│ │ - ghcr.io/morphee/ │ │
│ │ extensions/* │ │
│ │ - Version tags │ │
│ │ - Cosign signatures │ │
│ │ - Manifest metadata │ │
│ └────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
9.2 Implementation Roadmap
Phase 1: Foundation (2 weeks)
- Research complete ✅ (this document)
- Add
wasmtime-pyandwasmtimedependencies - Create
ExtensionManager(Python) andExtensionRunner(Rust) - Design WIT interface (
morphee:extensionspackage) - Implement basic host functions (http, vault, events)
- Create Rust SDK crate (
morphee-sdk-rust)
Phase 2: Security & Permissions (2 weeks)
- Implement permission model (
ExtensionPermissionenum) - Add resource limiters (memory, CPU, rate limiting)
- Code signing (RSA-PSS, SHA-256)
- Signature verification before loading
- Audit logging (all extension actions)
Phase 3: Distribution (1 week)
- OCI registry setup (GitHub Container Registry)
- Extension manifest schema (YAML)
- CLI tool (
morphee-cli package,morphee-cli publish) - Signature tooling (
morphee-cli sign,morphee-cli verify)
Phase 4: Developer Experience (2 weeks)
- Rust SDK documentation + examples
- TypeScript SDK (AssemblyScript + Javy support)
- Extension templates (GitHub template repos)
- Local testing tools (
morphee-cli dev) - Developer portal (morphee.app/developers)
Phase 5: Built-in Extensions (3 weeks)
- JIRA integration (reference implementation)
- GitHub integration
- Notion integration
- Linear integration
- Slack (migrate existing Python integration to WASM)
Phase 6: Marketplace (2 weeks)
- Extension catalog UI (browse, search, install)
- User permission approval flow
- Automatic updates (PATCH versions)
- Extension settings UI (per-user config)
- Usage analytics (extension calls, performance)
Total Estimate: 12 weeks (3 months)
9.3 Code Structure
morphee-beta/
├── backend/
│ ├── extensions/
│ │ ├── __init__.py
│ │ ├── manager.py # ExtensionManager class
│ │ ├── models.py # ExtensionConfig, ResourceLimits, etc.
│ │ ├── permissions.py # Permission checks
│ │ ├── resource_limiter.py # Wasmtime ResourceLimiter impl
│ │ ├── signing.py # Code signing/verification
│ │ └── registry.py # OCI registry client
│ ├── interfaces/integrations/
│ │ └── extensions.py # ExtensionsIntegration (meta-integration)
│ └── api/
│ └── extensions.py # /api/extensions/* routes
│
├── frontend/src-tauri/src/
│ ├── extensions/
│ │ ├── mod.rs
│ │ ├── runner.rs # ExtensionRunner
│ │ ├── permissions.rs # Permission checks
│ │ └── limiter.rs # Resource limiter
│ └── commands/
│ └── extensions.rs # Tauri commands
│
├── frontend/src/
│ ├── pages/settings/
│ │ └── ExtensionsTab.tsx # Extension marketplace UI
│ └── components/extensions/
│ ├── ExtensionCard.tsx
│ ├── InstallDialog.tsx
│ └── PermissionDialog.tsx
│
├── extensions/ # First-party extensions (examples)
│ ├── jira/
│ │ ├── Cargo.toml
│ │ ├── src/lib.rs
│ │ └── manifest.yaml
│ ├── github/
│ ├── notion/
│ └── linear/
│
├── sdk/
│ ├── morphee-sdk-rust/ # Rust SDK for extension authors
│ │ ├── Cargo.toml
│ │ ├── src/lib.rs
│ │ └── examples/
│ └── morphee-sdk-ts/ # TypeScript SDK (future)
│
└── wit/ # WIT interface definitions
├── morphee-extension.wit
└── deps/
└── wasi/ # WASI 0.3 WIT files
9.4 WIT Interface Definition
// wit/morphee-extension.wit
package morphee:extensions@1.0.0;
// HTTP capability
interface http {
record request {
method: string,
url: string,
headers: list<tuple<string, string>>,
body: option<list<u8>>,
}
record response {
status: u32,
headers: list<tuple<string, string>>,
body: list<u8>,
}
fetch: func(req: request) -> result<response, string>;
}
// Vault capability (secrets management)
interface vault {
get-secret: func(key: string) -> option<string>;
set-secret: func(key: string, value: string) -> result<_, string>;
}
// Event bus capability
interface events {
enum event-type {
info,
warning,
error,
}
emit: func(event-type: event-type, message: string);
}
// Data access capability
interface data {
record task {
id: string,
title: string,
description: option<string>,
status: string,
}
create-task: func(title: string, description: option<string>, space-id: string) -> result<string, string>;
get-task: func(task-id: string) -> result<task, string>;
}
// Extension contract
world morphee-extension {
import http;
import vault;
import events;
import data;
export init: func() -> result<_, string>;
export execute: func(action: string, params: list<tuple<string, string>>) -> result<string, string>;
}
10. Conclusion
WebAssembly is the ideal technology for Morphee's extension system:
✅ Security: Sandboxed execution, capability-based permissions, deny-by-default ✅ Performance: 88% of native speed, <1ms warm starts with caching ✅ Portability: Single binary runs on Python backend AND Rust frontend ✅ Developer Experience: SDKs in Rust, TypeScript, others ✅ Industry Proven: VS Code, Figma, Shopify, Cloudflare all use WASM for extensions ✅ Future-Proof: Component Model + WASI 0.3 enable rich, language-agnostic APIs
Recommended Stack:
- Python:
wasmtime-py(superior security, performance, WASI support) - Rust:
wasmtime(industry standard, comprehensive API) - Interface: Component Model + WASI 0.3 (async, rich types)
- Distribution: OCI registry (versioning, signing, immutability)
Next Steps:
- Approve this research (create issue for Phase 3l)
- Start Phase 1 (foundation) with WIT interface design
- Build JIRA extension as reference implementation
- Iterate on developer experience based on feedback
Sources
Python Runtimes
- wasmtime-py GitHub
- Wasmtime Python Docs
- PyPI wasmtime
- Wasmer vs Wasmtime
- Comparing Runtimes
- Sandboxing Python with WASM
Rust Runtimes
WASI & Component Model
Security
- WebAssembly Security
- Wasmtime Security
- How WASM Offers Secure Sandboxing
- WASM Security Concerns
- WaVe Verifiable Sandboxing
Real-World Examples
- Using WASM for VS Code Extensions
- How Figma Built Plugin System
- Shopify WASM Functions
- How Shopify Uses WASM
- Cloudflare Workers WASM