1# Single executable applications
2
3<!--introduced_in=v18.16.0-->
4
5<!-- YAML
6added:
7  - v19.7.0
8  - v18.16.0
9-->
10
11> Stability: 1 - Experimental: This feature is being designed and will change.
12
13<!-- source_link=src/node_sea.cc -->
14
15This feature allows the distribution of a Node.js application conveniently to a
16system that does not have Node.js installed.
17
18Node.js supports the creation of [single executable applications][] by allowing
19the injection of a JavaScript file into the `node` binary. During start up, the
20program checks if anything has been injected. If the script is found, it
21executes its contents. Otherwise Node.js operates as it normally does.
22
23The single executable application feature only supports running a single
24embedded [CommonJS][] file.
25
26A bundled JavaScript file can be turned into a single executable application
27with any tool which can inject resources into the `node` binary.
28
29Here are the steps for creating a single executable application using one such
30tool, [postject][]:
31
321. Create a JavaScript file:
33   ```console
34   $ echo 'console.log(`Hello, ${process.argv[2]}!`);' > hello.js
35   ```
36
372. Create a copy of the `node` executable and name it according to your needs:
38   ```console
39   $ cp $(command -v node) hello
40   ```
41
423. Remove the signature of the binary:
43
44   * On macOS:
45
46   ```console
47   $ codesign --remove-signature hello
48   ```
49
50   * On Windows (optional):
51
52   [signtool][] can be used from the installed [Windows SDK][]. If this step is
53   skipped, ignore any signature-related warning from postject.
54
55   ```console
56   $ signtool remove /s hello
57   ```
58
594. Inject the JavaScript file into the copied binary by running `postject` with
60   the following options:
61
62   * `hello` - The name of the copy of the `node` executable created in step 2.
63   * `NODE_JS_CODE` - The name of the resource / note / section in the binary
64     where the contents of the JavaScript file will be stored.
65   * `hello.js` - The name of the JavaScript file created in step 1.
66   * `--sentinel-fuse NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2` - The
67     [fuse][] used by the Node.js project to detect if a file has been injected.
68   * `--macho-segment-name NODE_JS` (only needed on macOS) - The name of the
69     segment in the binary where the contents of the JavaScript file will be
70     stored.
71
72   To summarize, here is the required command for each platform:
73
74   * On systems other than macOS:
75     ```console
76     $ npx postject hello NODE_JS_CODE hello.js \
77         --sentinel-fuse NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2
78     ```
79
80   * On macOS:
81     ```console
82     $ npx postject hello NODE_JS_CODE hello.js \
83         --sentinel-fuse NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2 \
84         --macho-segment-name NODE_JS
85     ```
86
875. Sign the binary:
88
89   * On macOS:
90
91   ```console
92   $ codesign --sign - hello
93   ```
94
95   * On Windows (optional):
96
97   A certificate needs to be present for this to work. However, the unsigned
98   binary would still be runnable.
99
100   ```console
101   $ signtool sign /fd SHA256 hello
102   ```
103
1046. Run the binary:
105   ```console
106   $ ./hello world
107   Hello, world!
108   ```
109
110## Notes
111
112### `require(id)` in the injected module is not file based
113
114`require()` in the injected module is not the same as the [`require()`][]
115available to modules that are not injected. It also does not have any of the
116properties that non-injected [`require()`][] has except [`require.main`][]. It
117can only be used to load built-in modules. Attempting to load a module that can
118only be found in the file system will throw an error.
119
120Instead of relying on a file based `require()`, users can bundle their
121application into a standalone JavaScript file to inject into the executable.
122This also ensures a more deterministic dependency graph.
123
124However, if a file based `require()` is still needed, that can also be achieved:
125
126```js
127const { createRequire } = require('node:module');
128require = createRequire(__filename);
129```
130
131### `__filename` and `module.filename` in the injected module
132
133The values of `__filename` and `module.filename` in the injected module are
134equal to [`process.execPath`][].
135
136### `__dirname` in the injected module
137
138The value of `__dirname` in the injected module is equal to the directory name
139of [`process.execPath`][].
140
141### Single executable application creation process
142
143A tool aiming to create a single executable Node.js application must
144inject the contents of a JavaScript file into:
145
146* a resource named `NODE_JS_CODE` if the `node` binary is a [PE][] file
147* a section named `NODE_JS_CODE` in the `NODE_JS` segment if the `node` binary
148  is a [Mach-O][] file
149* a note named `NODE_JS_CODE` if the `node` binary is an [ELF][] file
150
151Search the binary for the
152`NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2:0` [fuse][] string and flip the
153last character to `1` to indicate that a resource has been injected.
154
155### Platform support
156
157Single-executable support is tested regularly on CI only on the following
158platforms:
159
160* Windows
161* macOS
162* Linux (all distributions [supported by Node.js][] except Alpine and all
163  architectures [supported by Node.js][] except s390x and ppc64)
164
165This is due to a lack of better tools to generate single-executables that can be
166used to test this feature on other platforms.
167
168Suggestions for other resource injection tools/workflows are welcomed. Please
169start a discussion at <https://github.com/nodejs/single-executable/discussions>
170to help us document them.
171
172[CommonJS]: modules.md#modules-commonjs-modules
173[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
174[Mach-O]: https://en.wikipedia.org/wiki/Mach-O
175[PE]: https://en.wikipedia.org/wiki/Portable_Executable
176[Windows SDK]: https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/
177[`process.execPath`]: process.md#processexecpath
178[`require()`]: modules.md#requireid
179[`require.main`]: modules.md#accessing-the-main-module
180[fuse]: https://www.electronjs.org/docs/latest/tutorial/fuses
181[postject]: https://github.com/nodejs/postject
182[signtool]: https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool
183[single executable applications]: https://github.com/nodejs/single-executable
184[supported by Node.js]: https://github.com/nodejs/node/blob/main/BUILDING.md#platform-list
185