Runtime Support Matrix
Complete feature matrix for Hakobu-packaged binaries.
This page documents what works at runtime inside a Hakobu-packaged executable, including module system support, runtime APIs, native addons, and known limitations.
Node Version
Hakobu targets Node 24.x LTS exclusively.
| Property | Value |
|---|---|
| Node version | v24.14.0 |
| V8 ABI | 137 |
| Base binary format | Patched Node with snapshot filesystem support |
Older Node lines (18, 20, 22) are not supported. Hakobu only ships base binaries for Node 24.
Supported Targets
Published Base Binaries
| Target | Tier | Status |
|---|---|---|
linux-x64 | 1 | Available |
linux-arm64 | 1 | Available |
win-x64 | 1 | Available |
macos-arm64 | 1 | Available |
macos-x64 | 2 | Available |
linuxstatic-x64 | 2 | Available |
win-arm64 | 2 | Available (requires windows-11-arm runner) |
Blocked Targets
| Target | Reason |
|---|---|
linuxstatic-arm64 | muslcc GCC too old for Node 24's C++20 requirements |
alpine-* | Not yet built (would be similar to linuxstatic) |
Use hakobu targets to see which base binaries are cached locally and which are available remotely.
ESM Support
| Feature | Status | Notes |
|---|---|---|
"type": "module" in package.json | Full support | |
.mjs files | Full support | |
Static import | Full support | |
Dynamic import() | Supported | Static string paths only |
import.meta.url | Full support | Returns file:///snapshot/... URL |
package.json "exports" map | Full support | Main entry + subpath exports |
package.json "imports" map (#specifiers) | Supported | |
Import conditions: import, node, default | Full support |
CJS Support
| Feature | Status | Notes |
|---|---|---|
require() | Full support | |
require.resolve() | Full support | |
__dirname / __filename | Available | Point to snapshot paths |
module.exports / exports | Full support | |
.cjs files | Full support | |
require('./data.json') | Full support |
Mixed Module Support
| Scenario | Status |
|---|---|
ESM entry with createRequire() for CJS dependencies | Works |
CJS subpackage inside ESM root (via nested "type": "commonjs") | Works |
.mjs in CJS package / .cjs in ESM package | Works |
Child Processes
| Feature | Status | Notes |
|---|---|---|
child_process.spawn() / spawnSync() | Works | |
child_process.fork() with IPC | Works | |
child_process.exec() / execSync() | Works | |
process.execPath | Works | Points to the packaged binary |
| Environment inheritance | Works | |
| Custom stdio arrays | Works |
process.execPath -e "code" does not work from inside a packaged app. The prelude interprets -e as a module path. Use script files instead.
Worker Threads
| Feature | Status | Notes |
|---|---|---|
new Worker(path, { workerData }) | Works | |
parentPort.postMessage() / worker.on('message') | Works | |
isMainThread | Works | Correct in both main and worker contexts |
Native Addons
Native .node files are detected during analysis and included in the snapshot. At runtime, addons are extracted from the snapshot to a cache directory and loaded via process.dlopen.
| Behavior | Details |
|---|---|
| Detection | .node files found during static analysis |
| Extraction path | ~/.hakobu/addons/{sha256}/{filename}.node |
| Extraction behavior | Idempotent and cache-friendly |
Addon Cache Resolution
The cache directory is resolved in the following order:
| Priority | Source | Example |
|---|---|---|
| 1 | HAKOBU_ADDON_CACHE env var | /opt/myapp/addon-cache |
| 2 | PKG_NATIVE_CACHE_PATH env var (legacy compat) | /opt/myapp/cache |
| 3 | $HOME/.hakobu/addons (default) | /home/user/.hakobu/addons |
| 4 | os.tmpdir() (fallback when home is unavailable) | /tmp/hakobu-addons/{hash} |
This means packaged apps work correctly for service users (nobody, www-data) and in containers where $HOME may not exist.
Native addons must be compiled for the target platform before packaging. Cross-compilation of .node files is not handled by Hakobu.
External Artifacts
External binaries (browsers, helper tools, large data files) can be declared in the Hakobu config and resolved at runtime:
{
"hakobu": {
"externals": ["camoufox-browser"]
}
}Resolution order:
HAKOBU_EXTERNAL_{NAME}environment variable (uppercase, dashes replaced with underscores){execDir}/externals/{name}/(relative to the executable)- Pattern path (absolute or relative to cwd)
Assets
Non-code files (templates, data, config) can be included via the assets config:
{
"hakobu": {
"assets": ["templates/**", "config.yaml"]
}
}Assets are included in the snapshot and readable via fs.readFileSync() at runtime using __dirname-relative or import.meta.url-derived paths.
Bytecode Mode
Opt-in V8 bytecode compilation for CJS scripts:
hakobu ./my-app --bytecode --output ./dist/app| Input Type | Bytecode compiled? |
|---|---|
CJS scripts (.js, .cjs) | Yes |
ESM scripts (.mjs, type: "module") | No -- source only |
| JSON files | No -- included as content |
| Assets / native addons | No -- included as content/extracted |
--bytecode and --bundle cannot be combined (bundle produces ESM). Bytecode is V8 version-specific -- executables must run on the same V8 version they were compiled with.
Offline / Air-Gapped Packaging
Hakobu can package without network access when the required base binaries are already cached:
HAKOBU_OFFLINE=1 hakobu ./my-app --output ./dist/appTo pre-populate the cache on a machine with network access, run Hakobu once per target, then copy ~/.hakobu/cache/ to the air-gapped machine. The cache location is configurable via HAKOBU_CACHE_PATH.
Known Limitations
- Node 24 only -- no support for older Node lines.
process.execPath -enot supported -- use script files for child eval.linuxstatic-arm64blocked -- toolchain incompatibility with Node 24's C++20 requirements.- Bytecode + bundle incompatible -- use one or the other, not both.