Schist loads third-party plugins as sandboxed WebAssembly modules. A
plugin can add a filter (a function over pixels) or a format (an
image decoder). It runs with no filesystem, network, clock or randomness —
the host exposes exactly one import, schist::log — and is bounded by an
execution-fuel budget, so a runaway plugin is unwound instead of freezing
the editor.
# Cargo.toml
[lib]
crate-type = ["cdylib"]
[dependencies]
schist-plugin-sdk = { git = "https://github.com/Infrawrench/schist" }
serde_json = "1"use schist_plugin_sdk::*;
schist_filter! {
id: "com.example.sepia",
name: "Sepia",
category: "Plugins",
params: [param("amount", "Amount", 0.0, 100.0, 100.0, "%")],
apply: |pixels: &mut [f32], _w: usize, _h: usize, params: &Params| {
let amount = params.get("amount") / 100.0;
for px in pixels.chunks_exact_mut(4) {
// px is [r, g, b, a] in 0..1, straight (un-premultiplied) alpha
}
}
}rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown
cp target/wasm32-unknown-unknown/release/*.wasm ~/.config/schist/plugins/Restart Schist; the filter appears in the Filter menu with a slider per declared parameter. File → Plugins… lists what loaded, shows why anything was refused, and can enable/disable or install plugins.
A plugin's name, category and parameter labels are shown as written:
the sandbox has no access to the app's string catalogs, so a third-party
plugin ships whatever language its author chose (English by convention).
A category that matches one of the Filter menu's English headings
("Blur", "Sharpen", …) files the filter under that heading in every
language; see i18n.md.
Two complete examples live in examples/plugins:
sepia-filter (a filter) and pgm-codec (a Netpbm PGM decoder).
schist_codec! {
id: "com.example.pgm",
name: "Netpbm PGM",
extensions: ["pgm"],
probe: |bytes: &[u8]| bytes.starts_with(b"P5"),
decode: |bytes: &[u8]| -> Option<(u32, u32, Vec<u8>)> {
// return (width, height, RGBA8) or None
None
}
}The SDK is a convenience, not a requirement — any language that emits
wasm32-unknown-unknown modules can implement the ABI directly.
| export | signature | purpose |
|---|---|---|
schist_abi_version |
() -> i32 |
must return 1 |
schist_manifest |
() -> i64 |
packed (ptr << 32) | len of manifest JSON |
schist_alloc |
(i32) -> i32 |
allocate guest memory |
schist_free |
(i32, i32) |
release it |
schist_filter_apply |
(ptr, w, h, params_ptr, params_len) |
filters: edit f32 RGBA in place |
schist_codec_probe |
(ptr, len) -> i32 |
formats: 1 if recognised |
schist_codec_decode |
(ptr, len) -> i64 |
formats: packed pointer to header + RGBA8 |
The module must export its memory. Manifest fields: id, name, kind
("filter" or "codec"), api_version, and optionally description,
category, params, extensions, capabilities.
api_version is checked on load; a mismatch refuses the plugin rather than
running it against an ABI it wasn't built for. Version 1 is frozen — future
additions will arrive as new optional exports, and anything incompatible
will bump the version.