A JavaScript engine and runtime written in the Milo programming language.
MiloJS owns its parser, tree-walking evaluator, garbage collector, regular expression engine, BigInt implementation, module loader, and event loop. It has no V8, JavaScriptCore, or C JavaScript engine underneath it — about 26.5k lines of Milo, from source text to running program. It starts in a few milliseconds and embeds into a C program as a static library.
This is an experimental implementation, not yet a drop-in replacement for QuickJS or Node. See the current status for shipped capabilities, measured evidence, known gaps, and product gates.
There are two command-line binaries:
milojs-engineruns raw JavaScript with no host bindings. Its target is a small embeddable engine in the same problem space as QuickJS.milojsadds CommonJS loading, parse-time ESM compatibility lowering, an event loop, filesystem/network access, Node-compatible modules, and Node-API addon loading. Node compatibility is the API target; Deno and Bun are comparisons rather than additional contracts.
The tree walker keeps the implementation understandable but is substantially slower than a production bytecode VM or JIT.
Rolling builds from the latest passing commit on main are available for Linux x64/arm64 and macOS arm64:
P=$(uname -s | tr A-Z a-z)-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/')
curl -fsSL https://github.com/milo-language/milojs/releases/download/latest/milojs-$P.tar.gz | tar xz
cd milojs-$PVerify the downloaded binaries:
./milojs --version
./milojs-engine --versionprintf 'const name = "Milo"; console.log(`hello from ${name}`);\n' > hello.js
./milojs-engine hello.jshello from Milo
Use the milojs-engine CLI to evaluate raw JavaScript without built-in host APIs. Embedders can provide their own host through the preview libmilojs C ABI. The milojs runtime is one such host: it adds modules, timers, filesystem and network APIs, and Node-API addons.
Building from source requires the latest released Milo compiler, Clang 16 or newer, and OpenSSL development libraries. The complete test suite also uses Z3 for static contract verification.
milo build src/milojs-engine.milo -o /tmp/mj-engine
milo build src/milojs.milo -o /tmp/mj-runtime
MILOJS_ENGINE_BIN=/tmp/mj-engine MILOJS_RUNTIME_BIN=/tmp/mj-runtime ./tests/run.sh
./tests/run-milo.sh
MILOJS_RUNTIME_BIN=/tmp/mj-runtime ./tests/run-repl.sh
./tests/run-embed.sh
MILOJS_RUNTIME_BIN=/tmp/mj-runtime ./tests/run-napi.shRun the fast symbol and generated-document checks before committing:
./tools/precommit.shThe preview C ABI is single-context while async/generator re-entry remains process-global. It supports evaluation, primitive values, property reads, exceptions, explicit value release, and GC-rooted object handles. milo build-lib must finish successfully because it generates both libmilojs.a and libmilojs.h.
Build the library, then compile and run the C embedding example.
On Linux:
# Linux
milo build-lib src/libmilojs.milo -o libmilojs.a &&
cc -std=c11 -I. -Iinclude examples/embed/hello.c libmilojs.a -lm -lssl -lcrypto -lsqlite3 -ldl -pthread -o hello &&
./helloOn macOS (drop -lm/-ldl; point at Homebrew's OpenSSL):
# macOS
milo build-lib src/libmilojs.milo -o libmilojs.a &&
cc -std=c11 -I. -Iinclude \
-I"$(brew --prefix openssl)/include" -L"$(brew --prefix openssl)/lib" \
examples/embed/hello.c libmilojs.a -lssl -lcrypto -lsqlite3 -pthread -o hello &&
./hellohello from embedded milo, woof! the answer is 42
Include include/milojs.h together with the generated libmilojs.h. The tested link recipe and transitive system libraries are in tests/run-embed.sh; the API and ownership contract are documented in docs/milojs-embedding.md.
The runtime loads real .node shared libraries. Promise, reference, class, wrapping, threadsafe-function, and synchronous JavaScript-callback paths are implemented and exercised with a compiled differential addon. Some entry points remain explicit compatibility stubs; the maintained count and important gaps are listed in the current status.
Start with AGENTS.md for the repository workflow and Milo-specific hazards. The status is the canonical capability dashboard, the backlog carries current work, and the roadmap records product gates and design direction.
Milo contracts statically prove selected invariants. CI ratchets proved, unknown, and refuted counts so a proof cannot silently disappear. An unknown contract remains documentation, not runtime enforcement or a correctness proof.
MiloJS is available under the MIT License.
