Files
holesail-browser/docs/TUNNELING.md
T
Raven Scott f5c349a4a3
CI / Build & Test (push) Successful in 2m56s
docs
2026-02-28 22:29:56 -05:00

172 lines
7.4 KiB
Markdown

# Tunneling
Holesail Browser supports three tunnel types: virtual hosts (for browsing), server tunnels (for sharing), and service tunnels (for raw TCP forwarding). All tunnels use the Holesail P2P network and are persisted across restarts.
## How `*.hole.sail` browsing works
When you navigate to `https://myapp.hole.sail/`:
1. The PAC script (set by the extension) matches `*.hole.sail` and routes the request to `PROXY 127.0.0.1:8442`
2. Chrome sends `CONNECT myapp.hole.sail:443 HTTP/1.1` to the CONNECT proxy
3. The CONNECT proxy replies `200 Connection established` and pipes the stream to `127.0.0.1:8443`
4. The HTTPS proxy reads the TLS ClientHello, extracts the SNI hostname (`myapp.hole.sail`), derives the wildcard parent (`hole.sail`), and presents the `*.hole.sail` wildcard cert signed by the local CA
5. The HTTPS proxy reads the `Host` header, looks up the Holesail tunnel for `myapp.hole.sail`, and proxies the HTTP request to the tunnel's local port
6. The Holesail client tunnel forwards the request to the remote peer over the DHT
All traffic between the browser and proxy is TLS-encrypted using a locally-trusted certificate. Traffic between the native host and the remote peer is encrypted via the Noise protocol.
## Virtual hosts
Virtual hosts map an `hs://` key to a `*.hole.sail` hostname. The browser navigates to the hostname; the proxy looks up the tunnel and forwards the request.
### Adding a virtual host
1. Open the dashboard → **Virtual Hosts**
2. Enter a hostname (e.g. `myapp.hole.sail`) and the `hs://` key for the remote peer
3. Click **Add** — the tunnel connects immediately and the entry is saved to `state.json`
### Hostname rules
- Must have **at least 3 labels** (e.g. `myapp.hole.sail`, not `hole.sail`)
- The TLD portion must be **two-tier** (e.g. `.hole.sail`, `.my.internal`) — single-label TLDs like `.holesail` are not allowed
- Must **not** use a real public TLD (`.com`, `.net`, `.co.uk`, etc.)
- Each label may contain only lowercase letters, digits, and hyphens; no leading or trailing hyphens
- Any depth is supported: `myapp.hole.sail`, `i.love.hole.sail`, `api.v2.my.internal`
- You are not limited to `.hole.sail` — any valid private two-tier TLD works (see [VIRTUAL-HOSTS.md](VIRTUAL-HOSTS.md))
### Port allocation
Each virtual host gets a local port allocated from 19000 upward. The HTTPS proxy forwards requests for that hostname to `127.0.0.1:<allocated-port>`. Ports are released when the virtual host is removed.
### Lifecycle events
The native host emits events that the extension relays to subscribed tabs:
| Event | Description |
|-------|-------------|
| `tunnelReady` | Tunnel connected and ready to serve requests |
| `tunnelClosed` | Tunnel disconnected (peer went offline or was removed) |
| `tunnelError` | Tunnel failed to connect |
### Persistence
Virtual hosts are saved to `state.json` and restored on every native host startup. The tunnel reconnects automatically — if the remote peer is temporarily offline, the tunnel will reconnect when it comes back.
### Removing a virtual host
Open the dashboard → **Virtual Hosts** → click the remove button. The tunnel is closed and the entry is deleted from `state.json`.
---
## Server tunnels
Server tunnels expose a local TCP (or UDP) port as an `hs://` key. Other peers can connect to your service using that key.
### Creating a server tunnel
1. Open the dashboard → **Server Tunnels**
2. Optionally enter a **Label** (e.g. `My Web App`) to identify the tunnel
3. Enter the local port to expose (e.g. `3000`)
4. Select **TCP** or **UDP**
5. Optionally enable **Secure** mode (requires the connecting peer to have the key)
6. Click **Start** — an `hs://` key is generated and displayed
Share the `hs://` key with anyone who should be able to connect to your service.
### Parameters
| Parameter | Description |
|-----------|-------------|
| `label` | Optional human-readable name shown in the dashboard |
| `port` | Local port to expose |
| `host` | Local host to bind (default: `127.0.0.1`) |
| `udp` | Use UDP instead of TCP (default: `false`) |
| `secure` | Require key authentication (default: `true`) |
### Persistence
Server tunnels are saved to `state.json` and restarted on every native host startup. The `hs://` key is stable — it is derived from the server's keypair, which is preserved in state.
### Stopping a server tunnel
Open the dashboard → **Server Tunnels** → click **Stop**. The tunnel is closed and the entry is deleted from `state.json`.
---
## Service tunnels
Service tunnels forward a remote `hs://` peer to a local TCP port. Any local application can connect to `127.0.0.1:<port>` and communicate with the remote peer directly — without going through the browser proxy.
Use cases:
- Connect a local database client to a remote database over Holesail
- Forward a remote TCP service to a local port for use by any application
- Expose a Minecraft server, game server, or other TCP service locally
### Creating a service tunnel
1. Open the dashboard → **Service Tunnels**
2. Enter a label (e.g. `Postgres`), the `hs://` key, and the local port to bind
3. Click **Start** — the tunnel connects and begins listening on the specified port
### Parameters
| Parameter | Description |
|-----------|-------------|
| `label` | Human-readable name |
| `hsUrl` | `hs://` key for the remote peer |
| `localPort` | Local TCP port to bind |
### Persistence
Service tunnels are saved to `state.json` and restored on startup.
### Reconnecting a service tunnel
If a service tunnel enters an error or closed state, click **Reconnect** in the dashboard. This sends an `updateServiceTunnel` command to the native host, which stops the old tunnel (if any) and starts a fresh one with the same parameters.
### Stopping a service tunnel
Open the dashboard → **Service Tunnels** → click **Stop**.
---
## Tunnel lookup
You can check whether an `hs://` key is reachable on the DHT without creating a full tunnel. This is useful for verifying a key before adding it as a virtual host or service tunnel.
From the dashboard → **Virtual Hosts** or **Service Tunnels**, use the lookup/test button next to the `hs://` field.
---
## Tunnel type comparison
| Feature | Virtual Host | Server Tunnel | Service Tunnel |
|---------|-------------|---------------|----------------|
| Direction | Inbound (you browse to remote) | Outbound (remote connects to you) | Inbound (you connect to remote) |
| Access method | Browser via `*.hole.sail` URL | `hs://` key shared with peers | Local TCP port |
| Protocol | HTTPS (TLS terminated locally) | TCP or UDP | TCP |
| Use case | Browse remote web apps | Share local services | Connect local apps to remote services |
| Persisted | Yes | Yes | Yes |
---
## Troubleshooting
**`No tunnel for this hostname`**
The HTTPS proxy received a request for a hostname it has no tunnel for. Causes:
- The tunnel is still connecting — wait a few seconds and refresh
- The native host restarted and is still restoring tunnels — open the dashboard to check
- The hostname doesn't exactly match what was configured (check for typos)
**Tunnel shows `connecting` but never becomes `ready`**
- The remote peer may be offline
- The `hs://` key may be incorrect
- Check `~/.holesail-browser/holesail-browser.log` for error details
- Try setting `readyTimeoutMs` to `30000` in Settings to surface timeout errors
**Service tunnel port is already in use**
Another process is using the requested local port. Choose a different port, or stop the conflicting process.