A from-scratch JavaScript engine in Rust — std only, zero dependencies — and a
runtime being built on top of it, the way Node/Deno/Bun wrap a JS engine with an event
loop and host APIs. Every crate in the workspace is std-only: no tokio, mio, libc,
rustyline, serde, or any other third-party dependency, anywhere.
A lexer, parser, and tree-walking interpreter with generators and async/await running on
stackful coroutines, full RegExp (including \p{…} and inline modifiers), typed arrays,
Proxy/Reflect, ES modules (top-level await, import defer, source phase), Intl, and
Temporal. An opt-in bytecode execution tier is under active development alongside the
reference tree-walker.
Passes 100% of tc39/test262: 53,400/53,400 (including annexB, intl402, and staging).
Extracted from — and used by — the lucid-softworks/browser
engine as its JS backend (backend-lumen), with full git history.
A curated embed API on the engine exposes just enough — native-function registration, a
typed host-state slot, and event-loop hooks — for a runtime layer to be assembled from
independent op crates, without leaking the interpreter's internals into the published API.
On top of that:
-
Event loop (
lumen-runtime) — a single loop thread owns the (!Send) engine; blocking work runs on a std thread pool and completes back overmpsc. No epoll/kqueue reactor (that would need raw syscalls); the thread-pool-plus-completion model is libuv's own fs strategy. Each turn drains microtasks, queued callbacks, due timers, and I/O completions, then blocks until the next event. -
Timers (
lumen-timers) —setTimeout/setInterval/clearTimeout/clearInterval/setImmediate, plusqueueMicrotask. -
consoleandprocess— streamingconsole.*;process.argv/env/platform/cwd()/exit()/nextTick(). -
Filesystem (
lumen-fs) — synchronous ops (readFileSync,writeFileSync,existsSync,mkdirSync,readdirSync, …), file handles via a resource table (openSync/readSync/writeSync/closeSync), and asyncfs.promises.readFile/writeFileon the thread pool. -
Web platform (
lumen-web) — a growing slice of the WinterTC Minimum Common API:Event/EventTarget/CustomEvent/AbortController/AbortSignal/DOMException,TextEncoder/TextDecoder,atob/btoa,structuredClone,URL/URLSearchParams,performance.now(),crypto.getRandomValues/randomUUID/subtle.digest(SHA-256), andfetch/Headers/Request/Response. See the checklist at the top ofcrates/lumen-web/src/lib.rsfor what's implemented vs. deferred (streams,Blob/FormData,URLPattern, …).fetchspeaks HTTP/1.1 overstd::net.https:is not supported: TLS cannot be implemented on std alone and no third-party crate is permitted, sohttpsURLs reject with a clear error; plainhttpworks.Lumen.serve((request) => Response)is the matching HTTP/1.1 server — not a WinterTC API, but the cross-runtimeserve(handler)convention (Deno/Bun/Workers), so a Hono app runs withLumen.serve(app.fetch). v1 is single-accept,Connection: close, buffered bodies, http only (seecrates/lumen-web/src/server.rs). Cold-start and usage:examples/hono-app. -
Modules — both CommonJS and ESM.
lumen-clipicks the module kind the way Node does:.mjsis ESM,.cjsis CommonJS,.jsfollows the nearestpackage.json"type". ES modules run through the engine's real module graph (linking, top-levelawait);importspecifiers resolve against disk andnode_modules,node:builtins are importable (named imports included), and CommonJS packages interop by default export. CommonJS files run as the program entry withrequire.main === module. -
node:compatibility (lumen-node) — a CommonJSrequirewithnode_modulesresolution and the module wrapper,package.jsonmain/exports, thenode:path/node:os/node:fsbuiltins, andBuffer, so packages written against thenode:surface run. See the checklist at the top ofcrates/lumen-node/src/lib.rsfor the deferred pieces (subpath-pattern exports, the full N-API surface).Native addons load too:
require('./addon.node')dlopens the compiled library and runs its N-API registration, resolving the addon'snapi_*symbols against the lumen executable — the same mechanism thenodebinary uses. The N-API surface is implemented from scratch (values, properties, functions, callbacks, errors, references, object wrap, classes, promises, buffers, typed arrays, async work); the loader reachesdlopen/dlsymthrough rawextern "C"declarations, so no third-party crate is added. Seeexamples/native-addon.vite buildruns on lumen (examples/vite-app): a full Vite production build, bundling through Rollup's native N-API addon, transforming with esbuild's service subprocess, over ESM↔CommonJS interop and thenode:surface — buildingdist/and exiting cleanly. -
REPL + CLI (
lumen-repl,lumen-cli) — an interactive shell with a persistent realm, parser-driven incomplete-input detection (multi-line continuation), top-levelawait, and loop-to-quiescence so timers and awaited promises settle before the next prompt. Line editing is line-buffered (raw-mode/history would needtermios); userlwrapfor arrows and history.
lumen engine (std-only, zero-dep; `embed` feature gates the runtime API)
lumen-host substrate: OpState, ResourceTable, Extension, the thread-pool/callback primitives
lumen-timers setTimeout/setInterval/queueMicrotask/setImmediate
lumen-fs filesystem (sync + async)
lumen-web WinterTC Minimum Common API (Event, URL, crypto, fetch, …)
lumen-node node: compatibility (require, node:path/os/fs, Buffer)
lumen-runtime the event loop; assembles the op crates; console + process
lumen-repl interactive shell
lumen-cli node/deno-style entrypoint
The dependency graph is a strict DAG — lumen ← lumen-host ← {op crates} ← lumen-runtime ← lumen-repl ← lumen-cli — so each op crate can be worked on in isolation.
Grab a prebuilt runtime for your platform (macOS arm64, Linux x86_64/arm64):
curl -fsSL https://raw.githubusercontent.com/lucid-softworks/lumen/main/scripts/install.sh | bashIt installs the lumen CLI to ~/.lumen/bin from the rolling nightly release
(LUMEN_INSTALL and LUMEN_RELEASE override the location and tag). Other platforms build from
source — see below.
Run scripts / open a REPL through the runtime:
cargo build --release -p lumen-cli
./target/release/lumen-cli # REPL (or: lumen-cli repl)
./target/release/lumen-cli file.js [args] # run a script to loop quiescence
./target/release/lumen-cli -e 'code' # evaluate a stringThe engine also ships a minimal standalone shell (the test262 host, no runtime/host APIs):
cargo build --release -p lumen --bin lumen
./target/release/lumen file.js [more.js ...]scripts/test262-clone.sh # one-time: clone the suite into ./test262
scripts/run-test262.sh # run it (see crates/test262-runner for env knobs)scripts/run-v8bench.sh # classic V8 suite (v8-v7); downloads on first run
git clone https://github.com/chromium/octane.git ../octane # one-time: Octane checkout
scripts/run-octane.sh # full Octane suite
scripts/run-octane.sh richards crypto # selected benchmarksOctane is expected at ../octane by default; set OCTANE=/path/to/octane to override.