5.9 KiB
Virtual Hosts
Virtual hosts let you access remote services over Holesail tunnels using human-readable HTTPS URLs in your browser — for example https://myapp.hole.sail/ instead of http://127.0.0.1:19042/.
How it works
- You add a virtual host in the dashboard: a hostname (e.g.
myapp.hole.sail) and anhs://key - The native host opens a Holesail client tunnel to the remote peer and assigns it a local port (e.g.
127.0.0.1:19042) - The extension updates the PAC script so the browser routes that TLD through the local CONNECT proxy
- When you navigate to
https://myapp.hole.sail/, the browser sendsCONNECT myapp.hole.sail:443to the CONNECT proxy, which pipes it to the HTTPS proxy - The HTTPS proxy performs a TLS handshake with a wildcard cert for that TLD, then proxies your HTTP request to the local tunnel port
Supported hostname formats
Any hostname with at least 3 labels and a private (non-public) TLD is accepted:
| Hostname | Valid? | Notes |
|---|---|---|
myapp.hole.sail |
✓ | Default TLD |
api.haha.wooo |
✓ | Custom TLD |
i.love.hole.sail |
✓ | Deep hostname — 4 labels |
a.b.c.my.internal |
✓ | Any depth supported |
myapp.com |
✗ | Real public TLD |
api.co.uk |
✗ | Real public SLD |
myapp |
✗ | Single label — no TLD |
hole.sail |
✗ | Two labels — no host prefix |
TLD requirements
- Must be two-tier: the TLD portion must be two labels (e.g.
.hole.sail,.my.internal,.haha.wooo) — single-label TLDs like.holesailare not allowed - Must not be a real public TLD or second-level domain (
.com,.net,.co.uk,.github.io, etc.) - The full hostname must have at least 3 labels total
Custom TLDs
You are not limited to .hole.sail. Any private two-tier TLD works:
https://api.haha.wooo/
https://dashboard.my.internal/
https://service.dev.local/
When you add a virtual host with a new TLD for the first time:
- PAC script updated — the extension adds a
dnsDomainIs(host, ".haha.wooo")clause so the browser routes all*.haha.woootraffic through the proxy - Host permissions requested — the extension requests
*://*.haha.wooo/*at runtime viachrome.permissions.request() - Certificate generated on first connection — the HTTPS proxy generates a wildcard cert for the exact wildcard parent on demand (see below)
Certificate generation
The HTTPS proxy uses JS-layer SNI to select the right certificate per connection:
-
The raw TLS ClientHello bytes are read from each incoming TCP connection
-
The SNI hostname is extracted (RFC 5246 extension type
0x0000) -
The wildcard parent is derived by stripping the leftmost label:
SNI hostname Wildcard parent Certificate myapp.hole.sailhole.sail*.hole.sailapi.haha.wooohaha.wooo*.haha.woooi.love.hole.saillove.hole.sail*.love.hole.saila.b.c.my.internalb.c.my.internal*.b.c.my.internal -
certificate-authority.getOrCreateWildcardCert(parent)returns an existing cert or generates a new one signed by the local root CA -
A
bare-tls.Socketis created with that cert and the TLS handshake proceeds
Certs are stored in ~/.holesail-browser/holesail-browser-certs/wildcard.<parent>/ (the * in the wildcard is stored literally as wildcard in the directory name, e.g. wildcard.hole.sail/) and reused on subsequent connections. They are valid for 1 year and auto-renewed on expiry.
No proxy restart is needed when adding new TLDs — the cert is generated on the first connection.
Deep hostnames
Hostnames with more than 3 labels work at any depth. Each unique wildcard parent gets its own certificate:
https://i.love.hole.sail/ → cert: *.love.hole.sail
https://also.love.hole.sail/ → cert: *.love.hole.sail (same cert, reused)
https://deep.a.b.c.my.internal/ → cert: *.a.b.c.my.internal
The PAC script matches all depths automatically — dnsDomainIs(host, ".hole.sail") returns true for i.love.hole.sail as well as myapp.hole.sail.
PAC script
The PAC script is generated dynamically from the set of active virtual host TLDs:
function FindProxyForURL(url, host) {
if (dnsDomainIs(host, ".hole.sail")) return "PROXY 127.0.0.1:8442";
if (dnsDomainIs(host, ".haha.wooo")) return "PROXY 127.0.0.1:8442";
return "DIRECT";
}
It is updated whenever:
- A virtual host is added or removed
- The native host connects or reconnects
- The proxy port changes
WebSocket support
WebSocket connections (ws:// / wss://) through virtual hosts are supported. The HTTPS proxy handles Upgrade: websocket requests by opening a raw TCP connection to the backend tunnel and piping the socket bidirectionally.
Troubleshooting
DNS_PROBE_FINISHED_NXDOMAIN
The PAC script has not yet been updated for this TLD. This can happen if:
- The virtual host was just added and the background script hasn't refreshed yet — wait a moment and reload
- The extension is not active — check the extension is enabled in
chrome://extensions
ERR_CERT_AUTHORITY_INVALID
The local root CA is not trusted by your browser. Go to the dashboard → Proxy & CA and click Install Root CA, then restart Chrome.
ERR_SSL_SERVER_CERT_BAD_FORMAT / cert mismatch
The HTTPS proxy presented a certificate that doesn't cover the requested hostname. This should not happen with the SNI-aware proxy. If it does:
- Check the native host is running (dashboard should show connected)
- Delete the stale cert directory:
rm -rf ~/.holesail-browser/holesail-browser-certs/wildcard.<parent>/ - Reload the page — the proxy will regenerate the cert on the next connection
502 Bad Gateway
The virtual host tunnel is not connected. Check the Virtual Hosts page in the dashboard — the tunnel may still be connecting or may have failed. Click Reconnect if it shows an error state.