bashkit

Wasm Coreutils (real uutils programs)#

Experimental. Behind the wasm-coreutils cargo feature, off by default.

Bashkit can run real uutils/coreutils programs instead of its own reimplementations. The programs are compiled to WebAssembly (wasm32-wasip1), precompiled at build time and shipped inside the binary. Each call runs in a fresh, isolated instance that can only reach the Bashkit virtual filesystem and its own stdio, so many tenants can share one process.

See also:

Quick start#

[dependencies]
bashkit = { version = "0.18.2", features = ["wasm-coreutils"] }
use bashkit::Bash;

let mut bash = Bash::builder().wasm_coreutils().build();

// `pathchk` has no native builtin: the uutils program fills the gap.
let r = bash.exec("pathchk -p report_2026.txt && echo ok").await?;
assert_eq!(r.stdout, "ok\n");

// Any uutils program, also ones Bashkit implements natively, by name.
let r = bash.exec("printf '1K\\n3M\\n2\\n' | coreutils sort -h").await?;
assert_eq!(r.stdout, "2\n1K\n3M\n");

What gets registered#

  • coreutils <utility> [args...]: runs any of the 70 embedded utilities; coreutils --list prints them.
  • Missing utilities by name: csplit, dir, dircolors, pathchk, ptx, shred, vdir (the ones with no native builtin).
  • Native builtins (cat, sort, ls, …) stay native: they are much faster and stream. To route every embedded utility to its uutils program instead (GNU-compatible options and output), use BashBuilder::wasm_coreutils_replace_native(limits).

The module loads on first use (one-time engine and module setup). Call bashkit::WasmCoreutil::warm_up() at process start to move that cost out of the first request.

Limits#

use bashkit::{Bash, WasmCoreutilsLimits};
use std::time::Duration;

let bash = Bash::builder()
    .wasm_coreutils_with_limits(
        WasmCoreutilsLimits::default()
            .max_duration(Duration::from_secs(2)) // wall clock per call (default 10 s)
            .max_memory(32 * 1024 * 1024)         // guest memory (default 64 MiB, max 256 MiB)
            .max_output(1024 * 1024)              // stdout + stderr bytes (default 16 MiB)
            .max_concurrent(2),                   // guests this Bash runs at once (default 4)
    )
    .build();
  • Time: a call past its deadline stops with exit code 124 and <util>: execution timed out .... A tighter Bashkit ExecutionLimits timeout or cancellation wins.
  • Memory: allocation past the cap fails inside the program.
  • Output: bytes past the cap are dropped and stderr ends with <util>: output truncated at N bytes.
  • Files: Bashkit filesystem limits apply (EFBIG, ENOSPC and ENAMETOOLONG inside the program).
  • Work budget: guest instructions count against ExecutionLimits work units; a call stops mid-run when the budget runs out.
  • Concurrency: one Bash runs at most max_concurrent guests; more wait for a turn within their own deadline (exit 124 if it passes).

Isolation#

The programs never run native code in your process: they execute on Wasmtime’s Pulley interpreter, and the only things they reach are the virtual filesystem, captured stdin/stdout/stderr, clocks and a random source. Only exported shell variables are passed in; PWD becomes the working directory. A crash ends only that call (<util>: fatal error: ...).

  • builtin_filter applies to the guest utilities too, including through coreutils <utility>. A builtin you register yourself keeps its name. Script analysis reports coreutils as a command wrapper (ScriptAnalysis::command_wrappers()), like env or xargs.
  • Clock: programs see the Bash virtual clock (fixed_epoch, epoch_offset), also for file times they set; under the Hardened profile it is rounded to 100 ms.

See the threat model (TM-WCU) for the full list.

Limitations#

  • 70 utilities: uutils’ WebAssembly-buildable set. stat, du, df, id, install, chown, timeout, tac and dd are not included (the native dd builtin stays).
  • ls -l shows placeholder owners and permission bits (WASI has no file modes).
  • Output is captured, not streamed.
  • Programs run interpreted: much slower than native builtins on large inputs (see the measurements in the design notes).