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​


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​