A design note for SolidCraft in the browser: tiered storage, a SharedWorker job broker, GPU dispatch and in-browser agent control through the same engine commands, with the generic pieces in craft-libs. Linked from the MCP and web getting-started docs.
6.7 KiB
Plugins (design)
Design companion to #1221: sandboxed
WebAssembly plugins with a WIT interface, the same run(command, args) JSON as
MCP and the control protocol. Browser hosting is in
web-runtime.md.
This document adds parametric timeline features: a generic host-owned feature shell that plugins fill with parameters, display metadata, and rebuild logic, without a second timeline model or a private geometry API.
Goals (unchanged from #1221)
- Sandboxed by default: manifest permissions, fuel/memory limits on desktop, broker enforcement on web.
- Any language that compiles to Wasm components; Rust first.
- One command surface: plugins change the model only through engine commands (undoable, journaled).
- Versioned WIT world (
solidcraft:[email protected]).
Two plugin shapes
| Shape | Timeline | Rebuild | Milestone |
|---|---|---|---|
| One-shot | Optional; often no persistent feature | N/A | 1 (first ship) |
| Parametric | One Plugin feature row per plugin instance |
Host calls plugin on param/timeline rebuild | 2 |
One-shot plugins call execute(params) once; they may emit ordinary native features (sketch,
extrude, …) inside that transaction. Parametric plugins still use run() inside rebuild; the
difference is that the document records a single feature whose params the user edits later.
Generic feature shell (host-owned)
Add one variant to the parametric document (name illustrative):
FeatureKind::Plugin {
plugin_id: String, // "gear_generator"
api_version: u32, // matches WIT world
params: Value, // JSON owned by the plugin schema
display: PluginDisplay, // timeline + browser presentation
provenance: PluginProvenance, // optional: manifest hash, build id
}
PluginDisplay (stored in the file, not recomputed from plugin code at open):
| Field | Role |
|---|---|
label |
Primary timeline / browser text |
icon_id |
Host icon registry name (same system as feature_icon) |
tooltip |
Hover text, inspect type string |
type_name |
Fusion-like feature type for power users |
The timeline renders any Plugin feature through one code path: read display, draw icon +
label, wire suppress/rename/delete/reorder like built-in features. No per-plugin egui in milestone 2.
Optional later: parameter_schema() in WIT returns { id, label, kind, min, max, … } so the
host renders sliders and dimension rows without custom UI per plugin.
WIT entry points (phase 2 additions)
Milestone 1 (#1221): manifest(), execute(params) -> result<string, string>.
Milestone 2 parametric:
| Export | Purpose |
|---|---|
evaluate(params, ctx) -> result<string, string> |
Rebuild: plugin runs run() commands; result is success JSON or error |
parameter_schema() -> string |
Optional JSON schema for generic parameter UI |
fallback_policy() -> string |
Declares missing-plugin behaviour (see below) |
ctx is host-provided read-only hints (configuration name, units, feature id); plugins must not
rely on it for correctness.
Inside evaluate, the plugin uses the same run(command, args) import as one-shot mode.
The host applies commands to the current eval context (or a scratch session merged by the host).
No second registry of geometry ops.
Rebuild flow
Timeline rebuild reaches FeatureKind::Plugin
-> load plugin (wasmtime desktop / broker web) if permitted + present
-> evaluate(stored params)
-> plugin calls run("sketch.line", …), run("solid.extrude", …), …
-> host merges geometry like any native feature
-> errors surface on the feature row like native eval errors
Param changes (parameter panel, parameters.change, config switch) re-run evaluate for
affected plugin features only when dependencies say so (same invalidation rules as native features).
Missing plugin and API drift
Manifest declares fallback_policy:
| Policy | Behaviour |
|---|---|
recorded_mesh |
Optional B-rep or mesh blob stored beside params; open works, edit disabled without plugin |
recorded_script |
Stored list of native commands; rebuild without plugin if every command is built-in |
suppress_with_warning |
Feature suppressed; user sees clear message |
error_on_rebuild |
Hard fail on eval (strict teams) |
Design files shared in CollabCraft should prefer a policy that opens safely (recorded_mesh or
recorded_script). CI for plugins (#1221) should include: uninstall plugin, reopen file, assert
document still opens and policy matches manifest.
Icons and metadata
Plugins declare allowed icon_id values in manifest(); the host rejects unknown ids. Icons
stay in the app registry so untrusted plugins cannot inject arbitrary raster assets into the tree
without a reviewed icon pack.
Rename by the user updates the feature user label; display.label remains the default from
creation unless the plugin updates it via a host callback (optional future).
Command naming: prefer plugin_id.function in run() or registered command aliases to avoid
collisions (#1221 discussion).
Group header variant (lighter-weight v2a)
Before full evaluate, a stepping stone: one-shot plugin creates a feature folder whose
header carries PluginDisplay metadata while children are ordinary native features. Rebuild does
not need the plugin until the user edits plugin-owned params on the header. Migrates cleanly to
full FeatureKind::Plugin when evaluate lands.
Relationship to craft-libs
Plugin document model and eval stay in SolidCraft (crates/doc, crates/engine). craft-libs
provides worker/broker hosting only (web-runtime.md). Neutral serde types
(VersionedParams, PluginDisplay) may move to a shared crate later if multiple apps need them;
CAD plugins remain SolidCraft-specific.
Phasing
- WIT + wasmtime host + one-shot
execute+ permissions tests (#1221 milestone 1). FeatureKind::Plugin+PluginDisplay+ timeline generic row +evaluate+ fallback policy.- Web broker runs same WIT; parametric features offline only if plugin module cached and policy allows.
- Parameter schema UI, cloud-signed plugin index, inspector extensions.
Tests (parametric)
- Create plugin feature, change param, rebuild, measure geometry (oracle or bbox).
- Undo/redo across plugin eval.
- Remove plugin binary, reopen design, assert fallback policy.
- Icon id from manifest appears on timeline (scenario or screenshot harness).