Skip to main content

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 wasmtime to wasmer 5.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 wasmer 5.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

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/deserialize methods 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

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 tokio and cap-std for native platform equivalents
  • Component Model: First-class support via wasmtime-wasi crate
  • 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:

  1. Sandboxed Execution: Extensions cannot access host memory, filesystem, or network without explicit imports
  2. Memory Safety: Bounds checking prevents buffer overflows, stack smashing attacks impossible (callstack inaccessible)
  3. Deny-by-Default: Extensions have zero capabilities unless granted by host
  4. Capability-Based Security: WASI uses capabilities (not ambient authority) for filesystem/network access
  5. 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

  1. AOT Compilation: Pre-compile extensions to eliminate JIT risks (wasmtime supports AOT)
  2. Code Signing: Require extensions to be signed with developer key (verify before loading)
  3. Content Integrity: Use SHA-256 checksums for extension binaries
  4. HTTPS Distribution: Serve extensions over HTTPS only
  5. User Approval: Prompt users to review permissions before first run
  6. Audit Logs: Log all extension actions (HTTP requests, vault access, data mutations)
  7. 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_function crate 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 .wasm file (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

  1. Cache Compiled Modules: Compile once, instantiate many times
  2. Use AOT in Production: Pre-compile extensions for target platforms
  3. Optimize Binaries: Always run wasm-opt -Oz before distribution
  4. Minimize Memory Copies: Use linear memory for large data, avoid frequent host ↔ WASM calls
  5. Batch Operations: Group multiple actions in one call (reduce context switching)
  6. Async for I/O: Use async WASI for HTTP/filesystem (don't block execution)
  7. 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:

  1. Developer generates keypair (openssl genrsa, openssl rsa)
  2. Developer signs extension binary (SHA-256 hash + RSA signature)
  3. Developer publishes public key (keybase, GitHub, morphee.app/developers)
  4. 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-py and wasmtime dependencies
  • Create ExtensionManager (Python) and ExtensionRunner (Rust)
  • Design WIT interface (morphee:extensions package)
  • Implement basic host functions (http, vault, events)
  • Create Rust SDK crate (morphee-sdk-rust)

Phase 2: Security & Permissions (2 weeks)

  • Implement permission model (ExtensionPermission enum)
  • 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:

  1. Approve this research (create issue for Phase 3l)
  2. Start Phase 1 (foundation) with WIT interface design
  3. Build JIRA extension as reference implementation
  4. Iterate on developer experience based on feedback

Sources

Python Runtimes

Rust Runtimes

WASI & Component Model

Security

Real-World Examples

Performance

Distribution