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.
- Add extension/manifest_firefox.json (no key, moz-extension://*/* for
web_accessible_resources) for correct Firefox MV3 behavior
- Update pack-extension.js: zip excludes manifest_firefox.json; XPI
built separately with manifest_firefox.json as manifest.json
- Update generate-extension-id.js to set gecko.id in manifest_firefox.json
when present (no key in Firefox manifest)
- Document dual-manifest layout and pack/generate-id behavior in CONTRIBUTING.md
Use var(--bg) for btn-primary text color and color-mix for hover to
correctly adapt across themes. Add --terminal-bg CSS variable and
_getXtermTheme() helper so the SSH terminal picks up the correct
palette at connection time.
Normalize loadAvg to [0,0,0] fallback in both the native host handler
and the dashboard renderer to prevent TypeError when bare-node-os returns
a non-array value for loadavg().
- Add getProcessStats message handler to native host returning process
memory (rss, heap used/total, external), PID, uptime, OS memory,
CPU load averages, core count, platform/arch, and tunnel state counts
- Add stats-tracker.js to background SW tracking cumulative connections,
reconnect count, last disconnect time, and per-minute connection rate
ring buffer (30 buckets) for sparkline display
- Include statsHistory and activeConnections array in all getState responses
- Add Statistics dashboard page with six sections: Native Host Process,
System Resources, Tunnel Health, Proxy Traffic, Peer Connections
(with SVG sparkline), and Extension & Proxy Info
- Preserve checkbox selection across table re-renders on all bulk-action
tables (virtual hosts, servers, service tunnels)
- Add theme-aware custom checkbox styling for bulk selection rows using
design system tokens, with white checkmark override for light theme
Replace native browser checkbox appearance on table row and select-all
checkboxes with fully custom styled controls using design system tokens,
with a white checkmark override for the light theme's darker cyan.
Snapshot checked row IDs before each innerHTML swap in the virtual hosts,
service tunnels, and server tunnels tables so that periodic refresh cycles
no longer reset multi-selection state.
chrome.permissions.request() must be called synchronously within a user
gesture. Moving it from the background message-router (where the gesture
context has expired) into the addVhostSubmit click handler in virtual-hosts.js
fixes the "Unchecked runtime.lastError: This function must be called during
a user gesture" console error.
Most modern SSH servers disable plain password auth and only allow
keyboard-interactive. Excluding it from PreferredAuthentications caused
SSH to exhaust all auth methods and exit immediately.
Also scope SSH_ASKPASS_REQUIRE=force to only apply when a saved password
is provided. Setting it unconditionally broke keyboard-interactive auth
since that method ignores SSH_ASKPASS and reads from the PTY directly.
Reverts the experimental feature that attempted to serve the dashboard
via a local HTTP server registered as a hardcoded `my.dash.board` virtual
host, intended to satisfy Chrome's PWA "secure origin" installability
requirement. The approach caused proxy connection errors and file-not-found
issues that were not worth resolving.
Removed: native-host/dashboard-server.js, native-host/test-chain.mjs
Reverted: startup.js, message-router.js, virtual-hosts.js, index.js,
build-distributable.js, extension/pages/virtual-hosts.js,
CI workflows (ci.yml, release.yml)
Pass a proper EventEmitter as the server argument to bare-http1
ServerConnection in dashboard-server.js. The constructor signature is
(server, socket, opts) — passing the socket as the first argument caused
immediate connection resets because ServerConnection emitted 'request' on
the socket object instead of a handler, silently dropping all requests.
Fix "Proxy error: connection reset by peer" caused by dashboard-server.js
looking for files in native-host/ instead of extension/dashboard/. Remove
the devBasePath parameter and resolve DEV_DASHBOARD_DIR directly from
__dirname so dev and distribution mode both find the correct files.
Fix all manifest warnings (start_url/id/scope/protocol_handlers ignored)
by reverting manifest.webmanifest to relative/extension-origin URLs for
the chrome-extension:// context, and having dashboard-server.js dynamically
generate a manifest at /manifest.webmanifest with absolute
https://my.dash.board/ URLs — satisfying Chrome's same-origin requirement
for PWA installability.
Also serve extension icons at https://my.dash.board/icons/<file> with
correct bundle key mapping, and embed them in the distributable binary
via build-distributable.js.
Serve the dashboard over a local HTTPS virtual host (my.dash.board) so
Chrome treats it as a secure origin and shows the PWA install prompt.
- Add native-host/dashboard-server.js: bare-http1 static file server
backed by bare-bundle assets in distribution mode, disk fallback in dev
- Add setLocalVirtualHost() to virtual-hosts.js; type:local entries are
protected from removal and filtered out of saveState persistence
- Export setLocalVirtualHost from holesail-manager/index.js
- Start dashboard server in startup.js after proxies are ready and
register my.dash.board as a local virtual host
- Stop dashboard server in message-router.js cleanup()
- Update manifest.webmanifest: start_url, id, scope → https://my.dash.board/
- Embed all extension/dashboard/ files as bare-bundle assets in
build-distributable.js patchBundle() — no installer copy step needed
- Hide checkbox, hs:// URL, Reconnect, and Remove controls for built-in
my.dash.board row in the virtual hosts table UI
Read runtime.lastError in the onDisconnect callback so Chrome does not
log "Unchecked runtime.lastError: Native host has exited" to the
extensions console. The disconnect reason is forwarded to debugLog so
it remains visible when Debug Mode is enabled.
Chrome warns that an empty fetch listener adds navigation overhead.
Removed the handler entirely — a registered SW without a fetch handler
still satisfies Chrome's PWA installability requirement since v112.
The inline <script> for service worker registration was blocked by the
extension's script-src 'self' Content Security Policy. Extracted it to
register-sw.js and replaced the inline block with a <script src>.
Added register-sw.js to web_accessible_resources.
Add ov-empty CSS class to .ov-scroll-area when an overview list has no
items — disables overflow scrolling and uses flexbox to vertically and
horizontally center the empty message. Class is removed automatically
when data is present. Keeps the normal scrollable layout intact for
populated lists.
The Root CA card and Install CA modal incorrectly stated the CA only
covers *.hole.sail domains. Updated both to accurately describe that
the CA covers all virtual host domains using any private TLD
(e.g. myapp.hole.sail, api.my.internal).
Add a minimal sw.js with an empty fetch handler — required by Chrome
for the PWA install prompt to appear. Register it at the bottom of
dashboard.html with a silent catch. Add "scope": "." to the manifest.
Register sw.js in web_accessible_resources so Chrome can fetch it from
the extension origin.
- Fix icon purpose from "any maskable" to "any" to avoid incorrect
padding on some platforms
- Add explicit id field matching start_url to stabilise the App ID
- Add display_override with window-controls-overlay for native title bar
- Register web+holesail:// protocol handler so installed app can be
launched directly from custom protocol links
Add wide (1024x515) and narrow (390x844) screenshots to the web app
manifest so Chrome displays the richer install UI with a preview on
both desktop and mobile. Register screenshots directory in
web_accessible_resources so Chrome can fetch the images from the
extension origin.
Replace all extension icons (16, 32, 48, 128, 192, 512) with versions
derived from the new 512x512 brand icon for consistency. Add 192x192
and 512x512 entries to manifest.webmanifest — Chrome requires both
sizes to show the "Install page as app" option in the browser menu.
Mark the 512px icon as maskable for OS launcher compatibility.
Add manifest.webmanifest to the dashboard so Chrome can install it as
a standalone app via the browser menu. Register the manifest in
web_accessible_resources and link it from dashboard.html. Add a
dedicated PWA install section to docs/INSTALLATION.md covering install
steps, uninstall, and the requirement that the extension remains
installed.
"Sending to native" and "Received from native" are emitted on every
poll cycle and clutter the logs panel. Moved both to debugLog so they
only appear when Debug Mode is enabled in Settings.
- Default latencyPingEnabled to false so badges are hidden until opted in
- Fix toggleLatencyPing sync condition from !== false to === true to match
the new default correctly
- Default tunnelAutoReconnect to true so tunnels reconnect automatically
out of the box
- Hide latency badges in virtual hosts and service tunnels tables when
latency ping is disabled
Add latencyPingEnabled and latencyPingIntervalMs to the native host
SETTINGS_DEFAULTS and updateSettings handler so they are persisted to
state.json and returned in the save response. Previously these fields
were extension-only and were silently dropped, causing the UI to revert
to defaults after every save.
Track a _settingsDirty flag that is set whenever the user interacts with
a settings toggle or number input. updateSettingsUI() skips the sync while
dirty so the 2-second refresh cycle cannot revert in-progress edits.
The flag is cleared after a successful save or reset.
- Snapshot and restore latency badge values across innerHTML re-renders
so badges never flash back to "…ms" on the 2-second state refresh cycle
- Add CSS transition (color 0.4s ease) and in-flight opacity dim (.pinging)
to .latency-badge for smooth color changes
- Decouple latency pinging from the state refresh cycle — pings now run on
their own independent interval instead of being triggered by refresh()
- Add two new settings under a "Latency Ping" section in Settings:
- Enable Latency Ping toggle (latencyPingEnabled, default: true)
- Ping Interval in seconds (latencyPingIntervalMs, default: 5s)
- Restart ping interval immediately on settings save so changes take effect
without a page reload
Replace the inline result div with a dedicated modal that displays
structured lookup data (host, port, protocol, private) returned by
Holesail.lookup(). Shows a loading state while the request is in
flight, and renders clear error/no-record states when the key is
not found or the lookup fails.
CRITICAL:
- certificate-authority.js: declare `regenerated` variable in installRootCA Windows path to prevent ReferenceError crash
HIGH:
- virtual-hosts.js/service-tunnels.js: call hs.removeAllListeners() in catch blocks to prevent stale listeners on failed Holesail instances
- https-proxy.js: destroy rawSocket in TLS error handler to prevent file descriptor exhaustion
- message-router.js (native): move setEventEmitter() to module-level init instead of re-calling on every message
- ssh-manager.js: add error handler to WS server to prevent unhandled error crashes
- init.js: store setInterval ID and clear on beforeunload to prevent interval accumulation
- logs.js: store and remove chrome.runtime.onMessage listener on beforeunload; add duplicate-call guard
- events.js: move pending++ before async sendMessage call to fix SSH/RDP-only import showing "Nothing to import"
- ssh.js: store resizeTimer on activeSshSession and clear in disconnectSsh; fix auto-reconnect race with _sshConnecting lock
- native-messaging.js: track retry timer IDs in array and cancel all on disconnect
- rdp.js: reuse single offscreen canvas per session instead of allocating per bitmap
MEDIUM:
- virtual-hosts.js/service-tunnels.js: clear existing.reconnectTimer before replacing tunnel entries
- message-router.js (native): destroy pingTunnel socket on error path; clear 15s fallback timer via finally()
- startup.js: wrap setImmediate body in try/finally to always resolve proxiesReadyPromise
- https-proxy.js: fix pre-connect upstream error handler to avoid writing raw HTTP into piped TLS stream; move HOP_BY_HOP to module-level constant
- connect-proxy.js: destroy upstreamSocket on clientSocket close; track and destroy active sockets in stop()
- ssh-manager.js: call cancelPasswordWatch on WS disconnect during password collection
- rdp-manager.js: remove dead remotePort variable; add error handlers to both WS servers
- backup-manager.js: log cleanupStaging errors and non-zero exit codes
- rdp.js: null out ws callbacks before closing in disconnectRdp; disconnect MutationObserver on beforeunload
- proxy-ca.js: prune stale entries from validationResults Map in renderValidatorTable
- refresh.js: deduplicate in-flight pings per port via Set
- messaging.js: read chrome.runtime.lastError in sendToNative callback
- servers.js (dashboard): add null check for $('serverEditId') element
LOW:
- port-allocator.js: add dedup check before pushing to tunnelPortFreeList
- servers.js (native): add error listener to server-mode Holesail instances
- virtual-hosts.js: remove dead prevReconnectDelay variable
- tab-lifecycle.js: change swarmRefCount fallback from || 1 to || 0 to prevent premature swarm destroy
- ssh.js/rdp.js: disconnect MutationObservers on beforeunload
- Change readyTimeoutMs default from 0 to 30000 in both state files
- Register Alt+Shift+H keyboard shortcut via manifest _execute_action command
- Add severity filter buttons (All/Info/Warn/Error) and Download .txt to Logs page; background logs.js now tags entries with proper level field
- Fire browser notifications on tunnelError events (notifyOnTunnelError setting)
- Add exponential-backoff auto-reconnect for virtual hosts and service tunnels (tunnelAutoReconnect setting, 5s–120s backoff)
- Add backupIntervalHours setting and scheduled auto-backup timer in message-router.js
- Add TCP connect latency badges to Virtual Hosts and Service Tunnels tables via new pingTunnel native message
- Add bytesIn/bytesOut/requests counters to https-proxy.js; expose in Overview status bar via getState
- Add Export/Import connection configs (JSON, no CA key) in Settings
- Add autoReconnect flag and exponential-backoff reconnect to SSH connection cards
- Add light theme CSS variables and theme toggle in Settings (persisted to localStorage)
- Add checkbox column and bulk Stop/Remove actions to Virtual Hosts, Service Tunnels, and Servers tables
- Add Peer Lookup UI card on Overview page using existing lookup message handler
Split host.js (435 lines) into host/{paths,logger,startup,message-router}.js.
Split holesail-manager.js (698 lines) into holesail-manager/{state,settings,
connections,port-allocator,servers,virtual-hosts,service-tunnels,index}.js.
Top-level host.js and holesail-manager.js become thin shims so index.mjs
requires no changes. Deleted dev scratch file test-cp.mjs.
Updated CI with 12 new node --check lines for all sub-modules.
Updated docs/ARCHITECTURE.md with per-file tables for host/ and
holesail-manager/ sub-modules.
No functionality changed. No new dependencies.
Split monolithic dashboard.js (2753 lines) into 18 focused modules
under dashboard/{core,data,ui,pages}/ with refresh.js and events.js
as orchestrators. Extracted ~900-line inline <style> into dashboard.css
and moved dashboard.html to dashboard/dashboard.html.
Split background.js (609 lines) into background/{logs,state,proxy,
native-messaging,tab-lifecycle,message-router}.js with a thin entry
point using importScripts().
Deleted dead files: wrong-domain.js (duplicate of inline script).
Updated manifest.json web_accessible_resources for new paths.
Updated docs/ARCHITECTURE.md to reflect the new file structure.
No functionality changed. No build step introduced.
Replace the bare-https HTTPS proxy with a bare-tcp server that implements
SNI entirely in JavaScript. A pure-JS TLS ClientHello parser extracts the
SNI hostname from each incoming connection, derives the wildcard parent
domain by stripping the leftmost label, and selects (or generates on demand)
the correct wildcard cert via certificate-authority.getOrCreateWildcardCert().
This fixes ERR_SSL_SERVER_CERT_BAD_FORMAT for custom TLDs and supports
hostnames of any depth (e.g. i.love.hole.sail → cert *.love.hole.sail).
Also fixes ERR_INVALID_CHUNKED_ENCODING by stripping hop-by-hop headers
(Transfer-Encoding, Connection, etc.) from proxied responses — bare-http1
decodes chunked bodies internally so forwarding the header caused Chrome
to misinterpret the already-decoded body bytes.
- https-proxy.js: rewrite using bare-tcp + JS SNI peek + bare-tls per conn
- certificate-authority.js: add getOrCreateWildcardCert(parentDomain)
- host.js: remove refreshProxyCert/getActiveBaseDomains (no longer needed)
- background.js: PAC dnsDomainIs clauses already match any depth correctly
- dashboard.html: update vhost hint text to show deep hostnames are supported
- docs: update ARCHITECTURE, SECURITY, NATIVE-HOST; add VIRTUAL-HOSTS.md
- Replace SNI callback approach (unsupported by bare-tls) with a multi-SAN
wildcard cert covering all active base domains (e.g. *.hole.sail, *.heheh.jungle)
- Add buildMultiSanCert() in https-proxy.js — cert key encodes sorted domain list
so cache invalidates automatically when TLDs are added or removed
- Add restart(baseDomains, callback) to https-proxy.js to stop and restart the
proxy with a fresh cert without requiring a native host restart
- Add getActiveBaseDomains() and refreshProxyCert() in host.js; call after every
successful setVirtualHost and removeVirtualHost
- Fix background.js send handler to fetch updated virtualHosts via getState and
re-apply PAC immediately after setVirtualHost or removeVirtualHost succeeds,
so new TLDs are routed without waiting for the next dashboard refresh
- Remove unused tls require from https-proxy.js
- Remove invalid "permissions" entry from manifest.json permissions array
- Add isValidVhostHostname() to dashboard.js with embedded REAL_TLDS and
REAL_SLD_TLDS blocklists; enforces 3-label minimum (two-tier TLD requirement),
valid label characters, and blocks real public TLDs/SLDs (e.g. .com, co.uk)
- Replace hardcoded .hole.sail validation in vhost submit handler with new validator
- Update Add Virtual Host modal hint text and add inline format explanation
- Update applyPAC() in background.js to accept a tlds array, generating one
dnsDomainIs clause per unique two-label base domain; .hole.sail always included
- Store virtualHosts in extensionState and pass derived TLD list to applyPAC at
every getState response and retryGetStateForConnectProxy call
- Replace single upfront *.hole.sail cert in https-proxy.js with SNICallback that
lazily generates a wildcard cert per two-label base domain on first connection;
baseline *.hole.sail cert still pre-generated at startup
- Add chrome.permissions.request() in background.js send handler to grant host
permissions for new TLDs dynamically after successful setVirtualHost
- Add optional_host_permissions: ["*://*/*"] and "permissions" to manifest.json
to enable runtime host permission grants for custom TLDs
- Encode passwords as base64 (passwordB64) before persisting RDP and SSH
connections to native host state, decoded back on load/refresh
- Pre-fill password field when editing existing RDP or SSH connections
- Fix Quick Actions "Add SSH/RDP Connection" buttons to call openAddSshModal(null)
/ openAddRdpModal(null) so forms are properly reset instead of showing stale data
- Autofocus xterm.js terminal when SSH WebSocket connection is established
- Redesign SSH and RDP connection cards with a two-row layout: label + actions
in the top row, full hs:// key in a wrapping monospace meta row below;
removes all JS truncation and CSS ellipsis clipping
Critical fixes:
- Fix wrong registry key (com.bridgeswarm → com.holesail.browser) in
update-native-manifest-extension-id.ps1 — script was always failing on Windows
- Create missing wrong-domain.html redirect page for .host.test URLs
- Remove options_ui pointing to non-existent options.html from manifest
High-priority bug fixes:
- ssh-manager: track and kill orphaned printf FIFO writer when key auth succeeds
- ssh-manager: fix uncancelled 2000ms fallback password timer (assign to fallbackTimer,
clear in cancelPasswordWatch); fix null-check before removeAllListeners
- ssh-manager: add 30s Promise.race timeout to holesailInst.ready()
- backup-manager: fix macOS cp -R nesting bug by removing destination before copy;
add tar -tzf integrity check after archive creation
- host.js: restoreBackup now stops running tunnels before restore and re-starts them
- holesail-manager: fix stale closure bug in virtual host and service tunnel
error/close handlers (guard with v.holesail === hs check)
- dashboard.js: remove dead setText('dashTabs', ...) call referencing non-existent element
Medium improvements:
- manifest: remove unused storage and scripting permissions; restrict
web_accessible_resources match from <all_urls> to chrome-extension://*/*
- background.js: fix self-referential browser alias (globalThis.browser ?? chrome);
add 30s per-request timeout to send(); clean up dashboardTabs on tab close
- holesail-manager: gate saveStateSync stderr log behind DEBUG flag; updateSettings
now returns requiresRestart:true when proxy port changes; add backupRetention field
- host.js: pass requiresRestart through in updateSettings response
- dashboard.js: remove dead loadSettings() function; add requiresRestart warning toast;
add chrome.runtime.lastError guards in fetchState and refreshBackups;
set dynamic version from chrome.runtime.getManifest()
- dashboard.html: remove stray </button> tag; add id="sidebarVersion" for dynamic version
- install.sh/install.ps1: fetch version from RELEASE_BASE/VERSION instead of hardcoded 1.0.0
- install.ps1: add Firefox .xpi download and Firefox registry key
- update-native-manifest-extension-id.sh: add optional Firefox manifest update
- certificate-authority.js: defer RSA key generation to setImmediate to avoid blocking
startup; expose caReady promise
- host.js: await caReady before starting HTTPS proxy
Documentation:
- REMOTE-DESKTOP.md: correct RDP WebSocket protocol field names to match rdp-manager.js
(destLeft/destTop/destRight/destBottom, mouseMove/mouseButton/keyEvent/keyUnicode)
Feature additions:
- dashboard.js: add Reconnect button for service tunnels in error/closed state
- https-proxy.js: add WebSocket upgrade handler to support ws:// over *.hole.sail
- connect-proxy.js: add 10s header-read timeout to protect against idle connections
- native-host: add bare-fs as explicit dependency