CLI reference
The complete command-line surface of the cruft binary: every command and execution option, the capability and sandboxing flags, exit codes, all CRUFT_* environment variables, the Compartment constructor, and the cruft-caps.json grant file.
The cruft binary, complete. Concepts and rationale live in the concepts and subsystems tiers; this page is the surface itself. Where behavior is stated concretely, it describes the current cruft binary.
Commands
| Command | Effect |
|---|---|
cruft <file> | Run a file (shorthand for cruft run <file>) |
cruft run <file> | Run a JS/TS file or package script; --backend=auto/node/cruft --explain selects the engine (front door) |
cruft wrap -- <cmd> | Supervise a Node-family command (front door) |
cruft wrap install / cruft wrap status / cruft unwrap | Manage the persistent node/npm/npx shim layer (front door) |
cruft doctor | Explain execution modes and wrapper state (--json for automation) |
cruft policy | Show the policy-profile control semantics |
cruft trust install [--enforce] [-- npm install…] | npm install risk preflight (front door) |
cruft compat <target> / cruft promote | Compatibility-evidence cockpit (front door) |
cruft agent run/doctor/replay | Run code in an audited, budgeted compartment (full page) |
cruft install | Install dependencies, write cruft-lock.json |
cruft test262 run <path> / cruft test262 sweep … | Conformance harness entries (maintainer surface) |
cruft help, cruft --help, -h | Help |
cruft --version, -v, -V | Version |
Runnable file extensions now include .tsx and .fts (CruftScript) alongside the JavaScript and TypeScript set:
Unlike node, run is a real verb (node run x tries to execute a file named run). File-type dispatch is by extension: .mjs/.mts are ES modules, .cjs/.cts CommonJS, .js/.ts follow the nearest package.json "type"; .ts/.mts/.cts pass through type erasure first. .tsx is dispatched but not yet type-stripped and has no JSX support — TypeScript type annotations and JSX elements in a .tsx file currently fail to parse.
Execution options
| Option | Effect |
|---|---|
-e, --eval <src> | Run inline source as a script (sloppy mode, like node; not type-stripped) |
-p, --print <src> | Run inline source, print the result (console.log formatting) |
-c, --check <file> | Parse-only syntax check; exit 0 if valid, 65 on syntax error |
-i, --interactive | Force the REPL, even on piped stdin |
- | Read the script from stdin |
Option parsing stops at the first entry point; flags after it go to your program's process.argv, exactly like node.
Capability options
The sandboxing surface (full treatment: Compartments and capabilities):
| Option | Effect |
|---|---|
--audit | Run normally; log every I/O capability use (caller, capability, operation, timestamp) |
--audit-log <path> | Write the audit log to <path> instead of stderr |
--sealed | Deny all I/O not granted via cruft-caps.json; ungranted use throws |
--sealed-deps | Your code runs normally; node_modules code is denied I/O |
--allow-net-loopback | Re-grant loopback listen authority under sealed modes |
Inline -e/-p source runs under the same capability mode as a file; there is no separate eval policy.
Unrecognized flags
The flag set is deliberately small, and anything else in flag position is rejected, not ignored (cruft: bad option: --x, exit 64). This includes node flags Cruft has not implemented: they fail loudly rather than silently running without the requested behavior. (--require/-r preload is implemented — see CRUFT_PRELOAD in the environment table.)
Exit codes
process.exit(N) propagates faithfully. For Cruft's own errors the current convention is sysexits.h, which differs from node, branch on these with care:
| Situation | Cruft | node |
|---|---|---|
| Clean run | 0 | 0 |
process.exit(N) | N | N |
| Uncaught exception | 1 | 1 |
| Syntax error in entry | 1 (run) / 65 (--check) | 1 |
| Entry file not found | 66 | 1 |
| Unrecognized flag | 64 | 9 |
| Install resolution failure (peer conflict, unsatisfiable range) | 70 | — |
The convention difference is tracked and may converge toward node-faithful behavior; treat non-zero-ness, not specific codes, as the portable signal.
cruft install
No positional arguments, no flags, configuration is entirely by environment variable (below). Reads dependencies from ./package.json; writes node_modules and cruft-lock.json. Output format:
cruft install: project=<dir> registry=<url> mode=<mode>
+ name@version # installed this run
= name@version # already satisfied, skipped
cruft install: <n> installed, <m> skipped
There is no add/remove/init/update yet; edit package.json and re-run. Root devDependencies are not yet read, and lifecycle scripts are not run (both tracked, see the package manager documented limitations).
Environment variables
Runtime / GC
| Variable | Default | Effect |
|---|---|---|
CRUFT_GC_HEADROOM | 4.0 | Heap growth multiplier between collections; floor 1.1. Lower = tighter RSS, higher = more throughput. Applies to every heap including workers. |
CRUFT_GC_TARGET_MB | unset | Soft memory target (GOMEMLIMIT-style): effective headroom tightens as process RSS rises from 50% to 100% of target. Bounds reclaimable memory, not the live set. |
CRUFT_GC_STRESS | unset | Diagnostic: force collection at a bounded allocation cadence. Not for production. |
Package manager
| Variable | Default | Effect |
|---|---|---|
CRUFT_REGISTRY | https://registry.npmjs.org | Registry for resolution and downloads |
CRUFT_STORE | ~/.cruft/store | Content-addressed store root |
CRUFT_INSTALL_MODE | link | link (hard-links from store, pnpm-style) or isolated/npm/copy (self-contained copies) |
CRUFT_PM_LEGACY_PEER_DEPS | off | On peer conflict: pick most-satisfying and warn (npm --legacy-peer-deps) |
CRUFT_PM_NO_PEERS | off | Ignore peer dependencies entirely (escape hatch) |
CRUFT_PM_CONCURRENCY | 16 | Max concurrent registry connections |
Compartment API (runtime globals)
Not a CLI flag but part of the operable surface, the constructor options in one place:
new Compartment(opts) option | Effect |
|---|---|
globals: {…} | The capability grant: each entry becomes a global inside the fresh realm; nothing else exists there |
timeout_ms: N | Wall-clock budget per evaluate; expiry terminates beneath the language |
worker: true | Build the realm on a worker thread; communicate via send |
onMessageSource: "src" | Message-handler source text, compiled on the worker; handler receives { data } events |
Methods: evaluate(src) (compile + run in the realm, returns completion value; state persists across calls), send(payload) (worker-hosted only: structured-clone delivery; SharedArrayBuffer crosses by reference; functions rejected).
Capability grant file
Sealed modes read grants from a cruft-caps.json next to (or above) the entry file, allow-lists per family:
{ "fs": ["./"], "net": ["127.0.0.1:8080"], "env": ["HOME"], "exec": ["git"], "stdio": { "stdout": true, "stderr": true } }
The stdio family authorizes a sealed program to write to standard output and standard error, so a directly-run sealed script that prints can be granted the streams it needs. Note stdio takes object form ({ "stdout": true, "stderr": true }), not an array — the fs/net/env/exec families are arrays. fs entries match the as-written operation path (use "./" for relative access).