Add docs/CONTRIBUTING.md covering the build system, dev workflow, all npm scripts, how to add new native host message types, code style, and debugging guidance. Add CHANGELOG.md at the project root documenting all features and fixes across the 1.0.0 release. Add JSDoc (@param, @returns) to all previously undocumented exported functions across 35 JS files: - native-host/holesail-manager/ (index, virtual-hosts, service-tunnels, servers, port-allocator) - native-host top-level managers (startup, connect-proxy, https-proxy, certificate-authority, ssh-manager, rdp-manager) - extension/background/ (logs, native-messaging, proxy, message-router) - extension/dashboard/core/ (utils, navigation, init) - extension/dashboard/ui/ (modal, toast, state-tag) - extension/dashboard/pages/ (all 10 page files) - extension/dashboard/refresh.js, events.js - extension/dashboard/data/hostname-validator.js - scripts/ (build-host, run-install)
8.7 KiB
Contributing
This document covers the development workflow, build system, project structure, and how to extend the codebase.
Prerequisites
- Node.js v18 or later (for build scripts and dev tooling)
- Bare runtime — the native host runs under Bare, not Node.js
- A Chromium-based browser or Firefox for testing the extension
Install root-level dev dependencies:
npm install
Install native host dependencies:
cd native-host && npm install
Repository layout
Holesail-Browser/
├── extension/ # Browser extension (MV3, Chrome + Firefox)
│ ├── background.js # Service worker entry point
│ ├── manifest.json # Extension manifest
│ ├── background/ # Background module files
│ └── dashboard/ # Dashboard UI (HTML + JS modules)
├── native-host/ # Native host process (runs under Bare runtime)
│ ├── index.mjs # Entry point
│ ├── host/ # Startup, routing, logging, paths
│ ├── holesail-manager/ # Tunnel lifecycle sub-modules
│ └── ... # Proxy, CA, SSH, RDP, backup managers
├── scripts/ # Build and install scripts (Node.js)
├── docs/ # Documentation
├── releases/ # Build output (generated, not committed)
└── package.json # Root build scripts
Running from source
1. Build the native host launcher
npm run build:host
This runs scripts/build-host.js, which generates native-host/holesail-browser-host — a small shell script that invokes bare native-host/index.mjs. The script auto-detects the bare binary location.
2. Install (register the native messaging host)
npm run setup
This runs scripts/run-install.js, which delegates to scripts/install.sh (macOS/Linux) or scripts/install.ps1 (Windows). The installer:
- Copies the native host files to
~/.holesail-browser/ - Writes the native messaging manifest (
com.holesail.browser.json) to the OS-specific location Chrome/Firefox reads - Generates a unique extension ID via
scripts/generate-extension-id.js
3. Load the extension in Chrome
- Open
chrome://extensions - Enable Developer mode
- Click Load unpacked and select the
extension/directory
4. Load the extension in Firefox
- Open
about:debugging#/runtime/this-firefox - Click Load Temporary Add-on
- Select
extension/manifest.json
npm scripts reference
| Script | Command | Description |
|---|---|---|
npm run setup |
node scripts/run-install.js |
Install native host and register native messaging |
npm run build:host |
node scripts/build-host.js |
Generate the holesail-browser-host launcher script |
npm run build |
alias for build:host |
Default build |
npm run pack |
node scripts/pack-extension.js |
Pack extension into .zip and .xpi for distribution |
npm run build:dist |
node scripts/build-distributable.js |
Build standalone binary for the current host platform |
npm run build:dist:all |
...--all |
Build standalone binaries for all 5 platforms |
npm run build:dist:mac |
...--host darwin-arm64 --host darwin-x64 |
macOS only |
npm run build:dist:linux |
...--host linux-arm64 --host linux-x64 |
Linux only |
npm run build:dist:win |
...--host win32-x64 |
Windows only |
npm run build:dist:package |
...--all --package |
All platforms + zip archives |
Scripts in detail
scripts/build-host.js
Generates native-host/holesail-browser-host, a bash launcher that invokes bare index.mjs. Searches for the bare binary via which bare, then common Homebrew paths, then falls back to a sibling of the current node binary.
scripts/build-distributable.js
Produces self-contained native host binaries using the bare-pack + bare-build pipeline:
bare-packbundles the entire JS module graph (with optional-dep stubs) into a single.bundlefilebare-buildembeds the bundle into a pre-built Bare runtime binary for the target platform
Output goes to releases/<platform>/holesail-browser-host[.exe]. Supported targets: darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-x64.
scripts/pack-extension.js
Reads the version from extension/manifest.json and creates releases/Holesail-Browser-<version>.zip (Chrome) and .xpi (Firefox) by archiving the extension/ directory, excluding .map files.
scripts/generate-extension-id.js
Generates a unique Chrome extension ID (RSA-2048 public key → SHA-256 → first 16 bytes → base-26 a–p encoding) and a unique Firefox ID (holesail-browser-<8 random hex bytes>@example.org). Updates both extension/manifest.json and com.holesail.browser.json. Run automatically by the installer so each installation gets its own ID.
scripts/run-install.js
Cross-platform launcher: spawns install.sh on Unix or install.ps1 on Windows via PowerShell with -ExecutionPolicy Bypass. Inherits stdio and forwards the exit code.
scripts/install.sh / scripts/install.ps1
Full install scripts that:
- Detect platform and architecture
- Download or copy the native host binary to
~/.holesail-browser/ - Write the native messaging manifest to the correct OS path
- Optionally download the extension package
Adding a new native host message type
The extension communicates with the native host via typed JSON messages. To add a new command:
1. Add the handler in native-host/host/message-router.js
case 'myNewCommand': {
debugLog('myNewCommand: payload=', JSON.stringify(payload));
const result = await someManager.doSomething(payload);
reply(result);
break;
}
All handlers receive payload (the message payload object) and call reply(result) to send the response back to the extension. Async handlers must await before calling reply.
2. Implement the logic in the appropriate manager
- Tunnel operations →
native-host/holesail-manager/ - SSH sessions →
native-host/ssh-manager.js - RDP/VNC sessions →
native-host/rdp-manager.js - Certificates →
native-host/certificate-authority.js - Backups →
native-host/backup-manager.js
3. Call the command from the extension
In any dashboard JS file, use sendToNative:
const result = await sendToNative('myNewCommand', { someParam: 'value' });
if (result && result.ok) {
// handle success
}
Or via chrome.runtime.sendMessage directly from the background:
chrome.runtime.sendMessage({
target: 'holesail-native',
action: 'send',
payload: { type: 'myNewCommand', payload: { someParam: 'value' } }
}, (response) => { void chrome.runtime.lastError; /* ... */ });
4. Document the new message type in docs/NATIVE-HOST.md
Add an entry under the Commands section with the request payload shape, response shape, and any notes on optional fields.
Code style
- No build step for the extension — the dashboard JS files are loaded directly by the browser in dependency order (see the
<script>tags at the bottom ofdashboard.html). There is no bundler or transpiler for the extension. - Native host uses CommonJS (
require/module.exports) with the exception of the entry pointindex.mjswhich uses staticimportfor Bare compatibility. - JSDoc style — use
/** ... */blocks with@param {type} name,@returns {type}, and@throws {Error}tags where relevant. - Error handling — all async functions should catch errors and return
{ ok: false, error: e.message }rather than throwing, so the extension always gets a structured response. - Timers and listeners — always store timer IDs and remove event listeners in cleanup paths to avoid leaks (see
docs/ARCHITECTURE.mdfor the resource management patterns used throughout).
Testing
There is currently no automated test suite. Manual testing workflow:
- Make changes to the native host source
- Run
npm run build:hostto regenerate the launcher - Reload the extension in the browser (
chrome://extensions→ reload button) - The native host process is restarted automatically on the next message from the extension
For distributable builds, run npm run build:dist and replace the binary in ~/.holesail-browser/.
Debugging
Native host logs
The native host writes logs to ~/.holesail-browser/holesail-browser.log (macOS/Linux) or %APPDATA%\holesail-browser\holesail-browser.log (Windows). Set DEBUG_VERBOSE = true in extension/background.js to enable verbose logging from the extension side.
Extension background logs
Open the service worker DevTools from chrome://extensions → Inspect views: service worker.
Dashboard logs
The Logs page in the dashboard shows all native host log output in real time.