Skip to content

WebAssembly plugin support with end-to-end host invocation#64

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

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

Conversation

@monshri
Copy link
Copy Markdown
Contributor

@monshri monshri commented Jun 5, 2026

WASM Plugin End-to-End Support

Resolves #21

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-host (Rust-based host runtime) and cpex-wasm-plugin (reusable plugin template), along with the Python packagecpex-payloadfor a lightweight, WebAssembly-compilable version of cpex-core that includes a synchronous plugin implementation (since WebAssembly does not support asynchronous execution).

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

  • Direct — Instantiate SandboxManager, load a .wasm file with a SandboxPolicy, call invoke().
  • 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.

bogdanmariusc10 and others added 30 commits May 11, 2026 12:04
… files (contextforge-org#48)

* fix: respect PLUGINS_LOG_LEVEL environment variable in all runtime.py files

- Updated grpc/server/runtime.py to check PLUGINS_LOG_LEVEL env var before command-line arg
- Updated mcp/server/runtime.py to add logging configuration with PLUGINS_LOG_LEVEL support
- Updated unix/server/runtime.py to respect PLUGINS_LOG_LEVEL instead of hardcoded INFO level
- All implementations now log to stderr and follow consistent pattern

* refactor: use get_settings().log_level instead of hardcoded env var

Address PR review feedback to use the settings infrastructure instead of
directly accessing environment variables. This approach:
- Keeps the env var name defined in one place (PluginsSettings model)
- Gets .env file support for free
- Stays consistent with how other settings are read in the codebase

Changes:
- grpc/server/runtime.py: Use get_settings().log_level with fallback to args
- mcp/server/runtime.py: Use get_settings().log_level
- unix/server/runtime.py: Use get_settings().log_level

---------

Co-authored-by: Bogdan-Marius-Catanus <bogdan-marius.catanus@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Shriti Priya <shritip@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>

---------

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>
Signed-off-by: Shriti Priya <shritip@ibm.com>
Signed-off-by: Shriti Priya <shritip@ibm.com>
…g#55)

* fix: implement __eq__ and __ne__ for CopyOnWriteDict

Fixes equality comparison bug where CopyOnWriteDict compared equal to {}
even when containing data. This caused apply_policy() to incorrectly drop
valid payload modifications when plugins removed all arguments.

Changes:
- Add __eq__ and __ne__ methods to CopyOnWriteDict
- Add 13 comprehensive equality unit tests
- Add policy regression tests for empty args scenario
- Add end-to-end integration tests

Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>

* fix: added length check for performance

Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>

* fix: restore deleted assertion and add performance optimization

- Restored missing 'assert a not in keys' in test_iteration_order_with_deletions
- Added fast-path length check in CopyOnWriteDict.__eq__() for better performance
- Performance optimization is safe: if lengths differ, mappings cannot be equal

Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>

* fix: linted memory.py, added assertion to test.

---------

Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Co-authored-by: Teryl Taylor <teryl.taylor@gmail.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>
Signed-off-by: Shriti Priya <shritip@ibm.com>
…orge-org#31)

* chore: provde better examples for PluginPackageInfo constructor

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: add PluginVersionInfo and PluginVersionRegistry models w/unit tests

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: unit tests and fixtures for plugin isolation via venv.

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: refactored the invoke_hook method in cpex/framework/isolated/client.py to run async

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: updated unit test test_worker to get coverage to 97%.

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: The optimization eliminates the overhead of:
Forking a new Python process (~1.2ms per fork_exec)
Initializing the Python interpreter
Loading modules and dependencies
Setting up the subprocess communication pipes

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: fail early plugin_path do not exist, computer .venv path automatically, update cli to support creating an isolated plugin.

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: use the system config file (PLUGINS_CONFIG_FILE) for syspath update (Consistent with how the PluginManager works).

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: Validate plugin_dirs entries against an allowlist

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: remove hardcoded reference to plugins/config in the cpex/framework/isolated/client.py and update tests. remove methods_to_exclude from validator.

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: Add a maximum line length check before parsing.  Add model tests for PluginPackageInfo and PluginVersionRegistry

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: model updates for PluginManifest, InstalledPluginInfo, and InstalledPluginRegistry

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: example values for git monorepo  installation

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: add ConfigSaver class to ConfigLoader

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: plugin installation catalog

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: add support to enable installation of a plugin using the cli from a git monorepo, or pypi.

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: doc string fix

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: remove duplicate code

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: test coverage improvements, remove duplicate code

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: replace cargo with search for pyproject.

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fixes

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: use pygithub apis rather than github rest apis, as they provide automatic backoff when github response with too many requests.

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: add support for uninstall of plugin

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint-fix

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: use the manifest from the local catalog to pull the kind value of the package to be removed and use that to remove all matching kind entries from plugins/config.yaml unless kind is external or isolated_venv in which case check if the plugin name is a substring of the plugin name.

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: when installing a plugin via mono-repo or pipi, the cache_root will not exist under the plugins dir, create the directory if it does not exist on plugin startup for venv plugins.

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: enable package install from both pypi and test-pypi, fix: properly resolve location of requirements.txt while performing pypi installs.

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: stub for local installation

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* doc: add README for tools

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: use cached repo object

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: use rich emoji

Signed-off-by: habeck <habeck@us.ibm.com>

* ptf: workaround for version mis-match of cpex dependency in plugin

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: download install targets to a temp folder to avoid installing to incorrect venv.

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: pass the plugin install path to the update method, as isolated_venv plugins are not installed in the current venv.

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: the catalog now returns the install path for isolated_venv plugins

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: unit test updates

Signed-off-by: habeck <habeck@us.ibm.com>

* misc: type fix

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: the plugin self installs into the isolated_venv via requirements.txt

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: increase coverage above 90%

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: update min_max_framework_version

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: isolated venv cookiecutter update for install flow

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: catalog now properly persists all plugin-manifest*.yaml files

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: upgrade pip before installing requirements

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: allow the developer provided version registry values to persist, overriding only if they are not present. Improved install path resolution for isolated_venv plugins

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: only update the catalog when not installing from test-pypi  or pypi.  Correctly determine the install path for the plugin registry for monorepo installs and isolated_venv plugins.

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: refactor to reduce duplicate code, fix uninstall for isolated_venv, add install support for type local,

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: properly format info

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: update README.md

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: Add a, "before you begin" section detailing the required .env variable.

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: P0 fix — tarfile/zip path traversal

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: add remove_venv method to IsolatedVenvPlugin for uninstall cleanup.

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: priority 1 items

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: p2 item 17 search() case-insensitive match broken

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: add tests for _ver method.

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: version registry update cleanup

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: add missing doc string, and tests for _ver method.

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: review p2 moderate 21 - list function now uses console.print

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: fix failing unit test, address non-atomic registry write.

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: claude can't tell the difference between if "rc is False" and "if rc".

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: cpex/framework/models.py — register_plugin (line 2392): replaced the unconditional append with a filter-then-append. Any existing entry with the same
   name is removed before the new one is added, so a reinstall upgrades the entry rather than creating a duplicate. One save() call, same atomicity as
  before.

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: P2 Issue 20 Implementation Complete: Exit Code Handling

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: list function shadows built-in

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: logic tweak

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: p2 issue 23

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: P2 issue 19 error handling for corrupted JSON registry

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: P2 issue 24 - Registry file path triplicated

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint-fix

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: P2 issue 25

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: update worker to call cpex.framework.utils.import_module rather than importlib.import_module directly.

Signed-off-by: habeck <habeck@us.ibm.com>

* enh: add package integrity verification

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: missed commit

Signed-off-by: habeck <habeck@us.ibm.com>

* fix: if the plugins/config.yaml plugins array is empty, initialize it with the appropriate default from catalog settings.

Signed-off-by: habeck <habeck@us.ibm.com>

* chore: lint fix

Signed-off-by: habeck <habeck@us.ibm.com>

* chore:  version to '0.1.0 minimum'

Signed-off-by: habeck <habeck@us.ibm.com>

---------

Signed-off-by: habeck <habeck@us.ibm.com>
Signed-off-by: habeck <habeck@us.ibm.com>
Signed-off-by: Shriti Priya <shritip@ibm.com>
Signed-off-by: Shriti Priya <shritip@ibm.com>
…ecker, audit_logger, header_injector

Signed-off-by: Shriti Priya <shritip@ibm.com>
Signed-off-by: Shriti Priya <shritip@ibm.com>
Refactored from multi-feature-flag builds (identity_checker, audit_logger,
header_injector as separate .wasm files) into a single plugin.wasm template.
Users now write plugin logic in cpex-payload and point lib.rs to their function.

- Remove feature flags and per-plugin structs; single Plugin struct with export!()
- Remove unused plugin-config from WIT, clean up world.wit
- Makefile builds one plugin.wasm; add validate/inspect targets (wasm-tools)
- Add README documenting architecture, conversions layer, and plugin authoring steps
- Add comments/license headers to all source files
- Remove stale .wasm binaries and details.md
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>
Signed-off-by: Shriti Priya <shritip@ibm.com>
@monshri monshri marked this pull request as draft June 5, 2026 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants