sharjeenux
v1.0.1
Published
A modular, headless Buildroot Linux VM controlled from JavaScript. Install language runtimes as plugins: @sharjeenux/node, @sharjeenux/python, @sharjeenux/java.
Maintainers
Readme
Sharjeenux
Sharjeenux is a headless Buildroot Linux VM that runs through v86 and is controlled from Node.js. The base package contains Linux, BusyBox, networking, Git, curl, wget, OpenSSL, and archive tools. Language runtimes are separate npm packages, so applications install only what they need.
Packages
| Package | Guest tools |
| --- | --- |
| sharjeenux | Base Linux, shell, Git, curl, wget, OpenSSL, zip/unzip, tar, gzip, bzip2, xz |
| @sharjeenux/node | Node.js 20.20.2, npm, npx, Corepack, TypeScript 6.0.3 |
| @sharjeenux/python | Python 3.12.13 and pip 25.2 |
| @sharjeenux/java | OpenJDK 21.0.11 and Apache Maven 3.9.16 |
sharjeenux/node cannot be a separately installed npm package. In npm, a
slash denotes a scoped package only when the name begins with @. The
separately publishable form is therefore @sharjeenux/node, matching packages
such as @langchain/ollama.
Install
Install the base VM by itself:
npm install sharjeenuxAdd one runtime:
npm install sharjeenux @sharjeenux/node
npm install sharjeenux @sharjeenux/python
npm install sharjeenux @sharjeenux/javaOr install all runtimes:
npm install sharjeenux @sharjeenux/node @sharjeenux/python @sharjeenux/javaThe runtime packages declare sharjeenux as a peer dependency. Installing the
base explicitly keeps the intended version visible in your package.json.
Quick start
import { initialize, send, shutdown } from "sharjeenux";
await initialize();
console.log(await send("uname -a"));
console.log(await send("git --version"));
await shutdown();Installed runtime packages are detected from the nearest package.json:
import { initialize, exec, shutdown } from "sharjeenux";
await initialize();
console.log((await exec("node -e 'console.log(6 * 7)'" )).output);
console.log((await exec("python3 -c 'print(6 * 7)'" )).output);
console.log((await exec("java --version")).output);
await shutdown();CommonJS is supported:
const { initialize, send, shutdown } = require("sharjeenux");
await initialize();
console.log(await send("pwd"));
await shutdown();Selecting plugins explicitly
Automatic detection reads dependencies, optional dependencies, and development
dependencies from the nearest package.json. Explicit selection is useful in
monorepos or when the process starts from an unusual working directory:
import { create } from "sharjeenux";
const vm = create({
plugins: ["@sharjeenux/node", "@sharjeenux/python"],
});
await vm.initialize();
console.log(await vm.send("node --version"));
await vm.shutdown();A missing or invalid requested plugin rejects initialization with an error. It does not silently boot without the requested runtime.
API
initialize(options?)
Boots the default VM and returns it. Repeated calls return the same instance.
const vm = await initialize({
memoryMB: 2048,
bootTimeoutMs: 180_000,
commandTimeoutMs: 1_800_000,
networking: true,
networkRelayUrl: "fetch",
verifyAssets: true,
plugins: [],
});memoryMB must be a power of two and at least 128. An empty plugins array
still enables dependency auto-detection.
send(command, options?)
Runs a command and returns its output string:
const output = await send("ls -la");exec(command, options?)
Returns command details without throwing for a nonzero exit code:
const result = await exec("test -f package.json");
console.log(result.output, result.exitCode, result.durationMs);Both methods support streaming, cancellation, timeouts, and interactive echo:
await vm.send("npm install", {
timeoutMs: 600_000,
onOutput: chunk => process.stdout.write(chunk),
signal: abortController.signal,
});spawn(command, options?)
Use spawn() for a development server or any command that does not exit by
itself:
const server = vm.spawn("npm run dev -- --host 0.0.0.0", {
timeoutMs: 0,
onOutput: chunk => process.stdout.write(chunk),
});
server.write("input\n");
server.kill();
await server.exit;shutdown()
Destroys the default VM and releases its resources.
For independent VMs, use create(options) and call methods on the returned
instance.
Package managers
The language packages provide their normal package managers:
await vm.send("npm install is-number"); // @sharjeenux/node
await vm.send("pip3 install requests"); // @sharjeenux/python
await vm.send("mvn --version"); // @sharjeenux/javaBuildroot has no compatible apt, apk, or dnf repository. Native system
packages must be added to the Buildroot configuration and rebuilt. Pure
JavaScript and pure-Python dependencies are the safest choices; native npm or
Python extensions are not guaranteed because the guest does not ship an
on-target C/C++ compiler toolchain.
Networking and ports
The default fetch transport supports outbound HTTP. Sharjeenux configures npm
to use it. Direct guest TCP/TLS traffic, including many HTTPS Git, pip, Maven,
curl, and wget requests, requires a trusted Wisp relay:
await initialize({
networkRelayUrl: "wisps://your-relay.example",
});Set networking: false for an offline VM. Sharjeenux does not map listening
ports from the guest to the host, browser, or public network.
How the packages combine
Each npm package contains a compressed newc initramfs layer represented as
Base64 text chunks of at most 1 KiB. Sharjeenux verifies and decodes the base
layer and each installed runtime layer, concatenates them, and passes the result
to Linux as one external initramfs. Linux unpacks the archives in order into a
single in-memory root filesystem.
Decoded assets are cached under the host temporary directory by SHA-256. The first boot opens many small files; later boots reuse the verified cache.
Runtime constraints
Sharjeenux requires Node.js 20.6 or newer, WebAssembly support, a writable host temporary directory, and enough memory for the configured guest plus emulator overhead. It is a Node.js package, not a browser or edge-runtime package.
It emulates a complete 32-bit x86 computer and is materially slower than native containers or WebContainers. Streaming output makes long operations observable but does not remove the emulation cost.
Creating a third-party runtime layer
A plugin package must contain:
plugin.jsonwithformat: 1andtype: "sharjeenux-initramfs";assets/manifest.jsondescribing an image namedplugin;assets/chunks/plugin/...containing the Base64 chunks; and- a package name beginning with
@sharjeenux/orsharjeenux-.
Pass third-party package names through plugins if they are not direct project
dependencies. A plugin is executable code inside the guest; install only
packages you trust.
Licensing and corresponding source
The JavaScript wrapper is MIT-licensed. The VM layers contain third-party
components under their own licenses, including GPL and LGPL components. See
THIRD_PARTY_NOTICES.md, SOURCE_OFFER.md, and compliance/ in the base
package. Runtime packages repeat the notice and source-offer files so each
binary package carries the relevant distribution information.
Release artifacts must be shipped with the matching corresponding-source
archive identified by compliance/README.md. The compliance files are
engineering aids, not legal advice.
