dsh-cf-tunnel
Serve the DeepSeek Harness Web UI through a Cloudflare quick tunnel, and print the public URL together with a scannable terminal QR code.
$ dsh-cf-tunnel
11:48:19 | INFO | using cloudflared: cloudflared version 2026.10.0 (built 2026-10-05-17:37 UTC)
11:48:19 | INFO | auth shim listening on 127.0.0.1:39503 -> dsh on 127.0.0.1:45263
11:48:26 | INFO | quick tunnel ready: https://tidy-river-jumps-quietly.trycloudflare.com
11:48:28 | INFO | dsh Web UI ready on loopback port 45263
11:48:41 | INFO | public URL answered with HTTP 200 after 4 attempt(s)
========================================================================
Public URL : https://tidy-river-jumps-quietly.trycloudflare.com/?token=…
Tunnel : https://tidy-river-jumps-quietly.trycloudflare.com
Local URL : http://127.0.0.1:45263/?token=…
========================================================================
Scan to open the dsh Web UI (background style):
██████████████████████████████
██ ▄▄▄▄▄ █▀▄█▀▄▀█ ▄▀▄ ▄▄▄▄▄ ██
██ █ █ █▀▄ ▄ ▀▄▀█▄█ █ █ ██
…
Scan the code with a phone, or send someone the URL, and the dsh Web UI opens from anywhere.
Requirements
- Python 3.9 or newer.
- The
dshCLI onPATH(or--dsh-bin /path/to/dsh). This package launches it; it does not ship it. cloudflaredonPATH(or--cloudflared-bin /path/to/cloudflared).
Both tools are checked at startup, before anything is started. When one is missing, the program prints how to install it and exits with status 1 — it never installs or upgrades anything for you. Missing tools are reported together, so one run tells you about both.
For dsh the message offers npm install -g @deepseek-ai/dsh, the source tree, or --dsh-bin
for an installation that is already there. For cloudflared it offers the Debian/Ubuntu
repository and the standalone binary of the latest GitHub release for the detected platform.
Install
pip install dsh-cf-tunnel # or: uv tool install dsh-cf-tunnel / pipx install dsh-cf-tunnel
Usage
dsh-cf-tunnel # pick a free port, open a tunnel, print the URL and QR code
dsh-cf-tunnel --port 8123 # use a fixed port
dsh-cf-tunnel --qr-style half # compact QR code for a narrow terminal
dsh-cf-tunnel --qr-png ./qr.png # also write the QR code to a PNG file
dsh-cf-tunnel --no-qr # print the URL only
dsh-cf-tunnel --no-host-page # leave dsh unpatched (see "Settings pages" below)
Press Ctrl-C to stop; the tunnel and the dsh Web UI are shut down with it.
Options
| Flag | Default | Meaning |
|---|---|---|
--port |
0 |
Loopback port for the dsh Web UI; 0 picks a free one |
--dsh-bin |
dsh |
dsh executable to launch |
--cloudflared-bin |
cloudflared |
cloudflared executable |
--ready-timeout |
60 |
Seconds to wait for the tunnel URL and then the dsh URL |
--public-timeout |
120 |
Seconds to wait until the public URL really answers; 0 skips the check |
--qr-style |
auto |
background, half, plain, or auto (see below) |
--qr-border |
2 |
Quiet zone around the printed QR code, in modules |
--qr-png PATH |
– | Also write the QR code to a PNG file |
--no-qr |
– | Do not print a QR code |
--runtime-dir PATH |
temp dir | Where the generated dsh patch files are written |
--no-host-page |
– | Do not patch dsh with the host-owned page flag |
--shim-open-index |
– | Let the auth shim serve the index without a token (weaker) |
--no-auth-shim |
– | Connect the tunnel straight to dsh |
--log-level |
INFO |
DEBUG, INFO, WARNING, or ERROR |
How it works
browser ──https──▶ Cloudflare edge ──tunnel──▶ cloudflared ──▶ auth shim ──▶ dsh --profile web
127.0.0.1 127.0.0.1
- Preflight. Both external tools are resolved from
PATHbefore any process is started. When one is missing, the install commands for it are printed — forcloudflaredthe apt repository or the latest GitHub release asset for the running platform, fordshthe npm package and the existing-installation route — and the program exits with status 1. Nothing is ever installed on your behalf. - Quick tunnel.
cloudflared tunnel --no-autoupdate --url http://127.0.0.1:<shim>is started and itshttps://<name>.trycloudflare.comhostname is parsed from the log. A failed tunnel request is reported instead of being mistaken for a tunnel URL. - Auth shim. dsh authenticates a browser by trading the launch token for an
HttpOnly,SameSite=Strict, authority-bound cookie during a303redirect. Clients that open the URL from another app, a WebView, or a browser extension routinely lose that cookie and land ondsh web authentication required. The shim answers the token URL with the application itself and attaches the session cookie to everything it forwards — static assets,/api, and the/api/remote.muxWebSocket upgrade. - dsh with a generated patch.
dsh --patch <generated> --profile web --port <port> --trusted-host <tunnel host>is started. The tunnel hostname is not a loopback authority, so without--trusted-hostevery/apirequest would fail the browser-trust fence with 403; and because the browser side keeps its settings pages unavailable on a page that does not count as loopback, the generated patch publishesglobalThis.__DSH_TRANSPORT__ = {ownsHost: true}— the same flag the desktop shell sets for the pages it serves itself. - Banner. Once the public URL answers, the URL is printed with a QR code and both children are
supervised until
Ctrl-Cor until one of them exits.
QR styles
Module colors are always drawn explicitly as black on white, so a dark terminal theme cannot invert the code:
--qr-style |
Rendering | Width | Notes |
|---|---|---|---|
background |
Two spaces per module on an explicit black or white background | 2 cells/module | Default in a terminal when it fits; square modules, no font glyphs involved, no seams |
half |
Upper half block per top module, cell background per bottom module | 1 cell/module | Half the width; depends on the font drawing ▀ as a full half cell |
plain |
Half blocks without color | 1 cell/module | Used automatically when stdout is not a terminal |
--qr-style auto prefers background, falls back to half when the terminal is too narrow, and
uses plain when the output is redirected. A warning is printed when the code is wider than the
terminal; --qr-png is the fallback for scanners that cannot read terminal art at all.
Settings pages
The Settings and Models pages in dsh are deliberately limited to loopback pages. A tunneled page is never loopback, so without the generated patch those pages report:
Loading the provider directory failed: settings are unavailable in this browser
No request is made in that case, which is why the browser console and network panel stay clean. The
default patch lifts that limit for the tunneled page. Pass --no-host-page to keep dsh's original
behavior.
Security
The printed URL contains the dsh launch token: anyone who has the link can drive the agent on your
machine, including running commands. The auth shim does not change that, and --shim-open-index
additionally treats the tunnel hostname itself as the secret. Stop the program (Ctrl-C) when you
are done, and prefer a short-lived session over a long-running one.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
dsh: 'dsh' was not found in PATH |
The dsh CLI is not installed. Run npm install -g @deepseek-ai/dsh, or point --dsh-bin at an existing installation (a checkout, the desktop app's runtime, or another Node version's global bin). |
cloudflared: 'cloudflared' was not found in PATH |
Install cloudflared from the two options in the message, or point --cloudflared-bin at it. |
| npm installed dsh but the program still cannot find it | npm prefix -g prints the global prefix; its bin directory is not on PATH. Add it, or pass --dsh-bin "$(npm prefix -g)/bin/dsh". |
dsh web authentication required |
A client that could not keep the session cookie. The shim is on by default; if you disabled it with --no-auth-shim, re-enable it. If the client also mangles the URL, add --shim-open-index. |
settings are unavailable in this browser |
The page is not host-owned. Do not pass --no-host-page, and make sure the generated patch was applied (--log-level DEBUG prints its path). |
| Public URL does not resolve yet | A fresh quick tunnel needs a moment to propagate; the program waits for the first answer before printing the banner. |
| QR code is unreadable | Try --qr-style background, lower --qr-border, or write --qr-png ./qr.png. |
| cloudflared download fails | Export HTTPS_PROXY/HTTP_PROXY, or install cloudflared from the apt repository printed in the message. |
Development
uv venv --python 3.12 .venv
uv pip install -e '.[dev]'
uv run pytest # unit tests
DSH_CF_TUNNEL_E2E=1 uv run pytest -m e2e # boots a real tunnel, needs dsh + cloudflared
uv build # wheel and sdist in dist/
Releases are cut by hand with uv build + uv publish; the full checklist (PyPI tokens, version
bump, TestPyPI rehearsal) is in RELEASING.md, and notable changes are listed in
CHANGELOG.md.
License
MIT — see LICENSE.
Metadata
Release files for dsh-cf-tunnel 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dsh_cf_tunnel-0.1.0.tar.gz | 45.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dsh_cf_tunnel-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 73.7 kB
Release files / dsh_cf_tunnel-0.1.0.tar.gz
| Download URL | dsh_cf_tunnel-0.1.0.tar.gz |
|---|---|
| Size | 45.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
86689b5456ef76289a223df5dd8b143d3b612525c597bd3587ad4cf9437067e6
|
|
BLAKE2b-256 checksum How to use checksums |
7bd7dfbd8484043043a17bea7fb27c725152152d5efc2887724975f6b127fd9e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / dsh_cf_tunnel-0.1.0-py3-none-any.whl
| Download URL | dsh_cf_tunnel-0.1.0-py3-none-any.whl |
|---|---|
| Size | 28.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
368be4ffee7f5442d5e2858c742f0889babf1b2b53ee63c1848e462f9145762b
|
|
BLAKE2b-256 checksum How to use checksums |
69250bcd879f9ea11da829b5a9716a49fe17c2c065ac42330667210096a0b467
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|