Skip to content

Latest commit

 

History

History
442 lines (380 loc) · 16.9 KB

File metadata and controls

442 lines (380 loc) · 16.9 KB

WebAssembly Guests

Plugins, effects and extensions can be WebAssembly artifacts instead of native executables. The core detects them by their binary header, runs them inside a sandboxed wasmtime runtime and keeps the same JSON protocol version 1, so the rest of the system (config, timeouts, list/remove commands) is unchanged.

This page is the authoritative reference for the wasm runtime. Authoring guides and SDK notes live in xfetch-cli/api.

Guest Shapes

ShapeTargetContractLanguages
Core module wasm32-wasip1 JSON request on stdin, JSON response on stdout, host calls through the xfetch.host_call import Rust, C, Zig, Go, AssemblyScript, ...
Component Component model (wasm32) Export run from the WIT world, typed host imports Python (componentize-py), JavaScript (componentize-js), Rust (wit-bindgen), ...

Detection is content-based: an 8-byte header check distinguishes core modules (version 1) from components (version 13, layer 1). The file extension does not matter.

Quick Start

# Inspect an artifact without running it
xfetch wasm inspect ./xfetch-plugin-wasm-crypto.wasm
xfetch wasm inspect ./plugin.wasm --json

# Run it with a raw JSON request (useful for authors and scripts)
xfetch wasm run ./plugin.wasm --request '{"version":1,"kind":"info_provider"}'
xfetch wasm run ./plugin.wasm --request-file request.json --kind plugin --timeout 30

# Print the WIT contract used by component guests
xfetch wasm wit

# Install from a local checkout, a remote repository or a prebuilt URL
xfetch plugin install ./plugins/wasm-pacman
xfetch plugin install wasm-crypto
xfetch plugin install https://example.com/releases/plugin.wasm
xfetch effects install ./effects/wasm-matrix
xfetch extension install ./extensions/wasm-night-mode

wasm inspect prints a human-readable report or the same data as JSON with --json. wasm run accepts the request inline (--request) or from a file (--request-file), an optional --kind (plugin, effect or extension; default plugin) and a --timeout in seconds that overrides the manifest limit.

Manifest

Capabilities and limits come from a JSON manifest resolved in this order: a sidecar next to the artifact (xfetch-plugin-name.wasm → xfetch-plugin-name.json), a custom section embedded in the binary under the name xfetch:manifest, or conservative defaults. No manifest still works for pure stdin/stdout guests, but grants nothing.

Source repositories use the same schema in a file named xfetch-plugin.json, xfetch-effect.json or xfetch-extension.json; the installer copies it next to the artifact.

{
  "manifest_version": 1,
  "name": "wasm-crypto",
  "version": "0.2.0",
  "description": "Example info provider",
  "kind": "info_provider",
  "runtime": "core",
  "build": "cargo build --release --target wasm32-wasip1",
  "artifact": "../../target/wasm32-wasip1/release/xfetch-plugin-wasm-crypto.wasm",
  "artifact_url": "https://github.com/user/repo/releases/latest/download/plugin.wasm",
  "capabilities": {
    "http": { "allow": ["https://api.example.com/*"] },
    "exec": { "allow": ["git"], "env": ["PATH"] },
    "fs": [
      "~/.config/xfetch",
      { "host": "~/.cache/xfetch", "guest": "/data", "mode": "rw" }
    ],
    "env": ["HOME", "USER"],
    "args": false
  },
  "limits": {
    "timeout_ms": 30000,
    "memory_mb": 256,
    "output_kb": 4096,
    "host_call_kb": 4096
  }
}

Manifest Fields

FieldTypeDescription
manifest_versionnumberSchema version; currently 1.
namestringGuest name used in diagnostics and logs.
versionstringInformational version string.
descriptionstringInformational description.
kindstringinfo_provider, logo_animation, effect or config_provider.
runtimestringcore or component; informational (detection reads the header).
entrystringComponent export name; defaults to run.
artifactstringRelative path to a prebuilt artifact in the source directory.
artifact_urlstringAbsolute URL of a prebuilt artifact for remote installs that cannot build locally.
buildstringCommand executed in the source directory when no artifact is present.

Capabilities

Capabilities are deny-by-default. Empty or missing fields deny the corresponding operation.

CapabilityFieldSemantics
HTTP http.allow Glob patterns matched against the full URL (* and ?). Redirects are followed manually and every hop is re-checked.
Processes exec.allow Program file-name patterns. argv is passed verbatim; no shell is involved. exec.env allowlists which variables the guest may forward.
Filesystem fs WASI preopens. Strings mount read-only at the expanded host path; objects choose host, guest and mode (ro/rw).
Environment env Variable names exposed to the guest. ["*"] forwards everything (discouraged).
Arguments args When true, the guest receives its name as argv[0]; when false (default), argv is empty.

log and version host operations are always available and need no manifest entry.

Limits

FieldDefaultMeaning
timeout_ms30000Wall-clock deadline for the whole invocation. The config timeout_secs value overrides it when set.
memory_mb256Linear memory cap per memory.
output_kb4096Cap for the JSON response written to stdout.
host_call_kb4096Cap for a single host-call response (HTTP body, exec stdout/stderr).

Timeouts use wasmtime epochs: a background ticker advances the engine epoch every 10 ms and the guest traps when its deadline is reached. A guest that runs out of time is reported exactly like a native plugin timeout.

Guest Logs

Guests can write diagnostics through the log host operation (host.log in components, xfetch-guest-api in Rust core modules). The host prints them to stderr with the guest name and level:

[wasm-crypto] [info] fetching prices

By default only warn and error lines are shown, so informational guest chatter stays out of a normal fetch. Set XFETCH_WASM_LOG_LEVEL to off, error, warn, info or debug to change the threshold (for example XFETCH_WASM_LOG_LEVEL=debug xfetch when developing a guest).

Raw writes to the guest's stderr (including panics) are always forwarded; only the structured host log is filtered.

Host Calls (Core Modules)

Core modules import a single function from the xfetch module:

(func "host_call" (param i32 i32 i32 i32) (result i64))

The guest writes the operation name and a JSON argument object into its memory and exports xfetch_alloc(size) -> ptr so the host can place the response. The return value packs the response pointer and length: low 32 bits = pointer, high 32 bits = length. A return value of 0 means the host could not allocate the response.

Every response is a JSON object:

{ "ok": true,  "value": { } }
{ "ok": false, "error": { "kind": "denied", "message": "..." } }

Error kinds are denied, failed, timeout, too_large and unsupported.

OperationArgumentsResult
http { method, url, headers, body_base64?, timeout_ms? } { status, headers: [[name, value], ...], body_base64 }
exec { program, args, stdin_base64?, env?, timeout_ms? } { code, stdout_base64, stderr_base64 }
log { level, message } { }
version { } { runtime, protocol, xfetch, guest_kind }

Rust authors should use the xfetch-guest-api crate, which implements the ABI, the allocator exports and typed helpers (http_request, exec, log, protocol_version). The core does not depend on this crate: the host bridge lives in the runtime, and only the guest adds it to its own Cargo.toml. Declaring it as "0.2" lets Cargo resolve the newest compatible 0.2.x release.

Components

Components use typed imports instead of the JSON bridge. The contract lives in wit/xfetch-runtime.wit (also printable with xfetch wasm wit) and defines three worlds — plugin, effect and extension — that all export:

run: func(request: string) -> result<string, string>

The imported xfetch:runtime/host interface exposes fetch, exec, log and protocol-version. WASI preview 2 is linked as well, so componentized Python and JavaScript runtimes get clocks, randomness and preopened filesystems.

Installation

The installers detect wasm sources automatically:

  • A source manifest (xfetch-plugin.json, plugin.json, ...) with a wasm runtime, artifact, artifact_url or build.
  • A prebuilt artifact in artifact, dist/<name>.wasm, <name>.wasm, xfetch-<label>-<name>.wasm or a cargo wasm target directory.

When only a build command exists it runs first, then the artifact is located and installed as xfetch-<label>-<name>.wasm plus its sidecar manifest. Native crates (Cargo.toml without wasm hints) keep the existing cargo build flow.

Three install paths require no toolchain at all: a direct URL (xfetch plugin install https://.../plugin.wasm), a single local file (xfetch plugin install ./plugin.wasm) and a repository manifest with artifact_url (prebuilt GitHub releases).

Security Model

Wasm guests start with no ambient authority: no filesystem, network, environment or process access. Every host operation is checked against the manifest, and limits bound time, memory and output. Native plugins are unchanged and keep their full process privileges; choosing wasm is choosing the sandbox.

Redirects are re-validated per hop, exec never invokes a shell and clears the environment except for allowlisted names, and filesystem access is limited to explicit preopens.

Compilation Cache

Compiled native code is cached under the platform cache directory (~/.cache/xfetch/wasmtime on Linux), keyed by the wasmtime version and the module hash. The first run of an artifact pays compilation; later runs reuse the cache. Set WASMTIME_CACHE_DISABLE=1 to bypass it when debugging.

Building xfetch Without Wasm

The runtime is behind the wasm feature, enabled by default:

cargo build --no-default-features   # smaller binary, no wasm runtime

Such a binary still detects wasm artifacts and reports a clear error instead of trying to execute them as native processes.

Examples

GuestLanguageShapeRepository
wasm-cryptoRustCore module, allowlisted HTTPxfetch-cli/plugins
wasm-ip-geoPythonComponent, typed HTTPxfetch-cli/plugins
wasm-pacmanGoCore module, allowlisted execxfetch-cli/plugins
wasm-procCCore module, read-only /procxfetch-cli/plugins
wasm-matrixRustCore module effectxfetch-cli/effects
wasm-python-pulsePythonComponent effectxfetch-cli/effects
wasm-night-modeRustCore module extensionxfetch-cli/extensions
wasm-updates-footerGoCore module extension, execxfetch-cli/extensions
wasm-lang-labelsPythonComponent extension, envxfetch-cli/extensions

Multi-Repository Development

The ecosystem repositories consume the API crates from crates.io. For local development alongside this checkout they include a [patch.crates-io] section pointing at ../api, and wasm-specific crates such as xfetch-guest-api are referenced by path. Remove those sections (or publish the crates) before building a standalone clone.

Troubleshooting

SymptomLikely cause
does not export _start A core module built as a library. Build it as a WASI command (wasm32-wasip1 binary).
host returned no buffer The guest does not export xfetch_alloc. Use xfetch-guest-api or implement the export.
error: denied The manifest lacks the capability. Add an allowlist entry and reinstall.
exceeded its timeout The guest ran past the deadline. Raise timeout_ms or timeout_secs, or optimize the guest.
memory limit traps Raise memory_mb in the manifest (Python components need headroom).
Component fails with Invalid plugin world The component does not export run from the expected world. Rebuild with the correct -w flag.