172 lines
7.4 KiB
Markdown
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.
|