- CI: rename Test step to Unit tests
- README: add Testing and linting subsection, link to Contributing
- CONTRIBUTING: expand Testing with test layout table and CI note
- ARCHITECTURE: add Testing section
- INSTALLATION: add npm test to build-from-source steps
- Add unit tests (hostname-validator, TLDs, payload-schemas) and integration tests for message handler registry
- Refactor native host message router into handler registry (handlers/state, tunnels, ssh, rdp, backup, ca, connections)
- Add ESLint config and npm test + lint steps in CI
- Dashboard: visibility-based refresh pause, configurable refresh interval (2s/5s/10s/paused)
- Accessibility: ARIA on nav and modals, focus trap and restore, prefers-reduced-motion
- Empty states: primary action buttons for virtual hosts, servers, service tunnels
- Native host rate limiting for backup and CA operations; update SECURITY.md
- CONTRIBUTING: "Adding a new dashboard page", dev workflow; add npm run dev script
- Trigger getState + applyPAC after successful restoreBackup (same as setVirtualHost/removeVirtualHost)
- Always re-apply PAC when getState returns state so restored/different vhost TLDs are reflected
- Add kill-previous-instance.mjs with PID lock file under BASE_DIR; on
startup send SIGTERM to any existing native-host before binding ports
- Register removePidFile on shutdown and exit
- Run "stop any running native host" at the start of install.sh and
install.ps1
- Use ESM with static imports so bare-pack includes the module in the
bundle (dynamic require was not included and broke the built binary)
- Add kill-previous-instance.mjs to CI syntax check
- Add kill-previous-instance.js with PID lock file under BASE_DIR
- On startup, send SIGTERM to any existing native-host process before binding ports
- Register removePidFile on shutdown and exit so the lock is cleared
- Run "stop any running native host" at the start of install.sh and install.ps1
- Add kill-previous-instance.js to CI syntax check
- CONTRIBUTING: use holepunchto/bare repo URL
- NATIVE-HOST: getState servers use url (not hsUrl), no state field
- BACKUP/NATIVE-HOST: backup filenames use holesail-backup- prefix and _ timestamp
- NATIVE-HOST: document createBackup/listBackups path and createdAt
- SSH: clarify that saved password is stored as passwordB64 in state.json
- ARCHITECTURE: note extra state.json settings in SETTINGS_DEFAULTS
Write all path variants for every .json in the bundle when building
for win32 (backslash, with/without leading slash) so bundle.read()
returns content regardless of runtime key normalization. Refill empty
or invalid JSON from alternate key or disk before writing variants.
Explicitly write tt-native package.json under Windows path variants
in Fix 1. Document in CHANGELOG.
Use forward-slash addon resolution key for all platforms in the
distributable build so the runtime finds the tt-native prebuild when
the bundle normalizes paths on Windows. Update CHANGELOG and
CONTRIBUTING to document the fix.
After unmount, the runtime may resolve #package with or without a
leading slash. Write the same content to both keys so the .json
loader never gets an empty entry and the host no longer crashes with
"Unexpected end of JSON input" on Windows.
patchBundle so the runtime never gets empty content for that key
(fixes "Unexpected end of JSON input" in Module._extensions..json
when the host is started by the extension on Windows).
- State: treat empty or whitespace-only state.json (and legacy
persist file) as missing and return default state instead of
throwing in JSON.parse.
- State: add ensureStorageDir() and call it from message-router
after setStoragePath so the storage directory exists on fresh
install before any state is loaded or saved.
- SECURITY.md: Note that we install to system store and run
update-ca-certificates; add that Chrome/Firefox may not use it.
- Add step-by-step instructions to manually import the CA into
Chrome/Chromium and Firefox on Linux (path to ca.cert.pem).
- INSTALLATION.md: In first-time setup, add Linux note and link
to SECURITY.md for manual import.
- Replace sudo with pkexec so PolicyKit shows a graphical auth dialog
when the native host is launched by the browser (no terminal).
- Pass DISPLAY and XAUTHORITY into the pkexec env so the polkit
agent can display the dialog in the current session.
- Keep copy + update-ca-certificates in a single pkexec sh -c for
one password prompt.
- Update SECURITY.md and JSDoc to describe pkexec / PolicyKit.
When building with --all, the bundle used a single addon resolution (darwin-arm64),
so the Linux binary tried to load the wrong prebuild and tt-native failed. Patch
tt-native binding with a nested resolution map (addon → bare → node → platform →
arch) so the runtime selects the correct prebuild per platform; SSH PTY then works
on Linux.
- scripts/build-distributable.js: build host-specific addonResolutions, write
binding with full map; update comments
- docs/ARCHITECTURE.md, CONTRIBUTING.md: document addon resolution patch
- CHANGELOG.md: add entry under bug fixes
When building with --all, the bundle used a single addon resolution (darwin-arm64),
so the Linux binary tried to load the wrong prebuild and tt-native failed. Patch
tt-native binding with a nested resolution map (addon → bare → node → platform →
arch) so the runtime selects the correct prebuild per platform; SSH PTY then works
on Linux.
- Move connect-proxy and https-proxy into proxy/
- Move certificate-authority, backup-manager, ssh-manager, rdp-manager into managers/
- Move messenger.js into host/
- Move test-dirname.cjs into test/
- Update imports, CI lint paths, and ARCHITECTURE.md
- Import host/message-router.js and holesail-manager/index.js directly
- Delete host.js and holesail-manager.js
- Drop shim entries from CI syntax checks
- Update ARCHITECTURE.md diagram and file table
- Add "Use TLS (secure connection)" checkbox in Add Virtual Host modal
- Persist and restore useTls in state; show TLS badge in table
- When enabled, HTTPS proxy connects to tunnel backend over TLS (SNI =
hostname) for HTTP and WebSocket; supports services on port 443
- CHANGELOG: add Firefox support subsection (manifest_firefox, background
scripts, proxy/native-messaging/CSP, installer, pack, extension-id)
- CONTRIBUTING: repo layout with background-boot/main, Firefox load steps
and private-windows note, install.sh Chrome vs Firefox manifests,
DEBUG_VERBOSE in background-boot.js
- ARCHITECTURE: manifest.json vs manifest_firefox.json, background.js
importScripts, background-boot/main and proxy/native-messaging notes,
native host lifecycle and separate manifests
- INSTALLATION: allowed_origins vs allowed_extensions, Allow in Private
Windows for Firefox, extension ID updates both manifests
- SECURITY: PAC scope Chrome vs Firefox (autoConfig/data URL), proxy
security and private-windows requirement
Firefox's default MV3 CSP includes upgrade-insecure-requests, which upgrades
ws://127.0.0.1 to wss:// and breaks SSH/RDP WebSocket connections to the
local native host. Add explicit content_security_policy to manifest_firefox.json
allowing ws://127.0.0.1:* and http://127.0.0.1:* on extension pages.
- Firefox rejects native messaging manifest if allowed_origins is present.
Install script now writes Chrome-only manifest (allowed_origins) and
Firefox-only manifest (allowed_extensions) to their respective locations.
- Fix local Firefox manifest by removing allowed_origins.
- Log disconnect reason from port.error (Firefox) in addition to
runtime.lastError (Chrome) so native messaging failures are visible.
- Log getState failures on disconnect for easier debugging.
- Treat PAC as active when mode === 'pac_script' (Chrome) or proxyType ===
'autoConfig' (Firefox) so we don't mis-detect "not active" in Firefox.
- In proxy.settings.onChange, only call applyPAC() when levelOfControl !==
'controlled_by_this_extension'. When we still have control, return without
re-applying to stop the set -> onChange -> applyPAC loop.
Firefox's match pattern validator does not allow the moz-extension:// scheme
in matches. Use extension_ids: ["*"] so extension pages can load the
resources without invalid manifest error.
Firefox MV3 does not support background.service_worker. Split background
into background-boot.js (globals) and background-main.js (startup logic);
Chrome keeps using importScripts() in background.js, Firefox manifest uses
background.scripts with the same file list so the extension loads in both.