Custom Node.js Build

Motivation

VibeTunnel uses Node.js Single Executable Applications (SEA) to create a standalone terminal server. However, the standard Node.js binary is quite large:
  • Standard Node.js binary: ~110MB
  • Custom minimal Node.js: ~43MB (61% reduction)
  • Final executable size: ~45MB (down from ~105MB)
  • Final app size impact: Reduces app from ~130MB to ~88MB
We don’t need many Node.js features for VibeTunnel:
  • No internationalization (ICU) support needed
  • No npm package manager in the binary
  • No inspector/debugging protocol
  • No V8 snapshots or code cache
By building a custom Node.js without these features, we achieve a significantly smaller app bundle while maintaining full functionality.

Build Behavior

Debug Mode (Xcode)

  • Uses system Node.js for faster iteration
  • No custom Node.js compilation required
  • Build output shows: "Debug build - using system Node.js for faster builds"
  • If a custom Node.js was previously built, it will be reused for consistency

Release Mode (Xcode)

  • Automatically builds custom minimal Node.js on first run
  • Compilation takes 10-20 minutes but is cached for future builds
  • Uses the custom Node.js to create a smaller executable
  • Build output shows version and size comparison

Prerequisites

Required Build Tools

For optimal build performance, the following tools are required:
  • Ninja: Build system for faster compilation (significantly faster than Make)
  • ccache: Compiler cache to speed up rebuilds

Installation

  • macOS: brew install ninja ccache
  • Linux: apt-get install ninja-build ccache (or equivalent for your distribution)
The build script will automatically use these tools if available, falling back to Make if Ninja is not found.

Build Automation

Release Builds

The release script (mac/scripts/release.sh) automatically checks for and builds custom Node.js if needed. You don’t need to manually build it before releases.

Manual Custom Node.js Build

To build the custom Node.js manually (outside of Xcode):
cd web
node build-custom-node.js --latest
This will:
  1. Download the latest Node.js source
  2. Configure it without unnecessary features
  3. Build with optimizations (-Os, -flto, etc.)
  4. Cache the result in web/.node-builds/
To use the custom Node.js for building the executable:
cd web
npm run build -- --custom-node
Or directly:
node build-native.js --custom-node

Build Process Details

Automatic Detection

The build system automatically searches for custom Node.js builds in .node-builds/ when --custom-node is passed without a path. It finds the most recent build by checking directory modification times.

Code Signing on macOS

When building the executable:
  1. The Node.js binary is injected with our JavaScript code (SEA process)
  2. The binary is stripped to remove debug symbols
  3. The executable is re-signed with an ad-hoc signature
Note: You may see a warning about “invalidating the code signature” during the strip process - this is expected and harmless since we re-sign immediately after.

Technical Details

Features Disabled

  • --without-intl - Removes internationalization support
  • --without-npm - Excludes npm from the binary
  • --without-corepack - Removes package manager wrapper
  • --without-inspector - Disables debugging protocol
  • --without-node-snapshot - Skips V8 snapshot (~2-3MB)
  • --without-node-code-cache - Skips code cache (~1-2MB)

Optimization Flags

  • -Os - Optimize for size
  • -flto - Link-time optimization
  • -ffunction-sections / -fdata-sections - Enable dead code elimination
  • -Wl,-dead_strip - Remove unused code at link time

Build Cache

Custom Node.js builds are stored in web/.node-builds/ and are excluded from git via .gitignore. The build system automatically detects and reuses existing builds.

File Locations

  • Build script: web/build-custom-node.js
  • Native executable builder: web/build-native.js
  • Xcode integration: mac/scripts/build-web-frontend.sh
  • Build output: web/.node-builds/node-v*-minimal/
  • Final executable: web/native/vibetunnel

Troubleshooting

Custom Node.js not detected

  • Ensure the build completed successfully: check for .node-builds/node-v*-minimal/out/Release/node
  • In Debug mode, the system will use custom Node.js if already built
  • In Release mode, it will build custom Node.js automatically if not present

Code signature warnings

The warning “changes being made to the file will invalidate the code signature” is expected and handled automatically. The build process re-signs the executable after all modifications.

Known Limitations

  • The custom Node.js build process takes 10-20 minutes on first run
  • Cross-compilation is not supported - you must build on the target platform
  • The custom build excludes some features that may be needed by certain npm packages
  • Native module compatibility issues may occur when mixing Node.js versions