Open-Source Wikis

/

Node.js

/

Systems

/

Single Executable Applications (SEA)

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 tests

Key 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]
  1. Author a sea-config.json listing the entry, asset map, and snapshot toggle.
  2. Run node --experimental-sea-config sea-config.json to produce a payload blob.
  3. Copy the node binary, then use postject to embed the blob in the binary's NOTES/Mach-O segment.
  4. On macOS, re-sign with codesign. On Windows, sign with signtool. 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 kIsSea flag 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.js defers to the SEA entry when process.config.variables.node_sea etc. 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) and lib/sea.js (JS accessor) and document at doc/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 at test/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.

Single Executable Applications (SEA) – Node.js wiki | Factory