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.
| Shape | Target | Contract | Languages |
|---|---|---|---|
| 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.
# 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.
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
}
}| Field | Type | Description |
|---|---|---|
manifest_version | number | Schema version; currently 1. |
name | string | Guest name used in diagnostics and logs. |
version | string | Informational version string. |
description | string | Informational description. |
kind | string | info_provider, logo_animation, effect or config_provider. |
runtime | string | core or component; informational (detection reads the header). |
entry | string | Component export name; defaults to run. |
artifact | string | Relative path to a prebuilt artifact in the source directory. |
artifact_url | string | Absolute URL of a prebuilt artifact for remote installs that cannot build locally. |
build | string | Command executed in the source directory when no artifact is present. |
Capabilities are deny-by-default. Empty or missing fields deny the corresponding operation.
| Capability | Field | Semantics |
|---|---|---|
| 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.
| Field | Default | Meaning |
|---|---|---|
timeout_ms | 30000 | Wall-clock deadline for the whole invocation. The config timeout_secs value overrides it when set. |
memory_mb | 256 | Linear memory cap per memory. |
output_kb | 4096 | Cap for the JSON response written to stdout. |
host_call_kb | 4096 | Cap 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.
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.
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.
| Operation | Arguments | Result |
|---|---|---|
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 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.
The installers detect wasm sources automatically:
- A source manifest (
xfetch-plugin.json,plugin.json, ...) with a wasm runtime,artifact,artifact_urlorbuild. - A prebuilt artifact in
artifact,dist/<name>.wasm,<name>.wasm,xfetch-<label>-<name>.wasmor 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).
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.
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.
The runtime is behind the wasm feature, enabled by default:
cargo build --no-default-features # smaller binary, no wasm runtimeSuch a binary still detects wasm artifacts and reports a clear error instead of trying to execute them as native processes.
| Guest | Language | Shape | Repository |
|---|---|---|---|
wasm-crypto | Rust | Core module, allowlisted HTTP | xfetch-cli/plugins |
wasm-ip-geo | Python | Component, typed HTTP | xfetch-cli/plugins |
wasm-pacman | Go | Core module, allowlisted exec | xfetch-cli/plugins |
wasm-proc | C | Core module, read-only /proc | xfetch-cli/plugins |
wasm-matrix | Rust | Core module effect | xfetch-cli/effects |
wasm-python-pulse | Python | Component effect | xfetch-cli/effects |
wasm-night-mode | Rust | Core module extension | xfetch-cli/extensions |
wasm-updates-footer | Go | Core module extension, exec | xfetch-cli/extensions |
wasm-lang-labels | Python | Component extension, env | xfetch-cli/extensions |
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.
| Symptom | Likely 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. |