# phone-tunnel-pool — Cloudflare quick-tunnel pool for the dsh web GUI **English** | [简体中文](./README.zh-CN.md) Enable/disable a **self-healing Cloudflare quick-tunnel pool** for `http://127.0.0.1:3080` (the DeepSeek Harness web GUI) from a floating widget with a scannable QR code. One scan from your phone, and the pool keeps itself alive: - **Generational rotation (12h):** a new pair of tunnels spawns on schedule; older generations stay alive while anything is still on them. - **Chase service worker:** every origin your browser touches registers a service worker. Dead or rotated hostnames redirect to a live sibling or the newest primary — the same open tab survives generation changes as long as it stays connected. - **Prompt-free migrations:** the proxy injects credentials only into pages it has already authenticated; before any redirect the watchdog pre-authenticates the target hostname (minting its auth cookie), so migrations land authenticated — no "Authentication required" popups. - **Usage-based retirement:** generations retire only when idle (no tabs / websockets / recent traffic) or at a hard age cap. - **Respawn with backoff:** dead tunnels are replaced with new hostnames; quick-tunnel mint quota (Cloudflare 429) is respected via exponential backoff + a 2-probe dead-grace (DNS propagation). Extra: the daemon runs detached and **adopts** across `dsh web` restarts, so the same URL, password and QR stay valid until you click Disable — no re-scan (an OS reboot still costs one fresh scan; a named tunnel removes even that — see [`PLAN.md`](./PLAN.md) §7). ## Install / Uninstall ```bash # install (from this public repo) dsh plugin --profile web add github:iimaguest/phone-tunnel-pool dsh web # the GUI shows a floating 📱 widget (bottom-right) # uninstall (one command — removes the dependency AND the dsh.profile.bundles layer) dsh plugin --profile web remove phone-tunnel-pool dsh web ``` After install: open the widget → **Enable** → scan the QR with your phone camera. Install/remove reconcile `dsh.profile.bundles` against the installed state automatically — **never edit `~/.dsh/profiles/web/package.json` by hand**; a stray bundle entry with no matching dependency is exactly the kind of state that fails profile boot ("cannot resolve profile bundle"). ## Prerequisites (all of them) | What | Needed? | Who provides it | |---|---|---| | `dsh web` running on its default port **3080** (`DSH_TARGET_PORT` to override) | required | you (the plugin tunnels *to* it) | | `cloudflared` binary on PATH | required | you — `brew install cloudflared` (or apt/dnf/Chocolatey, or set `DSH_CLOUDFLARED` to the existing binary) | | Node.js runtime | required | dsh itself — no separate install (the daemon reuses dsh's node) | | `python3` + `qrcode` package | optional | you — `pip install qrcode`; without it the widget shows URL + login instead of a scannable QR | | `caffeinate` | optional | macOS ships it; skipped elsewhere | | PowerShell | optional | Windows ships it — used only for process cleanup on Windows (no `pkill` there) | | Outbound network | required | cloudflared → Cloudflare edge on 443/7844 (no inbound port needed) | The widget **preflights these on dsh web start** and shows a yellow warning line (with the exact fix, e.g. `brew install cloudflared`) before you even click Enable; the daemon also fails fast with a readable error if cloudflared is missing at Enable time, and `refresh` in the popup re-checks everything — a stale error clears once the prereqs pass. The feature flag set is version-gated on `cloudflared --version`: 2024.6+ enables the opt-in post-quantum handshake (`DSH_PQ=1`), 2024.8+ adds `--management-diagnostics=false`; older builds (apt/dnf packages) get a reduced, compatible flag set. **Platforms.** macOS, Linux and Windows (Windows uses PowerShell for process cleanup; `caffeinate` is macOS-only and silently skipped elsewhere). The daemon's state file and log live in the per-OS temp directory (`os.tmpdir()`); the widget settings file (`iptunnel-settings.json`) lives in `~/.dsh`. ## Screenshots