Skip to content

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

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

terylt and others added 8 commits May 6, 2026 14:02
* feat: initial revision rust core.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* fix: addressed comments in PR. Updated PluginContext to match spec.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

---------

Signed-off-by: Teryl Taylor <terylt@ibm.com>
Co-authored-by: Teryl Taylor <terylt@ibm.com>
* feat: added yaml and routing rule support.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: added example code to show how to load manager and plugins.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* fixes: updated plugin errors, configs to more match python.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

---------

Signed-off-by: Teryl Taylor <terylt@ibm.com>
Co-authored-by: Teryl Taylor <terylt@ibm.com>
* feat: initial revision rust core.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* fix: addressed comments in PR. Updated PluginContext to match spec.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: added yaml and routing rule support.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: added example code to show how to load manager and plugins.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* fixes: updated plugin errors, configs to more match python.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: RUST CMF initial revision.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: added invoke named support, added constants, fixed reviewed code.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: added owned extensions and did some refactoring.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

---------

Signed-off-by: Teryl Taylor <terylt@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Co-authored-by: Teryl Taylor <terylt@ibm.com>
Co-authored-by: Frederico Araujo <frederico.araujo@ibm.com>
* feat: initial revision rust core.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* fix: addressed comments in PR. Updated PluginContext to match spec.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: added yaml and routing rule support.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: added example code to show how to load manager and plugins.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* fixes: updated plugin errors, configs to more match python.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: RUST CMF initial revision.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: added invoke named support, added constants, fixed reviewed code.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: added owned extensions and did some refactoring.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: added cgo and golang bindings, examples and readme.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* address P0/P1/P2 review findings (except contextforge-org#17)

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* fix: address remaining P2/P3 review findings + testing gaps

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* docs: add CPEX Go public API spec

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* docs: renamed document

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* feat(cpex-rust): CGO review passes 1-11 + lint cleanup + Makefile targets

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* fix: address linting issues, updated makefile to support building examples.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* docs: updated the go spec to reflect recent changes.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

---------

Signed-off-by: Teryl Taylor <terylt@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Co-authored-by: Teryl Taylor <terylt@ibm.com>
Co-authored-by: Frederico Araujo <frederico.araujo@ibm.com>
Co-authored-by: Teryl Taylor <terylt@ibm.com>
* fix: initial revision APL.

* feat: apl-cpex bridge crate + plugin-registry-driven hook dispatch

* feat: add support for plugin calling in APL routes.

* feat: add more APL plugin support, unified config

* feat: added cedar direct PDP.

* feat: add identity hook and extensions.

* feat: added token delegation hooks and tests.

* feat: added plugin for jwt token identity, oauth and biscuit delegation, cedarling PDP.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* fix: updated identity and delegation to support keycloak. added delegate() function, and identity sections.

* fix: added some sample plugins, added updates to support cedar.

Signed-off-by: Teryl Taylor <terylt@ibm.com>

* feat: added session support, serialize and parallel and full effects capabilities.

* feat: add ffi pre-built .a library

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* chore: add workflow_dispatch target

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* fix: critical and high issues from review.

* feat: add APL FFI and go bindings

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* chore: add musl tools to musl runners

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* fix: potential double free after use bug.

* chore: update Go module paths after repo rename to cpex

* feat: map identity extension into cpex ffi

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* feat: add cpex_invoke_resolved abi

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* fix: has_hook_for handling

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* chore: update headers

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

---------

Signed-off-by: Teryl Taylor <terylt@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Co-authored-by: Frederico Araujo <frederico.araujo@ibm.com>
@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 added 3 commits June 9, 2026 15:20
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>
…ionality in wasm compilation

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 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
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.

3 participants