node script.mjs or js -e "..." need a JavaScript
engine. Mirage calls that engine a runtime, and node and js are
two names for the same command family. One runtime ships today:
What it is for
node/js runs small, self-contained scripts and pipe transforms:
compute, JSON munging, regex text processing, code generation. It is a
bare modern engine (ES2023 syntax, ES modules, JSON, Promise,
top-level await), not Node: there is no require, no npm, no
process, no fs, no fetch. Anything that needs those belongs in a
sandboxed deployment, not the workspace runtime.
python3:
-e <code>evaluates the argument.- A file (
node script.js) is read through the workspace before the run, so a mounted script executes. - stdin with no
-eor file runs the piped text as the program.
scriptArgs holds the arguments after the code or script, and
std.in.readAsString() reads piped stdin (the quickjs-ng std/os
globals are exposed). These match the Python quickjs runtime, so a
script behaves the same in both languages.
Modules
A.mjs file runs as an ES module automatically (top-level
import/export/await); .js and -e run as a classic script. Pass
-m/--module to force module mode for inline code:
Isolation
The engine runs under WebAssembly capability isolation: it sees only what the run passes it. Host files and the network are invisible. Workspace mounts are visible with no setup: mirage intercepts the sandbox’s filesystem calls and bridges them through the workspace dispatch, sostd.open('/data/f.txt', 'r') reads the mount live — RAM, Redis, S3, or
a virtual backend — and writes are immediately visible to every command,
with the same cache, write modes, and per-session mount narrowing as
cat. A read-only mount (or a session narrowed to read) fails the open,
so std.open returns null instead of writing.
This is capability isolation, not a resource sandbox: the sandboxed code
cannot reach your files or the network, but its CPU and memory are bounded
only by command_safeguards timeouts. For untrusted code or hard resource
limits, run behind a sandboxed deployment.
Each run gets its own epoch-interruption engine, so a command_safeguards
timeout traps the run and reclaims the thread instead of leaking it.
Setup
The runtime needs thequickjs extra plus a WASI build of quickjs-ng
(the qjs-wasi.wasm asset from a
quickjs-ng release):
qjs-wasi.wasm with the home
option on the runtime entry (or the MIRAGE_QUICKJS_HOME environment
variable):
qjs-wasi.wasm and caches the compilation as
qjs-wasi.cwasm next to it; runs after that boot in milliseconds.
Resource limits
node/js is a command like any other: the same command_safeguards
that guard cat or python3 guard it, enforced at the same central
point. A run that exceeds timeout_seconds answers with exit 124, and
max_bytes/max_lines cap its output. Firing the guard also cancels
the run and reclaims the engine at the deadline.