feat(sea): use node --build-sea when the base Node supports it (>= 25.5)#280
feat(sea): use node --build-sea when the base Node supports it (>= 25.5)#280cstrahan wants to merge 2 commits into
node --build-sea when the base Node supports it (>= 25.5)#280Conversation
SEA mode previously always downloaded a base Node binary from nodejs.org, so the resulting executable was pinned to an official build. That makes it impossible to ship a SEA on top of a custom runtime -- e.g. a Node built against an older glibc (to run on EL7 / older distros), or one configured differently. The internal SeaOptions / SeaEnhancedOptions already accepted `nodePath` / `useLocalNode`; this just exposes them: --sea-node-path <path> embed a specific Node binary as the base --sea-use-local-node embed the Node running pkg (process.execPath) Both are also settable in the pkg config (seaNodePath / seaUseLocalNode) and via the programmatic exec() API. The two are mutually exclusive. The embedded binary's major version must match the target's (the existing version-skew checks still apply). This is also a prerequisite for adopting `node --build-sea` (Node >= 25.5): that flag is only meaningful once the base Node is no longer a fixed download. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…25.5) Node v25.5.0 added an in-core `node --build-sea <config>` that generates the SEA blob and injects it into a base binary (config `executable` -> `output`) in one step, using Node's *bundled, current* LIEF. That avoids the external `postject`, whose 3-year-old vendored LIEF (0.13.0) corrupts the dynamic symbol table of PIE (ET_DYN) binaries -- which silently breaks native (raw-V8) addons in the resulting SEA (`undefined symbol: _ZN2v8...`). The corruption was fixed upstream in LIEF 0.16.7; Node >= 25.5 ships a fixed LIEF. When the host-runnable generator Node reports >= 25.5, both SEA paths (enhanced and simple) inject via `--build-sea` instead of generating a prep blob and shelling out to postject. `assertSingleTargetMajor` guarantees every target shares the generator's major, so the generated blob is compatible across targets -- the multi-target concern that previously ruled `--build-sea` out does not actually apply (it reads the base from the config `executable` field, not the running binary). Older generators keep the postject path unchanged. Combined with --sea-node-path / --sea-use-local-node, this lets a custom Node build (e.g. one linked against an older glibc) produce SEAs with working native addons and no external injector. ARCHITECTURE.md is updated to reflect the new path and to correct the prior claim that `--build-sea` could not serve pkg. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
node --build-sea when the base Node supports it (>= 25.5)
| * Uses --experimental-sea-config (not --build-sea): --build-sea produces | ||
| * a finished executable and bypasses the prep-blob + postject flow that | ||
| * pkg relies on for multi-target support and for injecting custom | ||
| * bootstraps into downloaded node binaries. |
There was a problem hiding this comment.
the reason I didn't do that is this one
There was a problem hiding this comment.
Sorry, this might boil down to my own lack of familiarity with the full functionality of pkg, resulting in my misinterpretation of the reasoning outlined here - I'm guessing around either the implications of multi-target (we only need one target, so I don't have experience with that yet), or the "injecting custom bootstraps" aspect.
I'll familiarize myself the Targets docs and the boostrap logic and circle back. If --biuld-sea is fundamentally unfit for this project, I'll see if I can add a little extra info here so others don't feel tempted to go down the same path (or maybe a patch sent upstream to Node's --build-sea could make it fit for purpose?).
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #280 +/- ##
==========================================
- Coverage 86.36% 86.14% -0.22%
==========================================
Files 22 22
Lines 7297 7442 +145
Branches 1047 1051 +4
==========================================
+ Hits 6302 6411 +109
- Misses 988 1024 +36
Partials 7 7
🚀 New features to boost your workflow:
|
(This builds on top of #279)
Node v25.5.0 added an in-core
node --build-sea <config>that generates the SEA blob and injects it into a base binary (configexecutable->output) in one step, using Node's bundled, current LIEF. That avoids the externalpostject, whose 3-year-old vendored LIEF (0.13.0) corrupts the dynamic symbol table of PIE (ET_DYN) binaries (see nodejs/postject#101 and lief-project/LIEF#1097) -- which silently breaks native (raw-V8) addons in the resulting SEA (undefined symbol: _ZN2v8...). The corruption was fixed upstream in LIEF 0.16.7; Node >= 25.5 ships a fixed LIEF.When the host-runnable generator Node reports >= 25.5, both SEA paths (enhanced and simple) inject via
--build-seainstead of generating a prep blob and shelling out to postject.assertSingleTargetMajorguarantees every target shares the generator's major, so the generated blob is compatible across targets -- the multi-target concern that previously ruled--build-seaout does not actually apply (it reads the base from the configexecutablefield, not the running binary). Older generators keep the postject path unchanged.Combined with
--sea-node-path/--sea-use-local-node, this lets a custom Node build (e.g. one linked against an older glibc) produce SEAs with working native addons and no external injector. ARCHITECTURE.md is updated to reflect the new path and to correct the prior claim that--build-seacould not serve pkg.