Repository navigation
feat(extension): icp-extension runtime crate (PoC 1/2) - #839
Merged
Merged
Conversation
lwshang
force-pushed
the
lwshang/extension-runtime
branch
from
October 22, 2026 02:34
aa1c99e to
d66ace5
Compare
Contributor
There was a problem hiding this comment.
🟡 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
lwshang
force-pushed
the
lwshang/extension-runtime
branch
from
October 26, 2026 09:12
0a7bb2d to
1058918
Compare
raymondk
force-pushed
the
lwshang/extension-runtime
branch
from
October 26, 2026 19:45
989a643 to
3625087
Compare
raymondk
force-pushed
the
lwshang/extension-runtime
branch
from
October 26, 2026 22:12
3625087 to
72c9e63
Compare
raymondk
approved these changes
Oct 27, 2026
lwshang
force-pushed
the
lwshang/extension-runtime
branch
from
October 27, 2026 06:56
72c9e63 to
e89c932
Compare
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
force-pushed
the
lwshang/extension-runtime
branch
from
October 27, 2026 11:04
e89c932 to
a2aa486
Compare
raymondk
marked this pull request as ready for review
October 27, 2026 13:46
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


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: theicp:extension@0.1.0world.describe(what it is and which commands it has, as data) andrun(run one parsed command).canister-call(as the resolved identity, on the resolved network),confirmandopen-url.runtime.rs: loads the component and picks the ABI from itsicp:extension/types@<version>import, the same way the sync-plugin runtime does.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 mockCanisterCalls).Stacked under #840, which wires this into the CLI.
🤖 Generated with Claude Code