Skip to content

feat(extension): icp-extension runtime crate (PoC 1/2) - #839

Merged
raymondk merged 4 commits into
mainfrom
lwshang/extension-runtime
Oct 27, 2026
Merged

raymondk merged 4 commits into
mainfrom
lwshang/extension-runtime

Conversation

@lwshang

@lwshang lwshang commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Draft / proof of concept. Part 1 of 2 of the icp-cli extensions PoC. Not meant to merge as is.

What

A new crate, icp-extension: the host side of icp-cli extensions. These are third-party commands (icp <name> …) shipped as a single wasm component and run in a wasmtime sandbox.

  • extension.wit: the icp:extension@0.1.0 world.
    • The extension exports describe (what it is and which commands it has, as data) and run (run one parsed command).
    • It imports canister-call (as the resolved identity, on the resolved network), confirm and open-url.
  • runtime.rs: loads the component and picks the ABI from its icp:extension/types@<version> import, the same way the sync-plugin runtime does.
    • No compute limit: extensions are interactive and may poll for minutes.
    • On mainnet, decided by root key, a call to a canister the extension didn't declare is refused. On any other network calls are unrestricted, so local canister IDs need no configuration.
  • validate.rs: install-time checks on what an extension declares (reserved flags such as -e/-n/-k/--identity, duplicates, argument shapes, groups), so nothing it declares can break icp's clap tree.
  • tests/fixtures/hello-extension: a test extension exercising every import and export.

The sandbox has no preopened directories, no env, no args, no stdin and no sockets. stdout/stderr go to the terminal. Clocks and randomness are WASI defaults.

Why

Integrations like CyclePay change faster than icp-cli should. With an extension boundary they can ship on their own schedule without icp-cli carrying their code. The design notes are in the linked report. The key choice is that the extension declares its commands, and icp-cli parses them: extensions never handle argv, and help, errors and completion stay consistent with built-in commands.

Tests

cargo test -p icp-extension: describe, errors, confirm/open-url, the "no network" refusal, and the mainnet allowlist versus unrestricted local calls (with a mock CanisterCalls).

Stacked under #840, which wires this into the CLI.

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings October 22, 2026 02:32
@lwshang
lwshang force-pushed the lwshang/extension-runtime branch from aa1c99e to d66ace5 Compare October 22, 2026 02:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Install-time validation still accepts declarations that can conflict with host options or violate clap’s command-tree requirements.

5 open issues
What changed in this PR

Adds the proof-of-concept icp-extension host crate for running third-party WebAssembly commands in a Wasmtime sandbox. This provides the runtime foundation; CLI integration is deferred to part 2.

Changes:

  • Defines the extension interface, command metadata, and host callbacks.
  • Adds sandboxed execution, mainnet canister allowlisting, and install-time declaration validation.
  • Adds a test extension and runtime tests for callbacks, errors, and network restrictions.
File Description
crates/​icp-extension/​tests/​fixtures/​hello-extension/​src/​lib.rs Implements the test extension’s commands and callbacks.
crates/​icp-extension/​tests/​fixtures/​hello-extension/​Cargo.toml Defines the standalone fixture crate.
crates/​icp-extension/​tests/​fixtures/​hello-extension/​Cargo.lock Locks fixture dependencies.
crates/​icp-extension/​tests/​fixtures/​hello-extension/​build.rs Tracks interface changes for fixture rebuilds.
crates/​icp-extension/​src/​validate.rs Validates extension declarations and adds validation tests.
crates/​icp-extension/​src/​runtime.rs Implements loading, execution, host callbacks, and runtime tests.
crates/​icp-extension/​src/​lib.rs Exposes runtime APIs and metadata types.
crates/​icp-extension/​extension.wit Defines the versioned extension contract.
crates/​icp-extension/​Cargo.toml Declares runtime and test dependencies.
crates/​icp-extension/​build.rs Builds the fixture when the WebAssembly target is installed.
Cargo.toml Registers the workspace dependency.
Cargo.lock Records the new crate’s dependencies.

Review effort: Balanced

Comment thread crates/icp-extension/src/validate.rs
Comment thread crates/icp-extension/src/validate.rs
Comment thread crates/icp-extension/src/validate.rs
Comment thread crates/icp-extension/src/validate.rs
Comment thread crates/icp-extension/tests/fixtures/hello-extension/src/lib.rs Outdated
@lwshang
lwshang force-pushed the lwshang/extension-runtime branch from 0a7bb2d to 1058918 Compare October 26, 2026 09:12
@lwshang
lwshang requested a balanced review from Copilot October 26, 2026 09:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unrestricted URL delegation exposes unintended host capabilities, and management-canister requests lack the effective routing target they require.

2 open issues
5 resolved since last review

Review effort: Balanced

Comment thread crates/icp-extension/src/runtime.rs Outdated
Comment thread crates/icp-extension/src/runtime.rs
@raymondk
raymondk added this pull request to stack #850 October 26, 2026 13:20
@raymondk
raymondk force-pushed the lwshang/extension-runtime branch from 989a643 to 3625087 Compare October 26, 2026 19:45
@raymondk
raymondk removed this pull request from stack #850 October 26, 2026 21:37
@raymondk
raymondk changed the base branch from main to rk/deny-zstd October 26, 2026 21:37
@raymondk
raymondk added this pull request to stack #852 October 26, 2026 21:37
@raymondk
raymondk force-pushed the lwshang/extension-runtime branch from 3625087 to 72c9e63 Compare October 26, 2026 22:12
Base automatically changed from rk/deny-zstd to main October 27, 2026 06:56
@lwshang
lwshang force-pushed the lwshang/extension-runtime branch from 72c9e63 to e89c932 Compare October 27, 2026 06:56
lwshang and others added 4 commits October 27, 2026 07:04
Adds the host side of icp-cli extensions: the icp:extension@0.1.0 WIT
world and a wasmtime runtime that loads an extension component into a
sandbox with no filesystem, environment, sockets or processes.

An extension declares its commands as data (describe); icp-cli parses
them and calls run with the parsed result and what it resolved. Its only
effects are canister calls (held to the declared allowlist on mainnet,
unrestricted elsewhere), confirm and open-url.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found porting cyclepay: `icp cyclepay cycles` had no description, since
only leaves were declared. A group may now be declared for its about,
and may take no arguments, so the host can always require a subcommand.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From review: reserve the network flags' group ids, refuse a leading '-'
in argument names and long flags (clap strips it, so '-debug' would
become '--debug'), check a default against the possible values, and
refuse a required positional after an optional one. The fixture's call
operands are now required, as its run needs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From review:

- open-url now returns a result and accepts only an absolute http(s)
  URL; the terminal is handed a parsed Url. The opener behind it would
  as readily launch a local file or a custom scheme's handler.
- The management canister can be neither declared nor called. A call to
  it acts on the canister its argument names, which neither the mainnet
  allowlist nor the call's routing (its effective canister id) can see;
  supporting it is left for when an extension needs it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@lwshang
lwshang force-pushed the lwshang/extension-runtime branch from e89c932 to a2aa486 Compare October 27, 2026 11:04
@raymondk
raymondk marked this pull request as ready for review October 27, 2026 13:46
@raymondk
raymondk requested a review from a team as a code owner October 27, 2026 13:46
@raymondk
raymondk merged commit c6c9e97 into main Oct 27, 2026
181 of 183 checks passed
@raymondk
raymondk deleted the lwshang/extension-runtime branch October 27, 2026 13:46
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.

3 participants