oam --help and oam <command> --help are authoritative. This page is the
overview, plus the environment variables, which --help does not list.
| Command | What it does |
|---|---|
oam run <file> |
Run a JS/TS file. TypeScript is type-checked concurrently — execution never waits on the checker. |
oam test |
Run *.test.* / *.spec.* / *_test.*, each file in a fresh isolate. Import oam:test. Does not block on types. |
oam repl |
Interactive typed REPL. Also the default with no subcommand. |
oam check |
Type-check a file or project with tsgo (TypeScript 7 native). |
oam daemon |
Inspect or stop the per-project type-check daemon. |
oam mcp |
Serve oam's introspection to coding agents over MCP (stdio). E.g. claude mcp add oam -- oam mcp. |
oam serve <file> |
oam run with PORT/HOST set from --port/--host. |
oam install |
Install from the lockfile (the npm ci equivalent). |
oam install --precompile |
oam install, plus transpile the TypeScript shipped inside installed packages into node_modules/.oam/precompile/ so the first run pays no transpile cost. Walks every package in the lockfile — a warm (already-installed) tree is populated or refreshed too, and entries invalidate automatically when a package's source, oam's transpiler, or the effective JSX settings change. |
oam trust |
Manage the trust list for package lifecycle scripts. |
oam compile <file> |
Embed a pre-bundled JS file into a standalone executable. Bundle it yourself first (esbuild/rollup); this does not bundle. |
oam self-update |
Update in place. Verifies the release's signed RELEASE-MANIFEST against the release keys built into oam (before v0.18.0: a pinned SHA256SUMS digest), then the binary against it. Refuses a downgrade unless --version names the tag. |
oam cache info / oam cache clean |
Inspect or delete the V8 bytecode cache (see OAM_CODE_CACHE below). |
Global: --json emits machine-readable ODIF JSONL on stderr instead of
pretty-printed errors — the form agents should consume. The envelope is
documented at https://oamjs.org/docs/odif, and every code it can carry at
https://oamjs.org/docs/errors.
Every check -- oam check, and the concurrent one behind oam run and
oam test -- also sees oam's own declarations for the oam: modules
(oam:mcp, oam:test, oam:ai, oam:permissions) and the oam global.
Nothing to install and no tsconfig.json edit: for a project they arrive
through a generated config that extends yours, so your compilerOptions,
include, paths, types and references still decide what is checked
and how. That config is written under node_modules/.oam/ts-decls/ at
your project's nearest node_modules (the directory oam's --precompile
output already lives in, gitignored there), because TypeScript resolves a
types entry from the root config's directory and that has to be where
your own @types are. It goes under oam's own cache dir only when there
is no node_modules at or above the project, or that directory cannot be
written; a project that names a package in types is then checked against
its own tsconfig alone, with TS2307 on oam: imports, and OAM_DEBUG=1
says so. ${configDir} in any config of the extends chain is honoured:
the generated config restates each key it affects with your project's own
directory substituted, so the program checked is the one
tsgo -p tsconfig.json builds; OAM_DEBUG=1 lists the keys restated.
Node's own globals are a separate question -- process, Buffer and
friends still want @types/node in the project, because those are Node's
types to publish, not oam's. A project that carries its own ambient
declare const oam (hand-written, or copied from oam's demo before 0.14)
should drop it -- two declarations of one global are a redeclaration error.
oam accepts the Node flags it implements, so existing invocations keep working. Notable ones:
| Flag | Notes |
|---|---|
--permission |
Denies everything, then grants back with the --allow-* flags below. |
--allow-fs-read=<paths> / --allow-fs-write=<paths> |
* for everything, otherwise a comma-separated allow-list. Both take the list with =; neither has a bare form. |
--allow-net[=<hosts>] / --allow-env[=<names>] |
Bare grants everything; the = form takes a comma-separated allow-list. A net entry without a port (example.com) grants the host on every port; one with a port (example.com:443) grants that port alone, for raw sockets and HTTP clients alike (https://example.com/ is port 443). |
--allow-child-process |
Spawning and process.execve, which replaces the image. |
--allow-worker |
Starting a worker_threads Worker or an oam.fork() isolate. Does not imply --allow-child-process — a child isolate inherits the parent's permissions, so it cannot spawn unless the parent could. |
--allow-addons |
Loading native addons. Enforced at require() of a .node file: without the grant the load throws ERR_ACCESS_DENIED carrying permission: 'Addon', and OAM_ENABLE_NATIVE_ADDONS=1 does not override it -- an environment variable cannot widen a permission the caller withheld. The two switches are independent and both must pass: this flag is the sandbox grant, the variable is the alpha opt-in. Requires a binary built with the napi cargo feature (the default); a build without it rejects the flag at startup rather than granting a permission it cannot honour. |
--experimental-vm-modules |
Enables vm.SourceTextModule. |
--expose-internals |
Resolves internal/* from the builtin registry. |
--expose-gc, --no-warnings, --no-deprecation, --pending-deprecation |
As in Node. |
--tls-min-v1.0, --tls-min-v1.1, --tls-min-v1.2, --tls-min-v1.3, --tls-max-v1.2, --tls-max-v1.3 |
As in Node: the initial tls.DEFAULT_MIN_VERSION / DEFAULT_MAX_VERSION, so the default version range of every TLS connection and server (NODE_OPTIONS takes them too, several resolve in Node's precedence, and --tls-min-v1.3 with --tls-max-v1.2 is refused at startup with exit 9, as Node refuses it). A floor below TLS 1.2 is accepted and raised to 1.2, which is what Node negotiates too -- see docs/node-divergences.md, entry 34. |
--env-file=<path>, --env-file-if-exists=<path> |
As in Node. |
--v8-pool-size=<n> |
As in Node: the number of V8 platform worker threads (concurrent GC and background compilation). Default 4, capped at one per core but one; 0 (or anything below 1) sizes it from the core count. Also read from NODE_OPTIONS, reflected in process.execArgv (so a fork() child inherits it), and wins over OAM_V8_POOL_SIZE. |
-e / --eval, -p / --print, -pe |
As in Node, including the bundled form. |
oam run and oam serve take the V8 Inspector flags, and so do executables
built by oam compile. oam test, oam check and oam repl do not, and
reject them rather than ignoring them.
| Flag | Notes |
|---|---|
--inspect[=[host:]port] |
Attach the V8 Inspector (Chrome DevTools Protocol) and run immediately. Default 127.0.0.1:9229; a bare port binds loopback. |
--inspect-brk[=[host:]port] |
As --inspect, but execute nothing until a debugger attaches, then break on the first statement. Wins if both are given. |
One debugger at a time: a second concurrent client is refused with 503, and reconnecting after the first disconnects is supported. CDP has no authentication, so a non-loopback bind hands the process to the network.
A TypeScript entry is transpiled before V8 compiles it and oam attaches no
source map for the debugger, so the Sources panel shows generated JavaScript
with reflowed line numbers. Printed stack traces are mapped back to the .ts
line; the debugger is not. Full story:
https://oamjs.org/docs/inspector.
| Variable | Effect |
|---|---|
OAM_ENABLE_NATIVE_ADDONS=1 |
Enable N-API addon loading. Off by default and alpha: an addon compiled against node.exe can deadlock the OS loader inside oam, before any oam code runs, so the default is a clean throw that lets a package's JS fallback take over. This is the runtime switch; there is also a compile-time one — the napi cargo feature (on by default). A binary built --no-default-features has no N-API layer and no exported napi_* ABI at all, so require() of a .node throws OAM-NATIVE0002 and this variable does nothing. |
OAM_MAX_HEAP_MB |
Cap the V8 heap, in megabytes. Set this to match a container memory limit. Unset or empty, the cap is 75% of the container (cgroup) memory limit when there is one, else 4096. There is always a cap: 0, a negative or a non-numeric value is not "unlimited" -- it prints one warning on stderr and keeps the default. Reaching the cap exits 134 with an error[OAM-RT-OOM] line naming the cap and where it came from. |
OAM_V8_POOL_SIZE |
The V8 worker-thread count when --v8-pool-size is not given; same values, except that a value that is not a whole integer prints one warning and keeps the default (the flag, as in Node, reads it as 0). Default 4 (one per core but one on a smaller machine). Earlier releases started one per core but one in every process, which on a many-core box running many oam processes added up to hundreds of mostly idle threads; set 0 to get that back for a single large program that wants every core for GC. |
OAM_PRIORITY |
Lower this process's scheduling priority at startup: normal, below_normal, idle (alias low), or a number from 0 to 19 on Node's os.setPriority scale. Applied before any thread starts, and inherited by child processes (the variable itself is in their environment too, so every oam in a fork tree applies it). Lowering only: a process already started at that priority or lower (start /low, nice, an idle oam parent) is left where it is, so normal changes nothing unless the process started above normal; an unrecognized value prints one warning and is ignored. Meant for background work -- test suites, sidecars, hooks -- that should yield to interactive processes. |
OAM_ECOQOS=1 |
Also true or on. Windows: opt the process into EcoQoS (execution-speed power throttling), so the scheduler favours efficiency cores and lower clocks for it. No effect elsewhere. |
OAM_MAX_BODY_BYTES |
Aggregate cap on queued HTTP request-body bytes across all in-flight requests (default 512MB). Past it, excess uploads are shed rather than buffered. Per-request backpressure is the first line of defence; this bounds the total once concurrency is high. |
OAM_CODE_CACHE |
V8 bytecode cache across runs. On by default; 0, off, false or no turns it off entirely -- no consume, no produce, no write. Blobs live under <cache dir>/bytecode, content-addressed by source text + V8 build, so a stale blob is a miss, never a wrong hit. Housekeeping is automatic: at most once a day a run sweeps the directory on a background thread, deleting orphaned .tmp files older than 10 minutes and blobs not written in 30 days. oam cache info prints the directory, entry count and total size; oam cache clean deletes it (safe at any time -- the next run recompiles and repopulates). |
OAM_TRANSPILE_CACHE |
Transpile output cache across runs (TypeScript/TSX -> JS). On by default; 0, off, false or no turns it off entirely -- no read, no write. Entries live under <cache dir>/transpile, content-addressed by source text + transpile settings, so a stale entry is a miss, never a wrong hit. Every filesystem failure is a silent miss: the fallback (transpile again) is always correct. |
OAM_CACHE_DIR |
Where oam keeps its caches (bytecode, transpile output, type-check daemon state). Default: %LOCALAPPDATA%\oam on Windows, $XDG_CACHE_HOME/oam or ~/.cache/oam elsewhere; when none of those resolve (a scrubbed environment), the system temp dir -- never the working directory. |
OAM_IO_URING |
Opt into the Linux io_uring FS path. Off by default — it benchmarked as not a win. |
OAM_EXPERIMENTAL_VM_MODULES / OAM_EXPOSE_INTERNALS |
Env equivalents of the flags above; set by the CLI, readable by the loader. |
| Variable | Effect |
|---|---|
OAM_TSGO |
Path to the tsgo binary. Without it, oam prefers the project's node_modules/.bin/tsgo (nearest, walking up), then PATH. |
OAM_TSGO_TIMEOUT_MS |
Wall-clock bound on one tsgo run (default 300000). A run past it is killed — whole process tree — and reported as OAM-TS0006. |
OAM_CHECK_WAIT_MS |
How long warn-mode oam run waits for the concurrent checker after the program exits (default 10000). |
OAM_DAEMON_IDLE_MS |
How long the type-check daemon stays warm before exiting. |
OAM_DEBUG |
1 prints why a check fell back from the daemon to one-shot (normally swallowed by the never-worse-than-one-shot contract). |
| Variable | Effect |
|---|---|
OAM_VERSION |
Pin the version the installer fetches (e.g. v0.8.0). |
OAM_INSTALL_DIR |
Install target. Default ~/.oam/bin, or %LOCALAPPDATA%\oam\bin. |
OAM_INSTALL_BASE |
Asset base URL, for a mirror or CDN. Default is GitHub Releases. Requires OAM_VERSION. |
OAM_INSECURE_SKIP_SIGNATURE |
1 lets the installer put a v0.18.0+ release on a host with no usable ssh-keygen, without checking its signature. A bad signature still fails. |
OAM_SELF_UPDATE_URL |
Fetch the release files oam self-update installs from this base instead of GitHub Releases (needs --version). They must still carry a valid signature. When unset, oam self-update reads OAM_INSTALL_BASE the same way. |
OAM_GH_API |
GitHub API base, for GitHub Enterprise. |
GH_TOKEN / GITHUB_TOKEN |
Needed while the repo is private — unauthenticated asset URLs 404. In a pipeline put it on sh, not on curl: curl -fsSL … | GH_TOKEN=… sh. |
OAM_IGNORE_SCRIPTS |
Skip package lifecycle scripts during install. |
| Variable | Effect |
|---|---|
OAM_NAPI_TRACE |
Trace N-API calls. |
OAM_CRASH_SELFTEST |
Force a crash path, for testing the fatal reporter. |
OAM_DEP_VERSIONS / OAM_GIT_SHA |
Build-time provenance, surfaced in process.versions. |
OAM_CLUSTER_WORKER |
Set by oam in cluster workers; not something you set yourself. |
- why-oam.md — how oam compares to Node, Deno and Bun, and when to pick one of them instead.
- node-divergences.md — every place oam knowingly differs from Node, and why.
- ../BENCHMARKS.md — numbers, and how to reproduce them.
- https://oamjs.org/docs/inspector -- debugging with
--inspect/--inspect-brk, in full. - https://oamjs.org/docs/errors -- every
OAM-*diagnostic code, by family. Diagnostics link here themselves, anchored at the code. - https://oamjs.org/docs/odif -- the ODIF envelope behind
--json, field by field.