Skip to content

WASM checks

Fork-per-check works, but it isn’t free, and a check plugin is still arbitrary code running with whatever the process’s ambient permissions happen to be. WASM checks give you a third option: a single portable module, sandboxed by default, that runs without spawning a process at all.

jaque runs WASM modules on wazero, a pure-Go runtime — no CGo, consistent with the single-binary invariant. By default a module gets:

  • No filesystem, no network. #WASMCheck — the CUE shape a wasm check is written against — accepts only module and args; there is no config field for a directory mount or a network grant. Those grants exist only as Go-API options (WithDirMount, WithReadOnlyDirMount) on the runner a Go embedder constructs directly, not as anything a CUE config file can reach. So today, every check written in config gets no filesystem and no network, full stop.
  • A memory cap. WASM’s own linear-memory pages are bounded so a runaway allocation can’t OOM the host process.
  • A hard deadline, enforced via context — a module that hangs is killed like anything else that exceeds timeout.
services: custom: check: {
type: "wasm"
module: "checks/disk-usage.wasm"
args: ["/", "80", "95"]
}

module is a path to a .wasm file, compiled once at startup.

A check module is a WASI (wasip1) command module — it exports _start and is instantiated fresh per run — speaking the same protocol as legacy exec plugins: exit code 0-3 maps to OK/WARNING/CRITICAL/UNKNOWN (state.NewStatus), and stdout is check output text with an optional | splitting off perfdata, same convention as everywhere else. Any language that compiles to wasip1 writes a jaque plugin with zero jaque-specific code.

Guest linear memory is capped at 128 MiB (2048 wasm pages of 64 KiB each) by default; growth beyond the cap fails inside the guest, never outside it. A Go embedder can raise or lower this per runner, but nothing in #WASMCheck exposes the cap to a CUE config.

WASM checks aren’t a replacement for the Nagios exec protocol — see Legacy exec plugins, which stays supported forever. They’re the option when you’re writing something new and want it distributable as one file, sandboxed from the host by construction rather than by convention, and runnable without a subprocess fork.