--- id: SMH-SPEC-WASM0001 type: spec title: "Wasm Plugin Runtime" research: [SMH-RESEARCH-WASM0001] --- # Wasm Plugin Runtime ## Intent SPEC0001 plugin contracts implemented with wasm components on an embedded wasmtime host. ## Engine - Engine enables NaN canonicalization. - Engine disables relaxed SIMD. - Fuel metering is on; fuel exhaustion is an explicit plugin error and leaves the runtime usable. - No ambient WASI: clocks, randomness, filesystem, network, and environment never reach the guest uncontrolled from the host OS. Where a guest needs such semantics, Smith provides controlled substitutes through granted interfaces — deterministic, recorded, replayable (a smith clock is injected and replayed; a smith RNG is seeded and recorded). - Time enters the guest only as explicit host-provided arguments. - WASI interfaces that guest-language std runtimes import for standard functionality (environment, poll, and peers) are satisfied on demand by Smith implementations with smith-controlled semantics: sealed by default, manifest-granted values, recorded and replayable. Standard guest code stays painless — no smith-specific surface is required for ordinary std usage. - Components are validated before instantiation: typecheck against the pinned smith world, memories and tables declared with max equal to min, runtime limiter rejects growth. ## Sealed WASI The host links no WASI implementation crate; every `wasi:*` import a guest std runtime needs is a synchronous Smith implementation in the host world. The host world is the smith world plus exactly the sealed interfaces below; an import outside that set fails instantiation naming the interface. Guest imports at any `0.2.x` resolve against the host's pinned `0.2` definitions by semver. | interface | sealed semantics | |---|---| | `wasi:cli/environment` | environment, arguments, and initial cwd are empty | | `wasi:cli/stdin` | closed stream | | `wasi:cli/stdout`, `wasi:cli/stderr` | writes are accepted in bounded chunks and discarded; nothing reaches the host process streams | | `wasi:cli/terminal-*` | no terminal | | `wasi:cli/exit` | traps with a diagnostic; a guest cannot end the host | | `wasi:clocks/monotonic-clock` | `now` returns the host-injected value for the current call; resolution 1; the value never advances inside a call | | `wasi:io/poll` | every pollable is ready; `poll` returns all indices | | `wasi:io/streams`, `wasi:io/error` | only the sealed stdio streams exist; splice and read report closed | Wall clock, randomness, filesystem, and sockets are not linked; a guest importing them fails instantiation until a granted Smith substitute exists for that interface. Sealed interactions carry no host state and need no recording; recorded substitutes (clock injection, seeded RNG) enter the trace as host inputs of the call. WIT sources for sealed interfaces are copied verbatim from the wasmtime WASI distribution under their license, minus worlds that reference unlinked packages; the file header names the origin and the modification. ## Capabilities - The manifest declares requested capabilities. - Grants resolve at trust time for project plugins and install time for user plugins. - Host imports are capability-scoped: fs-scope, net-scope, secret-proxy, log. - An import without a matching grant fails instantiation before guest execution. ## Calls - All host and guest interactions are recorded into the session trace. - Layout uses one batched frame call per render pass. - Tool invocation uses one call per execution with recorded input and output. - Keymap predicates receive fixed-size input descriptors. - Provider plugins pull stream chunks by explicit request. ## Lifecycle - Load, validate, instantiate, and register run as one transaction. - Failure at any step leaves no registrations or subscriptions. - Reload swaps to a fully instantiated replacement or keeps the previous state. - Fuel and memory ceilings per plugin are explicit configuration. ## Identity and installation - A plugin is a component artifact plus a KDL manifest. - The manifest declares id, version, world version, requested capabilities, and optional source provenance. - The artifact content hash is recorded at install; session traces record hashes of loaded plugins. - Smith never recompiles at load time; rebuilding happens only through explicit plugin commands with a toolchain present. - Unsupported world versions fail load with a diagnostic naming the supported range and the rebuild path. - Registration is explicit: init and build never mutate the plugin registry. - Dev links register a source directory by reference; the artifact is loaded from the linked path and its hash recorded per load. - Dev links are explicit overrides and take precedence over installed plugins. ## Guests - Rust is the primary guest language. - TypeScript via jco is a maintained compatibility target: continuous integration builds and loads a TypeScript example plugin against the current world and applies the same lints. ## Diagnostics - Every lifecycle stage produces a diagnostic naming stage, cause, and fix. - Instantiation failures name the missing grant or the world version mismatch. - Runtime failures distinguish typed guest errors, traps with backtraces, and resource exhaustion with ceilings. - `smith plugin check` explains every rejected lint with a concrete fix. ## Concurrency - One plugin instance is single-threaded; the host never calls one instance concurrently. - Parallelism is host-orchestrated across plugins and instances. - Intra-guest threads and shared memories are not supported. - Version 1 executes plugin calls sequentially per instance. - Concurrent plugin activity records into one ordered session trace with explicit ordering edges. ## Versioning - The smith plugin world is versioned; additive-only changes preserve compatibility. - New host requirements on guests are optional; missing exports mean the feature is not provided. - Breaking changes create a new world version; the host embeds the previous version during a deprecation window. - The manifest declares the world version; the host adapts data to the declared version. - Shared data types include a catch-all unknown case for forward tolerance. ## Tooling - `smith plugin init`: cargo project scaffold with a pinned WIT copy. - `smith plugin build`: cargo build plus component wrapping; emits artifact and manifest. - `smith plugin check`: artifact validation, world match, capability-versus-grant lint, determinism lint. - `smith plugin dev`: watch, rebuild, atomic hot swap into a running runtime. - `smith plugin test`: same host with mocked capabilities and golden determinism fixtures. - Plugin tooling dispatches on language adapters; no command assumes the Rust toolchain. ## Acceptance criteria - Same artifact and same recorded host inputs produce identical outputs across repeated runs. - An ungranted capability import fails instantiation with diagnostics. - Fuel exhaustion and memory ceilings surface as explicit plugin errors. - Reload is atomic; failed reload preserves previous plugin state. - `smith plugin check` rejects artifacts with ambient imports or growable memories. - A guest built from an unmodified `wasm32-wasip2` std target instantiates against the sealed host world without an async runtime in the host dependency graph.