Skip to main content

Feature: WASM Extension System for Professional Integrations

Date: 2026-02-16 Status: Approved Version: V1.2 — Extension Ecosystem Effort: XL (12 weeks, 6 phases)


1. Context & Motivation

Problem Statement

Morphee needs professional integrations (JIRA, GitHub, Notion, Linear, Slack, etc.) but hardcoding them has significant drawbacks:

Current Approach (Hardcoded Integrations):

  • backend/interfaces/integrations/jira.py — Python implementation
  • Tightly coupled to backend deployment
  • No community contributions (third parties can't build integrations)
  • Duplication problem: To run integrations on Tauri Rust frontend, must rewrite in Rust
  • Not portable with Spaces (can't share integrations with .morph/ directories)
  • Scalability bottleneck (every integration requires backend deployment)

User's Key Insight: "I don't want to do the work twice, and make JIRA integration only on backend. It could be a WASM executable that user can have on their device too."

Vision: Portable, Sandboxed, Community-Driven Extensions

Transform integrations from hardcoded Python modules to portable WASM extensions:

┌─────────────────────────────────────────────────────────┐
│ Extension Developer │
│ Writes integration in Rust/Go/C/AssemblyScript │
│ Uses morphee-sdk-rust crate │
└─────────────────────────────────────────────────────────┘
↓ compiles to
┌─────────────────────────────────────────────────────────┐
│ jira.wasm (WebAssembly binary) │
│ - Single binary, runs on Python + Rust │
│ - Sandboxed (cannot access filesystem/network/vault) │
│ - Capability-based permissions │
└─────────────────────────────────────────────────────────┘
↓ distributed via
┌─────────────────────────────────────────────────────────┐
│ rg.morphee.ai (OCI registry) │
│ - Public extensions (GitHub Container Registry, free) │
│ - Private extensions (self-hosted Harbor, groups only) │
│ - Versioned, signed, verified │
└─────────────────────────────────────────────────────────┘
↓ runs on
┌──────────────────────────┬────────────────────────────────────────────────┐
│ Python Backend │ Tauri Rust Frontend │
│ wasmtime-py v41+ │ WasmRuntime (wasmer 5.0) │
│ ExtensionManager │ ├── Desktop/Android: Cranelift JIT │
│ Host functions (http, │ └── iOS: Wasmi interpreter (JIT banned) │
│ vault, events, data) │ JSRuntime (webview JS — canvas/UI extensions) │
└──────────────────────────┴────────────────────────────────────────────────┘
↓ stored in
┌─────────────────────────────────────────────────────────┐
│ .morph/extensions/jira.wasm (OpenMorph integration) │
│ - Extensions live in Space's Git repo │
│ - Portable with Space (copy .morph/ = copy extensions) │
│ - File extension association system (Phase 3i) │
└─────────────────────────────────────────────────────────┘

Key Benefits:

  1. Portability: Same binary runs on Python backend AND Rust frontend (no duplication!)
  2. Security: True sandboxing (memory-safe, cannot escape without explicit host imports)
  3. Community: Third parties can build and share extensions
  4. OpenMorph Synergy: Extensions live in .morph/extensions/*.wasm alongside Space data
  5. Marketplace: OCI registry distribution, versioning, signing
  6. Performance: 88% of native speed, <1ms warm start after caching

2. Options Investigated

Option A: WebAssembly Extension System (CHOSEN)

Technology Stack (updated 2026-02-20):

  • Python Backend: wasmtime-py v41+ (best security, 88% native performance, full WASI 0.3 support)
  • Tauri Rust (all platforms): wasmer 5.0+ — unified API with platform-adaptive backends
    • Desktop/Android: Cranelift JIT (80-88% native)
    • iOS: Wasmi interpreter (50-60% native) — iOS §2.5.2 prohibits JIT; wasmtime JIT rejected by Apple
    • Future: wasm3 as ultra-minimal option (64KB, 65% native, sync-only)
  • BaseMorphRuntime: WasmRuntime (backend), JSRuntime (frontend canvas/UI), PythonRuntime (future)
  • Interface Standard: WebAssembly Component Model + WASI 0.3 (async support, rich types)
  • Distribution: OCI registry at rg.morphee.ai (GHCR for public, Harbor for private)
  • Security: Install-time granular permissions (like Google Play Store) + resource limits + code signing

Pros:

  • Portability: Same .wasm binary runs on Python backend AND Rust frontend
  • Security: True sandboxing, capability-based permissions
  • Performance: 88% native (wasmtime JIT), 65% native (wasm3 interpreter on mobile)
  • Industry-Proven: VS Code, Figma, Shopify, Cloudflare all use WASM for extensions
  • Language-Agnostic: Developers can use Rust, Go, C, AssemblyScript, TinyGo
  • OpenMorph Synergy: Extensions stored in .morph/extensions/*.wasm
  • Marketplace-Ready: OCI registry, versioning, signing, verified publishers

Cons:

  • ⚠️ Initial implementation effort (12 weeks)
  • ⚠️ Learning curve for extension developers (mitigated with SDK + templates)
  • ⚠️ Binary size: wasmtime-py ~10MB (desktop/server), wasm3 ~64KB (mobile)

Effort: XL (12 weeks, 6 phases)


Option B: JavaScript/Python Plugin System

Technology Stack:

  • Python backend: importlib + dynamic import
  • Tauri frontend: JavaScript via eval() or embedded Deno runtime

Pros:

  • ✅ Simpler for developers (more people know JS/Python)
  • ✅ No compilation step

Cons:

  • No true sandboxing — JS/Python can escape, access arbitrary filesystem/network
  • Not portable — need separate Python and JS implementations
  • Security nightmareeval() is dangerous, dynamic imports hard to audit
  • Not future-proof — can't run on mobile without embedding V8/Deno (~20MB+ binary)

Verdict: ❌ Rejected — security risks unacceptable


Option C: Hardcode Integrations (Status Quo)

Build each integration as Python module (backend/interfaces/integrations/jira.py).

Pros:

  • ✅ Fastest to ship first integration (1 week)

Cons:

  • Not scalable — every integration requires backend deployment
  • No community — third parties can't contribute
  • Doubles work — must rewrite in Rust for frontend execution
  • Not portable — doesn't align with OpenMorph vision

Verdict: ❌ Rejected — user explicitly said "I don't want to do the work twice"


3. Decision

Chosen approach: Option A — WebAssembly Extension System

Reasoning:

  1. Solves portability problem: One binary runs on Python backend AND Rust frontend (no duplication)
  2. Aligns with OpenMorph: Extensions live in .morph/extensions/*.wasm as versioned files
  3. Security-first: True sandboxing with install-time granular permissions (like Google Play Store)
  4. Future-proof: Industry standard (VS Code, Figma, Shopify, Cloudflare all use WASM)
  5. Marketplace-ready: OCI registry at rg.morphee.ai, versioning, signing, verified publishers
  6. Mobile-compatible: wasm3 interpreter (64KB) + optional wasmtime for power users

Trade-offs Accepted:

  • 12 weeks upfront investment vs 1 week per hardcoded integration
  • Extension developers need to learn Rust/Go/AssemblyScript (mitigated with excellent SDK + templates)
  • Binary size: ~10MB wasmtime on desktop (acceptable), ~64KB wasm3 on mobile (acceptable)

Technology Decisions (updated 2026-02-20):

  • Python Runtime: wasmtime-py v41+ (88% native performance, full WASI 0.3 async support)
  • Tauri Rust Runtime (all platforms): wasmer 5.0+ — unified API with pluggable backends
    • Desktop (macOS/Windows/Linux): Cranelift JIT (80-88% native speed)
    • Android: Cranelift JIT (80-88% native speed)
    • iOS: Wasmi interpreter (50-60% native speed) — iOS prohibits JIT compilation (App Store Review Guidelines §2.5.2); wasmtime's JIT mode is completely blocked on iOS, making wasmer with Wasmi the correct choice
    • Future option: wasm3 as ultra-minimal interpreter (64KB, 65% native, no async) — tracked for future ultra-low-footprint mobile scenarios
  • Interface Standard: WebAssembly Component Model + WASI 0.3 (async support, rich types)
  • Registry Hosting: Dual registry (GHCR for public extensions, Harbor for private group extensions)
    • Public: rg.morphee.ai/public/jira:1.0.0ghcr.io/morphee-ai/jira:1.0.0
    • Private: rg.morphee.ai/private/techcorp-crm:1.0.0 → self-hosted Harbor
  • Permission Model: Install-time granular permissions (like Google Play Store)
    • User reviews/approves permissions before extension is installed
    • No runtime approval prompts (too disruptive)
    • Permissions: http:read, http:write, vault:read, vault:write, event:emit, data:read, data:write, task:create, space:read, notify:send

Runtime Architecture — BaseMorphRuntime

The WASM Extension System introduces a BaseMorphRuntime abstract class — the common contract for all execution runtimes that back dynamically-installed Integrations. This lets InterfaceManager treat WASM, JavaScript, and future Python extensions identically, just like it treats built-in Python integrations.

Python (backend/extensions/runtime.py)

class BaseMorphRuntime(ABC):
"""
Abstract runtime that loads and executes portable code as a BaseInterface.
Implementations: WasmRuntime, JSRuntime, PythonRuntime (future).
"""

@abstractmethod
async def load(self, source: str | bytes) -> "LoadedModule":
"""Load a module from a file path or bytes. Returns opaque handle."""

@abstractmethod
async def describe(self, module: "LoadedModule") -> InterfaceDefinition:
"""Returns the Integration definition declared by the module.
Mirrors BaseInterface.get_actions() + published_events + config_schema."""

@abstractmethod
async def execute(
self, module: "LoadedModule", action: str,
params: dict, context: ExecutionContext
) -> ActionResult:
"""Execute an action. Mirrors BaseInterface.execute()."""

@abstractmethod
async def teardown(self, module: "LoadedModule") -> None:
"""Clean up resources (memory, connections)."""


class WasmRuntime(BaseMorphRuntime):
"""Runs .wasm files via wasmtime-py. Used on Python backend."""

class JSRuntime(BaseMorphRuntime):
"""Runs JS canvas/UI integrations via webview bridge. Used on Tauri frontend."""

class PythonRuntime(BaseMorphRuntime): # future
"""Runs sandboxed .py scripts dynamically. Used on Python backend."""

Rust (frontend/src-tauri/src/extensions/runtime.rs)

/// Abstract runtime for executing portable code as a MorpheeIntegration.
pub trait MorphRuntime: Send + Sync {
fn load(&self, source: &[u8]) -> Result<Box<dyn LoadedModule>, MorpheeError>;
fn describe(&self, module: &dyn LoadedModule) -> Result<InterfaceDefinition, MorpheeError>;
fn execute(&self, module: &dyn LoadedModule, action: &str, params: &str, ctx: &str)
-> Result<String, MorpheeError>;
}

/// WASM backend: wasmer 5.0 (Cranelift on desktop/Android, Wasmi on iOS)
pub struct WasmRuntime { store: Store }
impl MorphRuntime for WasmRuntime { ... }

/// Frontend canvas/UI extensions via Tauri webview JS bridge
pub struct JSRuntime { app_handle: AppHandle }
impl MorphRuntime for JSRuntime { ... }

This abstraction means:

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

└── BaseMorphRuntime-backed Integrations (dynamic, installed)
├── WasmIntegration("jira.wasm") ← WasmRuntime(BaseMorphRuntime) → wasmtime-py
├── WasmIntegration("github.wasm") ← WasmRuntime(BaseMorphRuntime) → wasmtime-py
├── JsIntegration("canvas-ui.js") ← JSRuntime(BaseMorphRuntime) → webview JS
└── PythonIntegration("custom.py") ← PythonRuntime(BaseMorphRuntime) → sandbox [future]

WasmRuntime: Executes .wasm backend integrations (JIRA, GitHub, Notion, Slack...) using wasmtime-py on the Python backend, and wasmer 5.0 (Cranelift/Wasmi) in Tauri Rust.

JSRuntime: Executes JavaScript frontend integrations — canvas components, dynamic UI renderers, interactive widgets. These run in the browser/Tauri webview context. Every canvas Integration and dynamic UI component routes through JSRuntime, keeping UI logic sandboxed and portable alongside their WASM data counterparts.

PythonRuntime (future): Executes dynamic Python scripts loaded at runtime without backend redeployment.

WASM extension contract — each .wasm extension exports the same two methods that mirror Python's BaseInterface:

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

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

This ensures installed WASM extensions are first-class citizens in the InterfaceManager, identical in behavior to built-in Python integrations.

OpenMorph Integration (Phase 3i):

  • Extensions are stored as .morph/extensions/*.wasm files in GitStore
  • File Extension Association System: GitStore maps file extensions to Integrations
    • .wasmWASMIntegration.can_open() returns true
    • .jpg, .pngImageIntegration.can_open() returns true
    • .pyPythonIntegration.can_open() returns true
    • .txt, .mdTextIntegration.can_open() returns true
  • Multi-Integration Support: One file type can be opened by multiple Integrations (e.g., .py can be opened by TextIntegration OR PythonIntegration)
  • Multi-File Support: One Integration can open multiple file types (e.g., ImageIntegration opens .jpg, .png, .gif, .webp)
  • Discovery: GitStore scans .morph/extensions/ on Space load, registers available extensions with InterfaceManager

4. Implementation Plan

Phase 1: Foundation (2 weeks, L effort)

Goal: Core runtime infrastructure, WIT interface definition, basic host functions, Rust SDK.

Tasks:

  1. Add dependencies

    • Python: wasmtime==41.0.0 to requirements.txt
    • Rust desktop: wasmtime = "41", wasmtime-wasi = "41" to Cargo.toml
    • Rust mobile: wasm3 = "0.5" to Cargo.toml (conditional compilation via mobile feature)
  2. Create backend/extensions/ module

    • manager.pyExtensionManager class (load, compile, cache, execute)
    • models.py — Pydantic models (ExtensionConfig, ExtensionPermission, ResourceLimits, ExtensionManifest)
    • permissions.py — Permission checking logic (10 granular permissions)
    • resource_limiter.pyResourceLimiter implementation for Wasmtime
    • signing.py — Code signing/verification (RSA-PSS + SHA-256)
    • registry.py — OCI registry client (pull from rg.morphee.ai)
  3. Create frontend/src-tauri/src/extensions/ module

    • wasm_runtime.rsWasmRuntime (wasmer 5.0, platform-adaptive: Cranelift/Wasmi)
    • js_runtime.rsJSRuntime (webview JS bridge for canvas/UI extensions)
    • permissions.rs — Permission checking
    • limiter.rs — Resource limiter
  4. Design WIT interface (wit/morphee-extension.wit)

    • Define host functions: http, vault, events, data interfaces
    • Define morphee-extension world (imports = host functions, exports = extension entry points)
    • Generate bindings: python -m wasmtime.bindgen (Python), wit-bindgen rust (Rust)
  5. Implement host functions

    • http::fetch(request) -> response — HTTP requests with permission checks
    • vault::get_secret(key) -> value — Read from vault (requires vault:read)
    • vault::set_secret(key, value) — Write to vault (requires vault:write)
    • events::emit(event_name, payload) — Emit event to EventBus (requires event:emit)
    • data::create_task(params) -> task_id — Create task (requires data:write)
    • data::get_task(task_id) -> task — Read task (requires data:read)
  6. Create Rust SDK crate (sdk/morphee-sdk-rust/)

    • Auto-generated WIT bindings
    • Helper macros: export_extension!, permission!
    • Examples: examples/echo.rs, examples/hello-world.rs
    • Publish to crates.io (or GitHub registry)

Deliverables:

  • ExtensionManager can load and execute WASM modules
  • ✅ Basic host functions (http, vault, events) working
  • ✅ Example extension (echo.wasm) runs successfully on Python + Rust
  • ✅ Rust SDK crate published and documented

Acceptance Criteria:

  • Load echo.wasm, call execute("echo", [("message", "hello")]), get "hello" back
  • HTTP permission check: extension without http:write cannot POST (returns error)
  • Resource limit: extension exceeding memory limit traps gracefully
  • Mobile runtime: wasm3 interpreter works on iOS/Android

Dependencies: None


Phase 2: Security & Permissions (2 weeks, L effort)

Goal: Production-grade security, permission enforcement, audit logging.

Tasks:

  1. Implement permission model

    • ExtensionPermission enum (10 granular permissions):
      • http:read — GET requests only
      • http:write — POST/PUT/DELETE requests
      • vault:read — Read secrets
      • vault:write — Write secrets
      • event:emit — Emit events to EventBus
      • data:read — Read tasks/conversations/memory
      • data:write — Create/update tasks/conversations
      • task:create — Shortcut for data:write (tasks only)
      • space:read — Read Space metadata
      • notify:send — Send notifications
    • Permission checks in ALL host functions (reject if permission not granted)
    • Install-time approval flow: backend endpoint + frontend dialog
  2. Add resource limiters

    • Memory limit (default 256MB, configurable per extension)
    • CPU limit (fuel-based, Wasmtime consume_fuel, default 10B instructions)
    • Rate limiting (HTTP requests: 100/min default, event emissions: 500/min)
    • Timeout (max 60s execution time per action)
  3. Code signing infrastructure

    • Generate developer keypairs: openssl genrsa -out developer.key 4096
    • Sign extensions: RSA-PSS signature over SHA-256 hash
    • Verify signatures before loading (reject unsigned/invalid extensions)
    • Revocation list (future: check against revoked developer keys)
  4. Audit logging

    • Log all extension actions (INFO level)
    • Include: extension_id, action, user_id, group_id, result, execution_time_ms
    • Store in extension_executions table (PostgreSQL)
    • Never log secrets or PII (only opaque identifiers)
  5. Add security tests

    • Test: Extension without http:write cannot POST
    • Test: Extension exceeding memory limit traps
    • Test: Extension without signature is rejected
    • Test: Unsigned extension is rejected
    • Test: Extension with invalid signature is rejected
    • Test: Extension exceeding CPU limit is terminated

Deliverables:

  • ✅ Install-time permission approval UI (frontend dialog)
  • ✅ All host functions enforce permissions
  • ✅ Resource limits enforced (memory, CPU, rate, timeout)
  • ✅ Code signing mandatory (unsigned extensions rejected)
  • ✅ Audit log tracks all extension actions

Acceptance Criteria:

  • Install JIRA extension → user sees permission prompt → approves → extension installs
  • JIRA extension calls http::fetch(POST) without http:write → error returned
  • Extension uses 300MB RAM (limit 256MB) → traps gracefully
  • Load unsigned extension → rejected with clear error message
  • All extension actions logged to extension_executions table

Dependencies: Phase 1


Phase 3: Distribution (1 week, M effort)

Goal: OCI registry setup, manifest schema, CLI tools for install/update/remove.

Tasks:

  1. Set up rg.morphee.ai dual registry

    • Public registry: GitHub Container Registry (free, unlimited public extensions)
      • Create GitHub organization: morphee-ai
      • Enable GHCR: Settings → Packages → Enable GitHub Container Registry
      • DNS: rg.morphee.ai/public/* → proxy to ghcr.io/morphee-ai/*
    • Private registry (Phase 6+): Self-hosted Harbor (for group-private extensions)
      • Deploy Harbor on DigitalOcean/Hetzner droplet (~$20/month)
      • DNS: rg.morphee.ai/private/*harbor.internal.morphee.ai/*
    • Smart router: Nginx reverse proxy at rg.morphee.ai
  2. Design extension manifest (manifest.json)

    {
    "name": "jira",
    "version": "1.0.0",
    "display_name": "JIRA Integration",
    "description": "Create, update, and read JIRA issues",
    "author": "Morphee Team <hello@morphee.ai>",
    "homepage": "https://morphee.ai/extensions/jira",
    "permissions": ["http:read", "http:write", "vault:read", "data:write"],
    "resource_limits": {
    "memory_mb": 128,
    "cpu_fuel": 10000000000,
    "http_rate_per_min": 100,
    "timeout_sec": 60
    },
    "requires_morphee_version": ">=0.9.0",
    "signature": "base64-encoded-RSA-PSS-signature"
    }
  3. Create CLI tool (morphee-ext CLI)

    • morphee-ext publish — Build, sign, push to registry
    • morphee-ext install <name> — Pull from registry, verify, install
    • morphee-ext list — List installed extensions
    • morphee-ext update <name> — Check for updates, pull, reinstall
    • morphee-ext remove <name> — Uninstall extension
  4. Add backend API endpoints (backend/api/extensions.py)

    • GET /api/extensions — List available extensions (from registry)
    • GET /api/extensions/installed — List installed extensions
    • POST /api/extensions/install — Install extension (pull from registry, verify, store)
    • DELETE /api/extensions/{id} — Uninstall extension
    • POST /api/extensions/{id}/execute — Execute extension action

Deliverables:

  • rg.morphee.ai live with dual registry (GHCR for public, Harbor planned for private)
  • ✅ Extension manifest schema defined and documented
  • morphee-ext CLI tool working (publish, install, list, update, remove)
  • ✅ Backend REST API for extension management

Acceptance Criteria:

  • Run morphee-ext install jira → pulls from rg.morphee.ai/public/jira:1.0.0 → installs successfully
  • Run morphee-ext list → shows installed extensions with version, permissions, enabled status
  • Call POST /api/extensions/jira/execute → runs JIRA extension action → returns result

Dependencies: Phase 2


Phase 4: Developer Experience (2 weeks, L effort)

Goal: Excellent SDK documentation, examples, templates, local dev tools.

Tasks:

  1. Write comprehensive SDK docs

    • Getting Started guide
    • WIT interface reference (all host functions documented)
    • Permission model explained
    • Resource limits explained
    • Code signing workflow
    • Publishing to registry
  2. Create example extensions (3 examples)

    • examples/echo — Minimal example (accepts string, returns string)
    • examples/hello-world — Uses http::fetch to call public API
    • examples/task-creator — Uses data::create_task to create Morphee tasks
  3. Create project templates

    • cargo generate morphee-ai/extension-template — Rust template with boilerplate
    • Includes: Cargo.toml, wit bindings, example actions, tests, README
  4. Build local dev tools

    • Hot reload: Watch .wasm file, auto-reload on change
    • Verbose logging: Log all host function calls for debugging
    • Mock mode: Provide mock host functions for unit testing extensions
  5. Create video tutorial (optional)

    • 10-minute walkthrough: "Build Your First Morphee Extension"

Deliverables:

  • ✅ SDK documentation (30+ pages)
  • ✅ 3 example extensions (echo, hello-world, task-creator)
  • ✅ Project template (cargo generate ready)
  • ✅ Local dev tools (hot reload, verbose logging, mock mode)

Acceptance Criteria:

  • Developer reads "Getting Started", generates template, builds extension in <30 minutes
  • Example extensions compile and run successfully
  • Local dev mode: Edit extension code → see changes immediately without restart

Dependencies: Phase 3


Phase 5: Built-in Extensions (3 weeks, XL effort)

Goal: Ship 5 professional integrations as WASM extensions.

Extensions to Build:

  1. JIRA (1 week, M effort)

    • Actions: create_issue, update_issue, get_issue, list_issues, search_issues, add_comment
    • Permissions: http:read, http:write, vault:read
    • OAuth: JIRA OAuth 2.0 (store tokens in vault)
  2. GitHub (1 week, M effort)

    • Actions: create_issue, create_pr, list_prs, add_comment, merge_pr, list_commits
    • Permissions: http:read, http:write, vault:read
    • OAuth: GitHub OAuth 2.0
  3. Notion (3 days, S effort)

    • Actions: create_page, update_page, get_page, search
    • Permissions: http:read, http:write, vault:read
    • OAuth: Notion OAuth 2.0
  4. Linear (3 days, S effort)

    • Actions: create_issue, update_issue, list_issues, add_comment
    • Permissions: http:read, http:write, vault:read
    • OAuth: Linear OAuth 2.0
  5. Slack (3 days, S effort)

    • Migrate existing slack.py to WASM extension
    • Actions: send_message, list_channels, read_messages
    • Permissions: http:read, http:write, vault:read

Tasks:

  1. Build each extension in Rust using morphee-sdk-rust
  2. Implement OAuth flows (store tokens in vault)
  3. Test on Python backend + Rust frontend (verify portability)
  4. Sign extensions with developer key
  5. Publish to rg.morphee.ai/public/{name}:1.0.0
  6. Write user-facing docs for each extension

Deliverables:

  • ✅ 5 professional integrations shipped as WASM
  • ✅ All published to rg.morphee.ai/public/*
  • ✅ User docs for each extension (setup guide, available actions)

Acceptance Criteria:

  • User runs morphee-ext install jira → JIRA extension installs
  • User connects JIRA account (OAuth) → credentials stored in vault
  • AI creates JIRA issue via chat → JIRA extension called → issue created successfully
  • Same extension works on Tauri desktop app (Rust runtime)

Dependencies: Phase 4


Phase 6: Marketplace UI (2 weeks, L effort)

Goal: Frontend catalog, install flow, auto-updates, usage analytics.

Tasks:

  1. Create marketplace catalog page (frontend/src/pages/Marketplace.tsx)

    • List available extensions from rg.morphee.ai
    • Filter by category (productivity, communication, development)
    • Search extensions
    • Show extension details (name, description, author, permissions, reviews)
  2. Build install flow

    • Extension detail page with "Install" button
    • Show permission approval dialog (like Google Play Store)
    • User approves → extension downloads, verifies signature, installs
    • Show installation progress
  3. Add Extensions settings tab (frontend/src/pages/settings/ExtensionsTab.tsx)

    • List installed extensions
    • Enable/disable extensions
    • View extension permissions
    • Uninstall extensions
  4. Implement auto-updates

    • Background check for updates (daily)
    • Show notification: "JIRA extension update available (1.0.0 → 1.1.0)"
    • User approves → downloads, verifies, updates
  5. Add usage analytics

    • Track: installs, executions, errors, execution time
    • Dashboard: Most popular extensions, trending extensions
    • Privacy-respecting (aggregate only, no user tracking)
  6. Build verified publisher program

    • Green checkmark for vetted developers
    • Require: identity verification, code review, track record
    • Display on extension cards

Deliverables:

  • ✅ Marketplace catalog page (browse, search, install)
  • ✅ Install flow with permission approval
  • ✅ Extensions settings tab (manage installed extensions)
  • ✅ Auto-update system
  • ✅ Usage analytics dashboard

Acceptance Criteria:

  • User browses marketplace → sees 5+ extensions
  • User clicks "Install JIRA" → sees permission dialog → approves → extension installs
  • User goes to Settings → Extensions → sees installed extensions → can enable/disable
  • System checks for updates daily → notifies user → user approves → extension updates

Dependencies: Phase 5


5. Questions & Answers

Q1: Timeline Priority — Build WASM first or hardcode JIRA first?

Answer: Build WASM system first (Option A, 12 weeks)

Reasoning: User explicitly said "I don't want to do the work twice." Building JIRA as hardcoded Python first, then migrating to WASM later = 14 weeks total (2 weeks hardcoded + 12 weeks WASM + migration). Better to invest 12 weeks once and get the right architecture.


Q2: Marketplace Hosting — Where to host the extension registry?

Answer: Dual registry at rg.morphee.ai

Architecture:

  • Public extensions: rg.morphee.ai/public/* → proxy to GitHub Container Registry (free, unlimited)
  • Private extensions: rg.morphee.ai/private/* → self-hosted Harbor (Phase 6+, ~$20/month)

Phase 1-3: Use GHCR only (simple, free) Phase 6+: Add Harbor for private group extensions

Why dual registry?

  • Align with Morphee's privacy-first values (self-hosted option for sensitive extensions)
  • Community-friendly (free public registry for community extensions)
  • Scalable (GitHub handles 99% of traffic, you only pay for private extensions)
  • Migration path (start simple, add complexity when needed)

Q3: Mobile Strategy — How to handle mobile?

Answer: Use wasmer 5.0+ for ALL Tauri targets — single unified API with automatic backend selection per platform.

The critical constraint discovered (Feb 2026): iOS prohibits JIT compilation (App Store Review Guidelines §2.5.2). This means:

  • ✅ Wasmtime JIT: works on macOS/Windows/Linux/Android
  • ❌ Wasmtime JIT: blocked by Apple on iOS — apps using JIT are rejected from the App Store
  • ✅ Wasmer with Wasmi interpreter: works on iOS (interpreter-only, Apple-compliant)

Implementation (wasmer 5.0, single crate):

// Platform-adaptive backend — same API for all platforms
#[cfg(target_os = "ios")]
let store = Store::new(wasmer::Singlepass::default()); // Wasmi interpreter, iOS-safe

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

Why wasmer over the original wasmtime + wasm3 plan?

  • Unified API: One crate, one API — no separate wasm3 bindings, no platform-switching logic
  • iOS coverage: wasmer's Wasmi backend is explicitly designed for iOS (wasmer 5.0 announcement)
  • Better performance on iOS: wasmer Wasmi gives 50-60% native vs wasm3's 65% — a small trade-off for a vastly simpler codebase
  • Same binary size: wasmer ~12MB (was planning wasmtime ~15MB + wasm3 ~200KB separately anyway)

wasm3 as future option: wasm3 (64KB, 65% native, sync-only) is tracked as a potential ultra-minimal mobile alternative for extremely constrained scenarios (low-end Android, microcontrollers). It is not in the current plan because wasmer covers all current use cases with a better developer experience.


Q4: Extension Approval — When should user approve permissions?

Answer: Install-time with granular permissions (like Google Play Store)

Flow:

  1. User clicks "Install JIRA" in marketplace
  2. Frontend shows permission dialog:
    JIRA Integration requests:
    ✅ Read web content (http:read)
    ✅ Send web requests (http:write)
    ✅ Read credentials (vault:read)
    ✅ Create tasks (data:write)

    [Cancel] [Install]
  3. User clicks "Install" → permissions granted → extension installs
  4. No runtime prompts — once approved, extension has permissions until uninstalled

Why install-time?

  • User reviews ALL permissions upfront (informed consent)
  • No disruptive runtime prompts (better UX)
  • Aligns with industry standard (Google Play Store, iOS App Store, VS Code extensions)
  • User can revoke by uninstalling (or future: permission management UI)

Q5: OpenMorph Integration — When to integrate .morph/extensions/*.wasm?

Answer: Extensions work standalone now, integrate with OpenMorph in Phase 3i

File Extension Association System (Phase 3i):

  • GitStore maps file extensions to Integrations via can_open(extension: str) -> bool interface
  • Examples:
    • .wasmWASMIntegration.can_open(".wasm") returns true
    • .jpg, .png, .gifImageIntegration.can_open(".jpg") returns true
    • .pyPythonIntegration.can_open(".py") returns true
    • .txt, .mdTextIntegration.can_open(".txt") returns true
  • Multi-Integration Support: One file can be opened by multiple Integrations
    • Example: .py can be opened by TextIntegration (edit as text) OR PythonIntegration (execute)
  • Multi-File Support: One Integration can open multiple file types
    • Example: ImageIntegration opens .jpg, .png, .gif, .webp, .svg
  • Discovery: When Space loads, GitStore scans .morph/extensions/, asks each Integration "can you open this file?", registers matches with InterfaceManager

Why defer to Phase 3i?

  • WASM extension system is useful standalone (marketplace install)
  • Phase 3i is already planned (Git-Native Spaces, OpenMorph spec)
  • File extension association is a broader feature (benefits images, Python scripts, etc., not just WASM)
  • Better to build WASM extensions first, then integrate with OpenMorph later (incremental delivery)

6. Open Items

Deferred to Future Phases

  1. AssemblyScript SDK (Phase 7+)

    • Provide TypeScript/AssemblyScript SDK for web developers
    • Effort: M (2 weeks)
  2. Extension Playground (Phase 8+)

    • Web-based editor (like Rust Playground) for writing/testing extensions
    • Effort: L (3 weeks)
  3. Verified Publisher Program (Phase 6)

    • Green checkmark for vetted developers
    • Effort: S (3 days)
  4. Extension Analytics Dashboard (Phase 6)

    • Track installs, executions, errors
    • Effort: M (1 week)
  5. Hot Reload for Production (Phase 7+)

    • Currently only in local dev mode
    • Effort: S (3 days)

Unresolved Questions

  1. Extension Pricing: Should we support paid extensions in marketplace?

    • Defer to Phase 8+ (need payment infrastructure)
  2. Extension Reviews: Should users be able to review/rate extensions?

    • Yes, but defer to Phase 7+ (need moderation system)
  3. Extension Sandboxing on Web: Can we run WASM extensions in browser?

    • Yes (browser has native WASM support), but defer to Phase 7+ (need WASI polyfill)
  4. Cross-Extension Communication: Can one extension call another?

    • Not in Phase 1-6 (KISS), consider for Phase 7+ if demand

7. References

Research Documents (Created by Research Agent)

External Resources

WASM Runtimes:

WASM Standards:

Real-World Examples:

OCI Registry:


8. Success Metrics

Phase 1 (Foundation)

  • ✅ Load and execute echo.wasm successfully
  • ✅ Permission check: extension without http:write cannot POST
  • ✅ Resource limit: extension exceeding memory limit traps gracefully
  • ✅ Mobile: wasm3 works on iOS/Android

Phase 2 (Security)

  • ✅ Install-time permission approval UI working
  • ✅ Unsigned extension is rejected
  • ✅ All extension actions logged to audit table
  • ✅ Resource limits enforced (memory, CPU, rate, timeout)

Phase 3 (Distribution)

  • rg.morphee.ai registry live and accessible
  • morphee-ext install jira → pulls and installs successfully
  • ✅ Extension manifest validated on install

Phase 4 (Developer Experience)

  • ✅ Developer builds first extension in <30 minutes using docs
  • ✅ 3 example extensions compile and run
  • ✅ Hot reload works in local dev mode

Phase 5 (Built-in Extensions)

  • ✅ 5 professional integrations published (JIRA, GitHub, Notion, Linear, Slack)
  • ✅ Same extension runs on Python backend AND Rust frontend (verified)
  • ✅ OAuth flows working (credentials stored in vault)

Phase 6 (Marketplace)

  • ✅ User installs extension via UI (no CLI required)
  • ✅ Auto-update notifies user of new versions
  • ✅ Analytics dashboard shows install/usage stats

Overall Success

  • Zero duplication: Same binary runs on Python + Rust (portability goal achieved)
  • Community-ready: Third-party developer builds and publishes extension
  • OpenMorph synergy: Extension stored in .morph/extensions/*.wasm works offline
  • Security: No security incidents (escapes, data leaks, crashes)

Appendix A: Technology Stack Summary

ComponentTechnologyVersionWhy Chosen
Python Runtimewasmtime-py41+Best security (rigorously tested), 88% native, full WASI 0.3 async
Tauri Desktop Runtimewasmer (Cranelift)5.0+Unified API with wasmer mobile; 80-88% native JIT
Tauri iOS Runtimewasmer (Wasmi)5.0+iOS JIT restriction — Wasmi interpreter is Apple-compliant; 50-60% native
Tauri Android Runtimewasmer (Cranelift)5.0+Same crate as desktop; 80-88% native JIT
Future Mobilewasm30.5+64KB ultra-minimal interpreter; tracked as optional future alternative
Frontend UI RuntimeJSRuntime (webview JS)Canvas components and dynamic UI integrations run as JavaScript in Tauri webview
Future Python RuntimePythonRuntimeDynamic Python scripts — same BaseMorphRuntime contract as WASM
Interface StandardComponent Model2025+Rich types, async support (WASI 0.3), future-proof
Public RegistryGitHub Container RegistryFree, unlimited public extensions, zero maintenance
Private RegistryHarbor2.11+Self-hosted, CNCF graduated, privacy-first
Code SigningRSA-PSS + SHA-256Industry standard, OpenSSL compatible
SDK LanguageRust1.75+Best WASM tooling, compile to tiny binaries
Alternative SDKAssemblyScriptTypeScript-like, lower barrier for web devs (Phase 7+)

Appendix B: Permission Model

10 Granular Permissions

PermissionDescriptionHost Functions GatedExample Use Case
http:readHTTP GET requestshttp::fetch (GET only)Fetch data from external API
http:writeHTTP POST/PUT/DELETEhttp::fetch (all methods)Create JIRA issue, send Slack message
vault:readRead secrets from vaultvault::get_secretRetrieve API tokens for external service
vault:writeWrite secrets to vaultvault::set_secretStore OAuth tokens after authentication
event:emitEmit events to EventBusevents::emitNotify app of external updates (e.g., new email)
data:readRead tasks/conversations/memorydata::get_task, data::list_tasksShow JIRA issues in Morphee UI
data:writeCreate/update tasks/conversationsdata::create_task, data::update_taskCreate Morphee task from JIRA issue
task:createShortcut for data:write (tasks only)data::create_taskSimpler permission for task-only extensions
space:readRead Space metadataspace::get_current, space::listDetermine which Space user is in
notify:sendSend notificationsnotify::sendNotify user of critical events

Install-Time Approval Flow

User clicks "Install JIRA Extension"

Frontend fetches manifest from rg.morphee.ai

Frontend shows permission dialog:
┌────────────────────────────────────────────┐
│ JIRA Integration │
│ │
│ This extension requests: │
│ ✅ Read web content (http:read) │
│ ✅ Send web requests (http:write) │
│ ✅ Read credentials (vault:read) │
│ ✅ Create tasks (data:write) │
│ │
│ The extension can: │
│ • Create JIRA issues from Morphee tasks │
│ • Sync JIRA issues to Morphee │
│ • Store your JIRA API token securely │
│ │
│ [Cancel] [Install] ← │
└────────────────────────────────────────────┘

User clicks "Install"

Backend downloads .wasm from registry

Backend verifies signature (RSA-PSS + SHA-256)

Backend stores extension + permissions in database

Frontend shows "JIRA Extension installed successfully"

Appendix C: File Extension Association System (Phase 3i)

Goal: GitStore maps file types to Integrations for seamless opening of files in .morph/ directories.

Interface Addition to BaseInterface

class BaseInterface:
def can_open(self, extension: str) -> bool:
"""
Returns True if this Integration can open files with the given extension.

Examples:
WASMIntegration.can_open(".wasm") → True
ImageIntegration.can_open(".jpg") → True
ImageIntegration.can_open(".png") → True
TextIntegration.can_open(".txt") → True
TextIntegration.can_open(".md") → True
PythonIntegration.can_open(".py") → True

One file type can be opened by multiple Integrations (e.g., .py can be
opened by TextIntegration OR PythonIntegration).

One Integration can open multiple file types (e.g., ImageIntegration
opens .jpg, .png, .gif, .webp, .svg).
"""
return False # Default: cannot open any files

Example Implementations

class WASMIntegration(BaseInterface):
def can_open(self, extension: str) -> bool:
return extension == ".wasm"

async def execute(self, action_name: str, parameters: Dict) -> ActionResult:
if action_name == "load":
# Load WASM file from GitStore
wasm_path = parameters["path"]
# ... load and execute WASM module
# ...

class ImageIntegration(BaseInterface):
def can_open(self, extension: str) -> bool:
return extension in [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp"]

async def execute(self, action_name: str, parameters: Dict) -> ActionResult:
if action_name == "view":
# Display image from GitStore
image_path = parameters["path"]
# ... render image in FrontendIntegration
# ...

class PythonIntegration(BaseInterface):
def can_open(self, extension: str) -> bool:
return extension == ".py"

async def execute(self, action_name: str, parameters: Dict) -> ActionResult:
if action_name == "run":
# Execute Python script from GitStore
script_path = parameters["path"]
# ... run script in sandboxed environment
# ...

class TextIntegration(BaseInterface):
def can_open(self, extension: str) -> bool:
return extension in [".txt", ".md", ".log", ".json", ".yaml", ".toml", ".py"]

async def execute(self, action_name: str, parameters: Dict) -> ActionResult:
if action_name == "edit":
# Open file in text editor
file_path = parameters["path"]
# ... show editor UI via FrontendIntegration
# ...

GitStore Discovery Flow

# When Space loads
async def load_space(space_id: str):
git_store = GitStore(space_id)
files = git_store.list_files(".morph/extensions/")

for file_path in files:
extension = Path(file_path).suffix # e.g., ".wasm"

# Ask all Integrations: "Can you open this file type?"
for integration in interface_manager.list_integrations():
if integration.can_open(extension):
logger.info(f"{integration.name} can open {extension} files")
# Register file handler
file_handlers[extension].append(integration)

# User clicks on .morph/extensions/jira.wasm in UI
# → UI shows: "Open with: WASM Integration, Text Integration"
# → User chooses WASM Integration
# → Calls WASMIntegration.execute("load", {"path": ".morph/extensions/jira.wasm"})

Multi-Integration Example

File: .morph/extensions/jira.wasm

Can be opened by:
1. WASMIntegration → Load and execute extension
2. TextIntegration → View raw bytes (hex editor)

User right-clicks in UI:
┌─────────────────────────────┐
│ Open with... │
├─────────────────────────────┤
│ ▶ WASM Integration (default)│
│ Text Integration │
└─────────────────────────────┘

Appendix D: Effort Estimates Breakdown

PhaseTaskEffortJustification
1. FoundationAdd dependenciesXS (1 hour)Just update requirements.txt and Cargo.toml
Create backend moduleM (3 days)6 files, ~800 lines of Python
Create frontend moduleM (3 days)4 files, ~600 lines of Rust
Design WIT interfaceS (1 day)Draft interface, generate bindings
Implement host functionsM (3 days)6 host functions, permission checks
Create Rust SDKS (2 days)Bindings + helper macros + examples
TOTALL (2 weeks)
2. SecurityPermission modelM (3 days)10 permissions, checks in all host functions
Resource limitersM (3 days)Memory, CPU, rate, timeout limiters
Code signingS (2 days)RSA-PSS signature, verification
Audit loggingS (2 days)Log extension actions to database
Security testsS (2 days)5 security tests
TOTALL (2 weeks)
3. DistributionSet up registryS (2 days)GHCR setup, DNS config
Design manifestXS (1 day)JSON schema
CLI toolM (2 days)5 commands (publish, install, list, update, remove)
Backend APIS (2 days)4 endpoints
TOTALM (1 week)
4. Developer ExperienceSDK docsM (3 days)30+ pages of documentation
Example extensionsS (2 days)3 examples
Project templatesXS (1 day)Cargo template
Local dev toolsS (2 days)Hot reload, verbose logging, mock mode
TOTALL (2 weeks)
5. Built-in ExtensionsJIRA extensionM (1 week)6 actions, OAuth, tests
GitHub extensionM (1 week)6 actions, OAuth, tests
Notion extensionS (3 days)4 actions, OAuth, tests
Linear extensionS (3 days)4 actions, OAuth, tests
Slack extension (migrate)S (3 days)Migrate existing slack.py to WASM
TOTALXL (3 weeks)
6. Marketplace UICatalog pageM (3 days)Browse, search, filter
Install flowM (3 days)Permission dialog, download, install
Settings tabS (2 days)List installed, enable/disable, uninstall
Auto-updatesS (2 days)Background check, notification, update
AnalyticsM (2 days)Track usage, dashboard
Verified publishersS (1 day)Badge system
TOTALL (2 weeks)
GRAND TOTALXL (12 weeks)

Appendix E: Risk Mitigation Strategies

RiskLikelihoodImpactMitigation
Extension developer learning curve too steepMediumHighExcellent SDK docs, video tutorials, project templates, responsive support
WASM performance insufficientLowHighBenchmarks show 88% native (acceptable), cache compiled modules, profile and optimize
Security vulnerability in wasmtimeLowCriticalUse latest stable version, subscribe to security advisories, have rollback plan
Binary size too large for mobileMediumMediumUse wasm3 (64KB) as default on mobile, wasmtime opt-in for power users
Extension marketplace spam/malwareMediumHighMandatory code signing, verified publisher program, user reviews, moderation
OCI registry downtimeLowMediumDual registry (GHCR + Harbor), cache extensions locally, offline mode
Extension compatibility breaksMediumMediumSemantic versioning, extension specifies required Morphee version, deprecation policy
Debugging extensions too difficultMediumMediumStructured error messages, logging host function, local dev verbose mode, examples
Community adoption too slowMediumMediumBuild 5 high-quality built-in extensions first (demonstrate value), marketing, incentives

END OF INVESTIGATION DOCUMENT