Skip to content

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

Draft
monshri wants to merge 7 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 7 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
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