Skip to content

feat: WebAssembly plugin support with end-to-end host invocation#65

Draft
monshri wants to merge 8 commits into
contextforge-org:devfrom
monshri:feat/plugin-wasm-e2e
Draft

feat: WebAssembly plugin support with end-to-end host invocation#65
monshri wants to merge 8 commits into
contextforge-org:devfrom
monshri:feat/plugin-wasm-e2e

Conversation

@monshri

@monshri monshri commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

WASM Plugin End-to-End Support

Resolves #21
Addresses parts of #24

Summary

Adds full WebAssembly plugin support using the WASI Component Model, enabling sandboxed plugin execution with fine-grained capability control over filesystem, HTTP, and environment access.

New crates:
cpex-wasm-plugin: Guest-side plugin crate compiled as a cdylib targeting wasm32-wasip2. Implements the WIT-defined plugin interface (evaluate-hook) with type conversions between WIT representations and Rust domain types.

cpex-wasm-host: Host-side runtime using Wasmtime. Includes:

  • sandbox_manager.rs — Resource-limited WASM execution (memory caps, CPU time limits, network allowlists)
  • policy_loader.rs — Loads sandbox policies from YAML config
  • factory.rs — Instantiates WASM plugin modules
  • conversions.rs — Marshals types across the host/guest boundary

cpex-payload — a stripped-down subset of cpex-core types specifically designed to compile to WASM. cpex-core depends on heavy crates (async runtimes like tokio, crypto libraries, FFI layers) that don't compile to wasm32-wasip2. This crate extracts the pure data types and trait definitions needed by the plugin guest.

  • Contains: CMF types (messages, content, views, enums), config structs, extensions (agent, completion, delegation, filter, HTTP, LLM, MCP, security, tiers), hook trait definitions, and plugin types.
  • Drops:No async runtime, no networking, no cpex-core executor/manager/registry logic, no FFI dependencies.
  • Adds a sample identity_checker plugin under src/plugins/ as a reference implementation.
  • Workspace Cargo.toml updated to include the new crate.

Architecture

cpex-wasm-plugin compiles plugins into a portable plugin.wasm binary. The host (cpex-wasm-host) loads and executes the WASM plugin inside a sandboxed wasmtime environment. Enforces resource limits (fuel, memory, execution time) and network/filesystem policies. Provides a bridge to cpex-core's PluginManager for integration into the hook pipeline.

┌──────────────────────────────────────────────────────────┐
│  PluginManager (cpex-core)                               │
│    invoke_named::<CmfHook>("cmf.tool_pre_invoke", ...)   │
└──────────────────────┬───────────────────────────────────┘
                       │
                       ▼
┌──────────────────────────────────────────────────────────┐
│  WasmBridgeHandler (factory.rs)                          │
│    native MessagePayload → WIT MessagePayload            │
│    native Extensions     → WIT Extensions                │
│    native PluginContext   → WIT PluginContext             │
└──────────────────────┬───────────────────────────────────┘
                       │
                       ▼
┌──────────────────────────────────────────────────────────┐
│  SandboxManager (sandbox_manager.rs)                     │
│    call_handle_hook() inside wasmtime sandbox             │
│    ┌─────────────────────────────────┐                   │
│    │  Sandbox Enforcement            │                   │
│    │  • Fuel budget (session-level)  │                   │
│    │  • Memory limit                 │                   │
│    │  • Execution timeout (per-call) │                   │
│    │  • Network allowlist            │                   │
│    │  • Filesystem permissions       │                   │
│    │  • Environment variable filter  │                   │
│    └─────────────────────────────────┘                   │
└──────────────────────┬───────────────────────────────────┘
                       │
                       ▼
┌──────────────────────────────────────────────────────────┐
│  WIT PluginResult → native PluginResult                  │
│  returned to PluginManager pipeline                      │
└──────────────────────────────────────────────────────────┘
plugins:
  - name: identity-checker
    kind: wasm://plugin.wasm
    hooks: [cmf.tool_pre_invoke, cmf.tool_post_invoke]
    mode: sequential
    priority: 10
    on_error: fail
    capabilities:
      - read_labels
      - read_subject
      - read_roles   
    config:
      # Sandbox policy controls what host resources the WASM plugin can access.
      # Empty lists = full lockdown (deny-all): no filesystem, no network, no env vars.
      # The plugin can only operate on data passed via handle-hook arguments.
      sandbox_policy:
        allowed_filesystem: []   # No host filesystem access
        allowed_network: []      # No outbound HTTP allowed
        allowed_env: []          # No host environment variables exposed
        resources:
          max_memory_bytes: 10485760   # 10 MB linear memory cap
          max_fuel: 1000000000         # ~1 billion instructions (session-level budget)
          max_execution_time_ms: 5000  # 5 seconds per invocation timeout
          max_instances: 10            # Max WASM module instances
          max_tables: 10               # Max WASM tables
  
routes:
  - tool: "*"
    plugins: []

cpex-wasm-host — Host Runtime

Loads and executes .wasm plugins inside a sandboxed wasmtime environment with resource limits (fuel, memory, execution time) and network/filesystem policies.

Component Role
SandboxManager Manages plugin lifecycle in an isolated wasmtime instance. Enforces fuel budgets, execution timeouts, network allowlists, preopened directories, and env-var filtering.
PolicyLoader Parses sandbox config from YAML (sandbox_policy key). Deny-by-default when absent.
Conversions Bidirectional type mappings between cpex-core types and WIT types (JSON serialization for complex types at the boundary).
Factory Bridges cpex-core's PluginFactory trait: WasmPluginFactoryWasmBridgePluginWasmBridgeHandler.

Usage Modes

  • Via PluginManager — Register WasmPluginFactory with a wasm:// scheme, load config, invoke hooks through the standard pipeline.

Details: Readme

cpex-wasm-plugin — Plugin Template

Compiles CPEX plugins into a portable plugin.wasm binary, loaded and executed in the host sandbox.

  • WIT interface (wit/world.wit) — Defines the host-plugin contract: a single exported handle-hook function receiving message payload, extensions, and plugin context; returns allow/deny.
  • src/lib.rs — Plugin entry point implementing the Guest trait; delegates to plugin logic in cpex-payload.
  • src/conversions.rs — Bidirectional type mapping between WIT flat types and rich Rust types.

Details: Readme

cpex-payload — Plugin Template

This crate includes only the files required for plugin compilation. Compiling the entire cpex-core into a WebAssembly component caused issues, so this separation was necessary. crates/cpex-payload/src/plugins/identity_checker.rs provides the implementation of the plugin but with fn vs async fn, since the WebAssembly Component Model does not yet support asynchronous calls.

@monshri monshri self-assigned this Jun 9, 2026
@monshri monshri marked this pull request as draft June 9, 2026 18:27
@monshri monshri marked this pull request as ready for review June 9, 2026 18:56
@monshri monshri force-pushed the feat/plugin-wasm-e2e branch from 51aeb57 to 7fde6ca Compare June 9, 2026 19:23
@araujof araujof assigned araujof and unassigned monshri Jun 10, 2026
@araujof araujof added this to CPEX Jun 10, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in CPEX Jun 10, 2026
@araujof araujof moved this from Backlog to In progress in CPEX Jun 10, 2026
@araujof araujof added this to the 0.2.0 milestone Jun 10, 2026
@araujof araujof changed the title WebAssembly plugin support with end-to-end host invocation feat: WebAssembly plugin support with end-to-end host invocation Jun 10, 2026
@monshri

monshri commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@araujof @terylt Few things to discuss on current implementation:

1. Raw Credentials - Completely Blocked

  • Intentional security boundary - tokens never cross WASM boundary
  • WASM plugins cannot access:
    • Inbound bearer tokens (JWTs, OAuth tokens)
    • Delegated/minted tokens for downstream services
    • Any raw credential material
  • Impact: Cannot implement identity resolvers, token exchangers, or forwarding proxies

2. SecurityExtension - Truncated

Only 4 of 8 fields are available in WASM:

Available:

  • labels - security labels
  • classification - data classification
  • subject - user identity (ID, roles, permissions, claims)
  • auth_method - authentication method used

Missing:

  • client (ClientExtension) - OAuth client identity unavailable
  • caller_workload (WorkloadIdentity) - can't do mTLS/SPIFFE-based policy
  • this_workload (WorkloadIdentity) - can't inspect outbound identity
  • objects/data policy maps - can't read data/object policies

Impact: WASM plugins cannot make workload-aware or client-aware authorization decisions

3. Extension Slots - Only 4 of 13 Available

Available in WASM:

  • request - request metadata (ID, timestamp, trace)
  • security - partial (see above)
  • http - request/response headers
  • meta - entity metadata, tags, properties

Missing from WASM:

  • agent - session/conversation/lineage context
  • mcp - MCP tool/resource/prompt metadata
  • completion - LLM token usage/stop reason
  • provenance - message origin/threading
  • llm - model identity/capabilities
  • framework - agentic framework context
  • delegation - delegation chain
  • raw_credentials - intentionally absent (security boundary)
  • custom - custom extension bag

To discuss

For 1, we can discuss if we want to keep it as-is or not
For 2 and 3, extensions could be extended through WIT definitions, however, we need to discuss what are the use cases we want to limit the wasm based plugins to?

@monshri monshri marked this pull request as draft June 18, 2026 13:34
@araujof araujof modified the milestones: 0.2.0, 0.2.2 Jul 1, 2026
@monshri monshri force-pushed the feat/plugin-wasm-e2e branch from 888ec5d to 51b3347 Compare July 2, 2026 23:38
monshri added 5 commits July 2, 2026 19:45
Introduces the WASM plugin sandbox system: cpex-wasm-plugin (guest-side
cdylib targeting wasm32-wasip2) and cpex-wasm-host (host-side runtime
using wasmtime with sandbox policy enforcement, resource limits, and
network filtering).

Signed-off-by: Shriti Priya <shritip@ibm.com>
… functionality in wasm compilation

Signed-off-by: Shriti Priya <shritip@ibm.com>
Signed-off-by: Shriti Priya <shritip@ibm.com>
Signed-off-by: Shriti Priya <shritip@ibm.com>
Signed-off-by: Shriti Priya <shritip@ibm.com>
monshri added 2 commits July 2, 2026 19:48
Signed-off-by: Shriti Priya <shritip@ibm.com>
Signed-off-by: Shriti Priya <shritip@ibm.com>
@monshri monshri force-pushed the feat/plugin-wasm-e2e branch from 51b3347 to 18988db Compare July 2, 2026 23:52
…ation

Fix WIT keyword conflict (%resource escape), add feature-gated plugin SDK
supporting multiple plugins from a single crate, and implement three WASM
demos: CMF pipeline, identity resolution, and capabilities isolation with
extension modification across the sandbox boundary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

2 participants