Skip to main content

VibeTunnel NPM Package Distribution

This document explains the npm package build process, native module handling, and prebuild system for VibeTunnel.

Overview

VibeTunnel is distributed as an npm package that includes:
  • Full web server with terminal sharing capabilities
  • Native modules for terminal (PTY) and authentication (PAM) support
  • Cross-platform prebuilt binaries to avoid requiring build tools
  • Command-line tools (vibetunnel and vt)

Package Structure

vibetunnel/
├── dist/                    # Compiled server code
├── public/                  # Web interface assets
├── bin/                     # CLI entry points
│   ├── vibetunnel          # Main server executable
│   └── vt                  # Terminal wrapper command
├── node-pty/               # Vendored PTY implementation
│   ├── lib/                # TypeScript compiled code
│   └── package.json        # PTY package configuration
├── prebuilds/              # Native module prebuilt binaries
│   ├── node-pty-*          # PTY binaries for all platforms/Node versions
│   └── authenticate-pam-*  # PAM binaries for all platforms/Node versions
└── README.md               # Package documentation

Native Modules

VibeTunnel requires two native modules:

1. node-pty (Terminal Support)

  • Purpose: Provides pseudo-terminal (PTY) functionality
  • Components:
    • pty.node: Main Node.js addon for terminal operations
    • spawn-helper: macOS-only C helper binary for process spawning
  • Platforms: All (macOS, Linux)
  • Dependencies: None (vendored implementation)

2. authenticate-pam (Authentication)

  • Purpose: PAM (Pluggable Authentication Modules) integration for system authentication
  • Components:
    • authenticate_pam.node: Node.js addon for system authentication
    • node_modules/authenticate-pam/: Full module with package.json, binding.gyp, and source files
  • Platforms: Both macOS and Linux
  • Dependencies: System PAM libraries
  • Note: While macOS uses different authentication mechanisms internally (OpenDirectory), VibeTunnel attempts PAM authentication on both platforms as a fallback after SSH key authentication
  • Critical: The entire authenticate-pam module directory must be included in the npm package, not just the prebuilds

Prebuild System

Overview

We use prebuild and prebuild-install to provide precompiled native modules, eliminating the need for users to have build tools installed.

Coverage

  • Node.js versions: 20, 22, 23, 24
  • Platforms: macOS (x64, arm64), Linux (x64, arm64)
  • Total prebuilds: 24 binaries
    • node-pty: 16 binaries (macOS and Linux, all architectures)
    • authenticate-pam: 8 binaries (Linux only - macOS builds may fail due to PAM differences)

Prebuild Files

prebuilds/
├── node-pty-v1.0.0-node-v115-darwin-arm64.tar.gz
├── node-pty-v1.0.0-node-v115-darwin-x64.tar.gz
├── node-pty-v1.0.0-node-v115-linux-arm64.tar.gz
├── node-pty-v1.0.0-node-v115-linux-x64.tar.gz
├── authenticate-pam-v1.0.5-node-v115-linux-arm64.tar.gz
├── authenticate-pam-v1.0.5-node-v115-linux-x64.tar.gz
└── ... (similar for node versions 22, 23, 24, Linux only)
Note: Node version numbers map to internal versions (v115=Node 20, v127=Node 22, v131=Node 23, v134=Node 24)

Build Process

Clean Build Approach

The npm build process uses a clean distribution directory approach that follows npm best practices:
  1. Creates dist-npm/ directory - Separate from source files
  2. Generates clean package.json - Only production fields, no dev dependencies
  3. Bundles dependencies - node-pty is bundled directly, no symlinks needed
  4. Preserves source integrity - Never modifies source package.json

Unified Build (Multi-Platform by Default)

npm run build:npm
  • Compiles TypeScript and bundles client code
  • Builds native modules for all supported platforms (macOS x64/arm64, Linux x64/arm64)
  • Creates comprehensive prebuilds for zero-dependency installation
  • Generates npm README optimized for package distribution
  • Creates clean dist-npm/ directory for packaging

Build Options

The unified build script supports flexible targeting:
# Default: All platforms
npm run build:npm

# Current platform only (faster for development)
node scripts/build-npm.js --current-only

# Specific platform/architecture
node scripts/build-npm.js --platform darwin --arch arm64
node scripts/build-npm.js --platform linux

# Skip Docker (Linux builds will be skipped)
node scripts/build-npm.js --no-docker

Docker Requirements

For Linux builds, Docker is required: The build will fail with helpful error messages if Docker is not available.

Installation Process

For End Users

  1. Install package: npm install -g vibetunnel
  2. Postinstall script runs: Extracts appropriate prebuilt binaries
  3. No compilation needed: Prebuilds included for all supported platforms
  4. Result: Working VibeTunnel installation without build tools

Key Improvements

  • No symlinks: node-pty is bundled directly, avoiding postinstall symlink issues
  • Clean package structure: Only production files in the npm package
  • Reliable installation: Works in restricted environments (Docker, CI)

Installation Scripts

The package uses a simplified postinstall approach:
{
  "scripts": {
    "postinstall": "node scripts/postinstall.js"
  }
}

Postinstall Process

  • Prebuild extraction: Extracts the appropriate prebuild for the current platform
  • No downloads: All prebuilds are included in the package
  • No compilation: Everything is pre-built, no build tools required
  • Platform detection: Automatically selects correct binary based on OS and architecture

Platform-Specific Details

macOS

  • spawn-helper: Additional C binary needed for proper PTY operations (now prebuilt as universal binary)
  • Authentication: Attempts PAM authentication but may fall back to environment variables or SSH keys
  • Architecture: Supports both Intel (x64) and Apple Silicon (arm64)
  • Build tools: Not required with prebuilds; Xcode Command Line Tools only needed for source compilation fallback

Linux

  • PAM authentication: Full support via authenticate-pam module
  • PAM libraries: Requires libpam0g-dev for authenticate-pam compilation from source
  • spawn-helper: Not used on Linux (macOS-only)
  • Build tools: Not required with prebuilds; build-essential only needed for source compilation fallback

Docker Build Environment

Linux prebuilds are created using Docker with:
  • Base image: node:22-bookworm
  • Dependencies: python3 make g++ git libpam0g-dev
  • Package manager: pnpm (more reliable than npm in Docker)
  • Environment: CI=true to avoid interactive prompts

spawn-helper Binary

What is spawn-helper?

spawn-helper is a small C helper binary used by node-pty for proper terminal process spawning on macOS.

Key Facts

  • Size: ~70KB pure C binary
  • Platform: macOS only (Linux doesn’t use it)
  • Purpose: Handles terminal device attachment for spawned processes
  • Dependencies: None (pure C, no Node.js dependencies)
  • Architecture: Platform-specific (x64 vs arm64)

Source Code

// Simplified version of spawn-helper functionality
int main (int argc, char** argv) {
  char *slave_path = ttyname(STDIN_FILENO);
  close(open(slave_path, O_RDWR));  // Attach to terminal
  
  char *cwd = argv[1];
  char *file = argv[2];
  argv = &argv[2];
  
  if (strlen(cwd) && chdir(cwd) == -1) {
    _exit(1);
  }
  
  execvp(file, argv);  // Execute the target command
  return 1;
}

Installation Handling

  • Current approach: Universal spawn-helper binary included in prebuilds (macOS only)
  • Benefits: No compilation needed, faster installation, works without build tools
  • Fallback path: If prebuild fails, compilation happens automatically via node-gyp
  • Error handling: Non-fatal if missing (warns but continues)

Universal Binary Implementation

spawn-helper is now shipped as a prebuilt universal binary in all macOS prebuilds: Implementation:
  • Built for both x64 and arm64 architectures using clang++
  • Combined into universal binary with lipo
  • Included in every macOS node-pty prebuild automatically
Benefits:
  • ✅ Faster installation (no compilation needed)
  • ✅ Works without build tools (Xcode Command Line Tools)
  • ✅ Universal compatibility across Intel and Apple Silicon Macs
  • ✅ Smaller download than compiling during install
Build process:
# Build for both architectures
clang++ -arch x86_64 -o spawn-helper-x64 spawn-helper.cc
clang++ -arch arm64 -o spawn-helper-arm64 spawn-helper.cc

# Create universal binary  
lipo -create spawn-helper-x64 spawn-helper-arm64 -output spawn-helper-universal

# Include in all macOS prebuilds

Package Optimization

File Exclusions

Development artifacts are excluded from the final package:
  • Test files (public/bundle/test.js, public/test/ directory)
  • Recording files (*.cast prevented by .gitignore)
  • Build artifacts (dist/ selectively included via package.json files field)

Size Optimization

  • Final size: ~8.5 MB
  • File count: ~275 files
  • Prebuilds: Included for zero-build installation experience
  • Source code: Minimal, compiled assets only

Development Commands

Local Development

# Multi-platform build with prebuilds (default)
npm run build:npm

# Single-platform build for local testing
node scripts/build-npm.js --current-only

# Test package locally
npm pack

# Verify package contents
tar -tzf vibetunnel-*.tgz | head -20

Quality Checks

Always run before publishing:
pnpm run lint          # Check code style
pnpm run typecheck     # Verify TypeScript

Publishing

Prerequisites

  1. Update version in package.json
  2. Run multi-platform build
  3. Test package locally
  4. Verify all prebuilds are included

Publish Command

npm publish

Usage After Installation

Installation

# Install globally
npm install -g vibetunnel

Starting the Server

# Start with default settings (port 4020)
vibetunnel

# Start with custom port
vibetunnel --port 8080

# Start without authentication
vibetunnel --no-auth
Then open http://localhost:4020 in your browser to access the web interface.

Using the vt Command

# Monitor AI agents with automatic activity tracking
vt claude
vt claude --dangerously-skip-permissions

# Run commands with output visible in VibeTunnel
vt npm test
vt python script.py
vt top

# Launch interactive shell
vt --shell
vt -i

# Update session title (inside a session)
vt title "My Project"

Command Forwarding

# Basic usage
vibetunnel fwd <session-id> <command> [args...]

# Examples
vibetunnel fwd --session-id abc123 ls -la
vibetunnel fwd --session-id abc123 npm test
vibetunnel fwd --session-id abc123 python script.py

Coexistence with Mac App

The npm package works seamlessly alongside the Mac app:

Command Routing

  • The vt command from npm automatically detects if the Mac app is installed
  • If Mac app found at /Applications/VibeTunnel.app, npm vt defers to it
  • Ensures you always get the best available implementation

Installation Behavior

  • Won’t overwrite existing /usr/local/bin/vt from other tools
  • Provides helpful warnings if conflicts exist
  • Installation always succeeds, even if vt symlink can’t be created
  • Use vibetunnel or npx vt as alternatives

Build Process Validation

Ensuring authenticate-pam Module is Included

Critical: The authenticate-pam module must be copied to the npm package during build. This was missing in beta 14 due to a hardcoded pnpm path issue.

What to Check Before Publishing

  1. Verify authenticate-pam is installed:
    # Check if the module exists (may be a symlink)
    ls -la node_modules/authenticate-pam/
    
  2. Run the build and check output:
    npm run build:npm
    # Look for: "✅ authenticate-pam module copied to dist-npm for Linux PAM auth"
    # If you see: "⚠️ authenticate-pam source not found", the module won't be included
    
  3. Verify in the built package:
    # Check if authenticate-pam was copied
    ls -la dist-npm/node_modules/authenticate-pam/
    # Should contain: package.json, binding.gyp, authenticate_pam.cc, etc.
    
  4. Test the package contents:
    # Create package and verify
    cd dist-npm && npm pack
    tar -tzf vibetunnel-*.tgz | grep authenticate-pam
    # Should show: package/node_modules/authenticate-pam/...
    

How the Build Script Works

The copyAuthenticatePam() function in scripts/build-npm.js:
  • Searches multiple possible locations for the module (direct node_modules, pnpm structures)
  • Uses fs.statSync() to properly follow symlinks
  • Logs which path was found or lists all searched paths if not found
  • Copies the entire module directory to dist-npm/node_modules/authenticate-pam/

If authenticate-pam is Missing

  1. Ensure it’s installed: Run pnpm install to install all dependencies
  2. Check for optional dependency issues: authenticate-pam is listed as a regular dependency, not optional
  3. Verify pnpm didn’t clean it: Sometimes pnpm removes unused modules during cleanup
  4. Force reinstall if needed: pnpm install authenticate-pam

Troubleshooting

Common Issues

Missing Build Tools

Error: gyp ERR! stack Error: not found: make Solution: Install build tools:
  • macOS: xcode-select --install
  • Linux: apt-get install build-essential

Missing PAM Development Libraries

Error: fatal error: security/pam_appl.h: No such file or directory Solution: Install PAM development libraries:
  • Linux: apt-get install libpam0g-dev
  • macOS: Usually available by default

Docker Not Available

Error: Docker is required for multi-platform builds Solution: Install Docker using OrbStack or Docker Desktop

Prebuild Download Failures

Error: prebuild-install warn install No prebuilt binaries found Cause: Network issues or unsupported platform/Node version Result: Automatic fallback to source compilation

npm_config_prefix Conflict with NVM

Error: Global npm installs fail or install to wrong location when using NVM Symptoms:
  • npm install -g installs packages to system location instead of NVM directory
  • Command not found errors after global install
  • Permission errors during global installation
Cause: The npm_config_prefix environment variable overrides NVM’s per-version npm configuration Detection: VibeTunnel’s postinstall script will warn if this conflict is detected:
⚠️  Detected npm_config_prefix conflict with NVM
   npm_config_prefix: /usr/local
   NVM Node path: /home/user/.nvm/versions/node/v20.19.4/bin/node
   This may cause npm global installs to fail or install in wrong location.
Solution: Unset the conflicting environment variable:
unset npm_config_prefix
Permanent fix: Remove or comment out npm_config_prefix settings in:
  • ~/.bashrc
  • ~/.bash_profile
  • ~/.profile
  • /etc/profile
  • CI/CD environment configurations
Common sources of this issue:
  • Previous system-wide npm installations
  • Docker containers with npm pre-installed
  • CI/CD environments with global npm configuration
  • Package managers that set global npm prefix

Debugging Installation

# Verbose npm install
npm install -g vibetunnel --verbose

# Check prebuild availability
npx prebuild-install --list

# Force source compilation
npm install -g vibetunnel --build-from-source

Architecture Decisions

Why Prebuilds?

  • User experience: No build tools required for most users
  • Installation speed: Pre-compiled binaries install much faster
  • Reliability: Eliminates compilation errors in user environments
  • Cross-platform: Supports all target platforms without user setup

Why Docker for Linux Builds?

  • Cross-compilation: Build Linux binaries from macOS development machine
  • Consistency: Reproducible build environment
  • Dependencies: Proper PAM library versions for Linux

Why Vendored node-pty?

  • Control: Custom modifications for VibeTunnel’s needs
  • Reliability: Avoid external dependency issues
  • Optimization: Minimal implementation without unnecessary features
  • scripts/build-npm.js - Unified npm build process with multi-platform support
  • scripts/postinstall-npm.js - Fallback compilation logic
  • .prebuildrc - Prebuild configuration for target platforms
  • package.json - Package configuration and file inclusions

Release Notes

Version 1.0.0-beta.14.1 (2025-07-21)

Published to npm: Successfully published as both vibetunnel@beta and vibetunnel@latest Critical Fix:
  • Fixed missing authenticate-pam module that was excluded from the npm package in beta.14
  • The build script now properly detects authenticate-pam in various pnpm directory structures
Package Details:
  • Package size: 14.8 MB (34.4 MB unpacked)
  • Contains 234 files including all prebuilds and web assets
  • Includes all 24 prebuilds (16 node-pty + 8 authenticate-pam)
  • authenticate-pam module is now properly bundled in node_modules/authenticate-pam/
Installation:
# Install latest (now 1.0.0-beta.14.1 with the fix)
npm install -g vibetunnel

# Or install beta specifically
npm install -g vibetunnel@beta

# Or install specific version
npm install -g vibetunnel@1.0.0-beta.14.1
Build Script Improvements:
  • Enhanced copyAuthenticatePam() function to search multiple pnpm locations
  • Added comprehensive logging to track which paths are searched
  • Properly follows symlinks with fs.statSync() for accurate module detection
Verification: The build now includes clear output confirming authenticate-pam inclusion:
✅ authenticate-pam module copied to dist-npm for Linux PAM auth

Version 1.0.0-beta.13 (2025-07-19)

Published to npm: Successfully published as both vibetunnel@beta and vibetunnel@latest Key Features:
  • All features from previous releases maintained
  • Updated to match macOS app version 1.0.0-beta.13
  • Full cross-platform support with prebuilt binaries
  • Zero-dependency installation experience
Package Details:
  • Package size: 15.5 MB (37.1 MB unpacked)
  • Contains 235 files including all prebuilds and web assets
  • Includes prebuilds for Node.js 20, 22, 23, and 24
Installation:
# Install latest (now 1.0.0-beta.13)
npm install -g vibetunnel

# Or install beta specifically
npm install -g vibetunnel@beta

Version 1.0.0-beta.11 (2025-07-16)

Published to npm: Successfully published as vibetunnel@beta Key Features:
  • Cross-platform support for macOS (x64, arm64) and Linux (x64, arm64)
  • Pre-built native binaries for Node.js versions 20, 22, 23, and 24
  • Zero-dependency installation experience (no build tools required)
  • Comprehensive prebuild system with 24 total binaries included
Release Process Learnings:
  1. Version Synchronization:
    • Must update version in both web/package.json and mac/VibeTunnel/version.xcconfig
    • Build process validates version sync to prevent mismatches
    • Version mismatch will cause build failure with clear error message
  2. NPM Publishing Requirements:
    • Beta versions require --tag beta flag when publishing
    • Previously published versions cannot be overwritten (must increment version)
    • Use --access public flag for public package publishing
  3. Package Build Process:
    • pnpm run build:npm creates the complete package with all prebuilds
    • Build output filename may show older version in logs but creates correct package
    • Always verify package version in dist-npm/package.json before publishing
  4. Docker Testing Verification:
    • Successfully tested on Ubuntu 22.04 (both ARM64 and x64 architectures)
    • Installation works without any build tools installed
    • Server starts correctly with all expected functionality
    • HTTP endpoints respond properly
  5. Package Structure:
    • Final package size: 8.3 MB (24.9 MB unpacked)
    • Contains 198 files including all prebuilds and web assets
    • Proper postinstall script ensures seamless installation
Installation:
npm install -g vibetunnel@beta
Testing Commands Used:
# Build the package
cd web && pnpm run build:npm

# Verify package contents
tar -tzf vibetunnel-1.0.0-beta.11.tgz | head -50

# Test with Docker
docker build -t vibetunnel-test .
docker run --rm vibetunnel-test

# Test cross-platform
docker run --rm --platform linux/amd64 vibetunnel-test

Version History

  • 1.0.0-beta.14.1 (2025-07-21): Fixed authenticate-pam module missing from npm package (patch release)
  • 1.0.0-beta.14 (2025-07-21): macOS app release (npm package had missing authenticate-pam module)
  • 1.0.0-beta.13 (2025-07-19): Synchronized with macOS app version
  • 1.0.0-beta.12.1 (2025-07-17): Minor updates and fixes
  • 1.0.0-beta.12 (2025-07-17): Package structure improvements
  • 1.0.0-beta.11.1 (2025-07-16): Fixed npm installation issues
  • 1.0.0-beta.11 (2025-07-16): Initial release with full prebuild system
  • 1.0.0-beta.10 (2025-07-14): Previous version (unpublished)

NPM Distribution Tags

VibeTunnel uses npm dist-tags to manage different release channels:

Current Tags

  • latest: Points to the most stable release (currently 1.0.0-beta.14.1)
  • beta: Points to the latest beta release (currently 1.0.0-beta.14.1)

Managing Tags

# View current tags
npm dist-tag ls vibetunnel

# Set a version as latest
npm dist-tag add vibetunnel@1.0.0-beta.13 latest

# Add a new tag
npm dist-tag add vibetunnel@1.0.0-beta.14 next

# Remove a tag
npm dist-tag rm vibetunnel next

Installation by Tag

# Install latest stable (default)
npm install -g vibetunnel

# Install specific tag
npm install -g vibetunnel@beta
npm install -g vibetunnel@latest

# Install specific version
npm install -g vibetunnel@1.0.0-beta.11.1

Best Practices

  • Always tag beta releases with beta tag
  • Only promote to latest after testing confirms stability
  • Use semantic versioning for beta iterations (e.g., 1.0.0-beta.11.1, 1.0.0-beta.11.2)
I