* Add an explicit native Wasm command plugin runtime * Document the plugin test workflow in English * Report plugin execution causes and explicit fuel budgets * Allow explicit user plugin resource ceilings * Document the persistent isolated plugin test profile * Require the Wasmtime 48.0.8 security fixes Raise the minimum native runtime version and update its locked Cranelift and Wasmtime dependencies for RUSTSEC-2026-0422, RUSTSEC-2026-0423 and RUSTSEC-2026-0424. Preserve the existing plugin resource defaults and approval flow. * claim: #1221 phase 1 * Plugins: share host contracts and reject unused network imports Split plugin manifests, permissions, limits, typed errors, entry schemas and the PluginHost trait into craft-plugin. Put the Wasmtime sandbox in craft-plugin-wasm, with no dependencies on SolidCraft crates and no WASI or network linker. SolidCraft supplies its command catalog and transactions, and the UI routes namespaced plugin calls through the existing worker. Inspect a component's imports before instantiating it (bounded). Refuse sockets, HTTP and fetch imports even when Wasmtime would elide an unused import. Keep net manifest requests forbidden and report typed errors. Exercise WAT components against a tiny independent app and SolidCraft. Cover file reads and writes without grants, swallowed denials, granted reads, runaway fuel exhaustion, traps and rollback, network refusal, read-only entries, exact approval and one-step undo/redo. Attribute the shared fixture. Checked: - cargo test -p craft-plugin-wasm: 11 passed - cargo test -p solidcraft-engine --features plugins plugin: 16 passed - cargo check -p solidcraft-ui-egui --all-targets: passed - cargo clippy -p craft-plugin -p craft-plugin-wasm -p solidcraft-engine --all-targets --features solidcraft-engine/plugins -- -D warnings: passed - cargo xtask layers: passed, 35 crates - cargo xtask lists: passed - cargo fmt -p craft-plugin -p craft-plugin-wasm -p solidcraft-engine -p solidcraft-ui-egui -p xtask -- --check: passed Full-suite and remaining platform checks are left to CI after push. Refs #1221, phase 1; design review remains required before phase 2. * Plugins: model.mesh is an export in the mesh-decisions check The plugin command hands a plugin a bounded copy of a body's display tessellation; it decides nothing about the model. It joins the check's list of display and export functions. * Cargo.lock: the plugin crates follow the workspace version (0.40.1) * Kernel cases: the plugin commands name where they are tested model.mesh, plugin.inspect, plugin.list and plugin.run take no kernel case (the folder takes no new ones); each EXEMPT line names the engine's plugin test file. * Plugins: enforce command authority, file scopes and per-run consent Stage granted file writes until successful execution and app commit. Check caller scopes for every file path and default command effects to edit. Preserve inspection identity, preflight complete scripts and clean up accepted sketch exits. Correct plugin schemas, preference drafts, fixture identifiers and runtime documentation. Add regressions in the existing engine, runtime, UI and CLI test files. Restore package-folder mismatch diagnostics and initialise consent-test catalogs. * Plugin tests: keep a test-only command out of the icon audit * Plugins: a link inside a granted folder is never followed File authorization looks at a path as the plugin spells it, so a visible symbolic link to a hidden folder (public -> .hidden) made public/secret.txt readable, and let staged writes land in the hidden folder, when no caller scope was set. Every file access now checks each part of the requested path inside the granted folder and refuses a link (or a Windows junction), for reads, writes and staged publication alike. A plugin cannot create links itself: it only writes new regular files. Test: a visible link to a hidden folder, and a file link to a hidden file, are refused for reads and writes, nothing is staged through them, and a plain file beside them still reads. * Show plugin entry fuel use in CLI debug logs * Plugin runtime README: links inside a granted folder are refused * Plugins: main's new operations-panel tests build the Answer job with no inspection main added two tests in control_job.rs that construct `Work::Answer` as a unit value; on this branch the variant carries `inspection`. * Designs keep plugin data under plugins/<plugin-id>/ untouched A design container reserves plugins/<plugin-id>/ for a plugin's own files (plugin_data, base64 by <plugin-id>/<path>, in a plain JSON design). They are kept and saved back byte for byte whether or not the plugin is installed, and opening a design never needs a plugin. One pending format file; the io design tests cover save/open with the namespace and without the plugin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Plugins 0.1.0: craft:plugin@0.1.0, manifest capabilities, every MCP command - The WIT package is craft:plugin@0.1.0; today's surface is the commands@0.1 capability (engine -> commands, exported command -> entry). ui, viewport, formats and storage arrive later as their own versioned interfaces; 0.x may break between minors. - plugin.json lists "capabilities": ["commands@0.1"] instead of runtime.api. The host refuses an unknown or newer capability at load with a typed, plain message ("needs commands 0.2; update SolidCraft or the plugin"), and a component importing another interface version before instantiation. - The hard-coded command allowlist is gone: every command in the MCP catalog can be listed. Sensitive groups (file, doc, app, plugin) are refused unless the manifest requests them in permissions.command_groups and the user grants them; a granted file or doc command sees only the granted folders. - Remove the stray REVIEW_REPORT.md; update docs and the test guide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Plugins 0.1.0: cargo fmt --------- Co-authored-by: kenos <crono2srv@gmail.com> Co-authored-by: Brennen Herbruck <brennen.herbruck@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
13 KiB
Command plugin test guide
This guide exercises the experimental native Wasm command host with the independent
example-box plugin. The plugin creates an ordinary 20 x 10 x 3 mm box. It does not
implement the reconstruction, conversion or mesh UI proposed in #8061.
What you need
Use a Windows x64 test distribution built from this branch, extracted into a writable local folder. The distribution contains the GUI, CLI, a compiled component, scripts, source, dependency notices, this manual and file checksums. It is an unsigned test build, not an official release or installer. Running its checks does not require Rust, wasm-tools, an AI account or additional network permissions.
The source checkout alone does not contain the executables or compiled component. Developers can build the native host using the instructions below. The independent guest source, package instructions and test-distribution launchers live outside this repository; they cannot be built from this checkout. A package must contain a WebAssembly component, not just a core Wasm module.
Use only a separately supplied test distribution built for this host's craft:plugin@0.1.0 WIT.
Its manifests say "capabilities": ["commands@0.1"]. Kits from the earlier
solidcraft:plugin@1.0.0 preview are refused at inspection with a message saying what they were
built for; editing their manifest alone does not update compiled components, which must be rebuilt
against the new WIT. Check the distribution's BUILD-INFO.json
and file checksums before starting. This guide does not supply that distribution.
Run the Windows test distribution
-
Extract the ZIP. In File Explorer, open the extracted folder that contains
Run-Plugin-Test.cmdandStart-SolidCraft-Test.cmd. -
Open
plugins/example-box/plugin.jsonin a text editor. It needs the capabilitycommands@0.1, and its only command issolid.box; this example cannot read the existing design's mesh. Close the editor without saving changes. -
Return to that folder in File Explorer. Press Ctrl+L, type
cmd, and press Enter. A Command Prompt opens in the extracted folder. Type the following and press Enter:Run-Plugin-Test.cmd -
Wait for the command to finish. Keep the Command Prompt open and look for this exact line:
PASS: 600 mm3 / one Undo / Redo / saved model / snapshot.PASSmeans the automatic checks succeeded. They inspect the exact bundled component, run it, check the volume, undo once, redo, save and reopen the design, and render an image. You do not need to inspect an exit code or operate Undo/Redo yourself. At the start,Results:prints the full output-folder path. Record that path: itsplugin-box.scis the file to open in the next step.If you see
FAILED. See ...instead, stop here and include that line and the referencedtest.logwhen reporting the failure. To read the log, copy its folder path fromResults:into File Explorer's address bar, press Enter, right-clicktest.log, choose Open with, and select Notepad. -
In the extracted folder, double-click
Start-SolidCraft-Test.cmd. In the test GUI, press Ctrl+O, enter the full path toplugin-box.scin the results folder you recorded, and open it. The box should appear as an ordinary editable native feature.
The command-line test creates a fresh results folder each time. The GUI launcher uses an
isolated application data and recovery profile under results/gui-profile and places the
sample in its isolated plugin folder.
Neither launcher replaces your normal SolidCraft settings or installed plugins.
The test GUI retains its settings under results/gui-profile between launches.
The English starter profile is copied only on first launch, so changes to plugin ceilings persist.
Use one test GUI at a time with this profile.
Automatic AI-app registration and update checking are off in this isolated profile.
| Check | Expected result |
|---|---|
After plugin.run |
One body, volume 600 mm3 |
| After one Undo | No bodies |
| After Redo | One body, volume 600 mm3 |
| Reopen the saved file | One body, volume 600 mm3; no plugin execution |
| Snapshot | An image of the box in the same results folder |
Undo/Redo in the script use one CLI session. Opening its saved design in a new GUI session does not restore the CLI's session history. You can inspect and edit the native box in the GUI. A saved result can be evaluated without the component installed.
Inspect and run another package
plugin.inspect reads its static manifest and component bytes without executing
plugin code. Review the package, its command grants and limits before passing its
returned approval hash to plugin.run. The hash identifies exact content; it is
not a publisher signature or proof that a package is safe.
A CLI script is a JSON array. For example, save this as inspect.json, with a path
relative to the directory from which you run the CLI:
[{"command":"plugin.inspect","params":{"path":"plugins/example-box"}}]
Run bin\solidcraft-cli.exe run inspect.json --results --json-out inspection.json.
Copy the approval value from the first result into a separate run.json:
[{
"command":"plugin.run",
"params":{
"path":"plugins/example-box",
"approval":"COPY_THE_EXACT_APPROVAL_FROM_INSPECTION",
"entry":"make",
"params":{"length":20,"width":10,"height":3}
}
}]
Then run bin\solidcraft-cli.exe run run.json --results --save box.sc.
Changing even whitespace in the manifest requires inspecting and reviewing it again.
A desktop control client must send plugin.run as a standalone engine.execute
request; desktop batches and scripts refuse it before applying any earlier edits.
GUI behavior and current boundaries
Preferences > Plugins lists inert package metadata. Listing or enabling a package never executes it. The CLI/MCP and standalone desktop command are the execution entry points; this version has no dedicated plugin action panel or installer. Desktop plugin calls compute on a worker. Stop, a changed design/selection/context, or changed disabled-plugin preferences or resource ceilings prevent the pending result from being adopted. The CLI is a separate process and does not inherit the GUI's plugin settings.
Guest code receives no WASI filesystem, network or process imports. Its exact manifest grants are intersected with host policy. Commands run on a scratch design under an empty file scope; a refused call, trap, invalid result or exceeded budget discards the transaction, including errors caught by the guest. A successful edit is committed as one undo step.
The optional model.mesh grant returns an evaluated body's component-local vertices
and triangle indices, up to 8192 vertices and 16384 triangles, subject also to a
1 MiB result cap. It contains no reconstruction or conversion algorithm. The bundled
box sample does not request that grant.
The API identifier is the package declaration in the
shared WIT. craft-plugin defines the app-independent
manifest and PluginHost trait; craft-plugin-wasm supplies the native runtime. The SolidCraft
engine supplies its command schemas and transaction implementation. Defaults are 10 million fuel,
64 MiB guest memory and a 5000 ms execution deadline. Component compilation and native
CAD work are outside the guest execution budget. Browser execution, automatic
updates, custom panels, persistent guest storage and plugin-defined parametric
feature types are not implemented. See the runtime reference
for exact limits and supported commands.
A design may carry plugin data under plugins/<plugin-id>/ in its container. Opening and saving
such a design with no plugin installed keeps that data byte for byte; nothing in this version
writes it yet.
Entries declare read/edit effects, input schemas and optional named menu/ribbon slots. The app
can inspect these declarations with plugin.list and engine.describe. Selection and target
inputs are lists. A read entry cannot run an edit command. Named files permissions require
matching app-approved folders; the guest has no ambient file access. The optional clock
permission enables wall time; without it the clock returns a fixed epoch. Network permission
requests are refused, and components importing sockets, HTTP or fetch are refused before
instantiation, even when the import is unused.
Explicitly allow a larger plugin budget
The box sample uses the unchanged invocation defaults; it needs no settings changes. Use the following steps only for a separate package whose reviewed manifest requests a larger budget. The original host ceilings are 100,000,000,000 fuel and 128 MiB. Raising either ceiling does not change the sample's request or run any plugin.
For the desktop:
- Inspect the package with
plugin.inspectand readmanifest.runtime.limits,host_limits,within_host_limitsand anylimits_error. This step does not execute code. - Open Preferences > Plugins. Enter a positive integer in Max plugin fuel and in Max plugin memory (MiB), large enough for the specific request you reviewed. For example, a request of 200,000,000,000 fuel and 256 MiB fits ceilings of 200000000000 and 512.
- Choose Apply to accept both fields and keep the dialog open, or OK to accept and close it. Enter also accepts both fields. Invalid or incomplete values keep the dialog open. Cancel restores changes since the last Apply; Restore defaults resets this page.
- Inspect the package again to check
within_host_limits: true. Review its exact manifest, code and grants, then invokeplugin.runwith that inspection'sapproval. A host ceiling is not a substitute for approval of external code. The package still uses only its declared budget. If the manifest changes, inspect and approve the new content again.
For a standalone CLI:
-
Open Notepad, paste the JSON below, and save it as
plugin-limits.jsonin your extracted test package folder. In the Save dialog choose All files, so the name does not gain.txt; select UTF-8 encoding.{"max_fuel": 200000000000, "max_memory_mib": 512} -
Open a terminal in that folder and include
--plugin-limits plugin-limits.jsonin each command that should use these ceilings, for example:bin\solidcraft-cli.exe --plugin-limits plugin-limits.json run inspect-plugin.json --resultsHere
inspect-plugin.jsonis the inspection script for the package you are reviewing, following Inspect and run another package above. Keep its package path and reviewed approval hash in the run script; use the same startup flag for that run command. Missing settings keys use the original ceilings; omitting the flag uses both original ceilings. -
Headless MCP uses the same flag with
mcp --headless. For a connected desktop, change the desktop preferences instead; the CLI refuses to override a connected application's policy.
Fuel accepts positive integers through 18446744073709551615. Memory accepts 1..4096 MiB because this runtime uses Wasm32. These are guest limits; component compilation, native CAD work and other SolidCraft memory are separate. Choose values suitable for available machine resources. The execution deadline remains independently capped at 30 seconds.
Build from source
Build the native GUI/CLI following the build guide:
cargo build --locked --profile ci -p solidcraft -p solidcraft-cli
Plugin examples and converters, including the example-box guest and its distribution
scripts, live outside this repository, per the plugin RFC.
This checkout supplies the host WIT and synthetic
host-test fixtures; it does not supply the guest source or build instructions. Obtain an
independently distributed package and follow its own instructions. Use plugin.inspect
to obtain that package's actual hash before explicitly running it. Native binary output
paths depend on Cargo's target configuration.
Troubleshooting
- No result window: use Command Prompt as described in step 3. Double-clicking the test CMD may close its window before you can read it.
- Approval mismatch: restore the bundled files, or inspect and review modified package contents before using their new hash.
- Component compilation failed: verify this is a compatible WebAssembly component, not a core Wasm module, DLL or executable.
- Command is not granted: the manifest must list the exact command id; there are no wildcard grants. Every command in the MCP catalog can be listed.
- Command needs the
file(ordoc,app,plugin) permission: that command reaches past the open design. The manifest must request the group inruntime.permissions.command_groups, and the user must grant it. This test build has no grant UI, so such commands are refused. - "needs commands 0.2; update SolidCraft or the plugin": the plugin was built for a newer
capability than this build has. Use a matching build. "was built for solidcraft:plugin@1.0.0"
means a kit from the earlier preview: rebuild it against
craft:plugin@0.1.0. - Desktop asks for a standalone request: send one
engine.executecontainingplugin.run, outsideengine.batchandengine.script. - Check build identity: consult the distribution's
BUILD-INFO.json,SHA256SUMS.txtand the checksum beside its ZIP. Keep the generatedtest.logwith any failure report. Test distributions are unsigned and are not releases.