nodejs/node
Single Executable Applications (SEA)
Owners: @nodejs/single-executable. Public docs at doc/api/single-executable-applications.md.
Purpose
Bundle a Node application into a single, distributable native executable. SEA produces a copy of the node binary with the user's bundled JS injected as a section that is read at startup instead of looking for a main script.
Directory layout
lib/sea.js // public node:sea API
src/node_sea.{cc,h} // SEA reader / startup integration
src/node_sea_bin.cc // standalone tool? (sea binary helpers)
deps/postject/ // tool that injects payload sections
deps/LIEF/ // ELF/Mach-O/PE manipulation library
tools/dep_updaters/update-postject.sh
tools/dep_updaters/update-lief.sh
test/sea/ // integration testsKey abstractions
| Type / file | Role |
|---|---|
SEAFlags enum (node_sea.h) |
Section flags: kIsSea, kDisableExperimentalSeaWarning, etc. |
IsExperimentalSeaWarningNeeded |
Decides whether to print the experimental warning. |
lib/sea.js |
getAsset, getAssetAsBlob, isSea — the small JS surface. |
postject |
Injects a binary blob into a Mach-O / ELF / PE without resigning. |
LIEF |
Used for code-sign re-signing on macOS / Windows. |
Build flow
graph TD
Entry[your app entry.js] --> Bundle[bundle JS into single file]
Cfg[sea-config.json] --> NodeBuild[node --experimental-sea-config]
Bundle --> Blob[sea-prep.blob]
Blob --> Postject[postject ./node NODE_SEA_BLOB blob NODE_SEA_FUSE]
Postject --> Out[your-app native binary]
Out --> Codesign[macOS codesign / Windows signtool via LIEF]- Author a
sea-config.jsonlisting the entry, asset map, and snapshot toggle. - Run
node --experimental-sea-config sea-config.jsonto produce a payload blob. - Copy the
nodebinary, then usepostjectto embed the blob in the binary's NOTES/Mach-O segment. - On macOS, re-sign with
codesign. On Windows, sign withsigntool. LIEF helps by keeping the signature in sync with the new section layout.
How startup detects SEA
src/node_sea.cc reads the embedded blob early in node::Start. If the blob is present:
- The blob's "main script" name overrides the script-resolution logic.
- The asset map is wired into
lib/sea.js#getAsset. - The
kIsSeaflag suppresses the default REPL/eval/main-module dispatch and runs the bundled entry instead.
If the blob is absent (the binary is a normal node), nothing else changes.
Permission model
SEA runs with the same permission model as normal Node, including --permission defaults. Embedding --permission in the SEA config makes it permanent for that binary.
Integration points
- Bootstrap:
lib/internal/main/run_main_module.jsdefers to the SEA entry whenprocess.config.variables.node_seaetc. are set. - Snapshot: a SEA can ship its own V8 snapshot, generated from the user's entry. The snapshot path is the same one Node uses for its own bootstrap (
src/node_snapshotable.cc). node:sea: the JS API is intentionally tiny —isSea(),getAsset(name[, encoding]),getAssetAsBlob(name). Heavier features live in user space.
Entry points for modification
- Adding a new manifest field? Edit
src/node_sea.cc(parser + flags) andlib/sea.js(JS accessor) and document atdoc/api/single-executable-applications.md. - Changing the embed format? Coordinate with
deps/postject/and the platform code-sign workflow. - Tests at
test/sea/and fixtures attest/fixtures/postject-copy/are the canonical regression spots.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.