DeepSeek Harness plugin

dsh-cftunnel

Cloudflare Tunnel plugin for DeepSeek Harness: expose the local web GUI through a quick / token / API tunnel behind a token-gated loopback proxy, with optional Cloudflare Access email OTP.

Jump to install

Source facts

Repository
weicy98/dsh-cftunnel
Latest update
Aug 16, 2026
Category
Remote & Mobile
GitHub stars
1
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/weicy98/dsh-cftunnel
Plugin: dsh-cftunnel
Author: weicy98

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.mdSource · read only
README language

dsh-cftunnel

English | 中文

A DeepSeek Harness bundle plugin that runs a Cloudflare Tunnel to expose the local web GUI (http://127.0.0.1:3080) on the public internet.

Install the plugin, open Settings, type your Cloudflare info, click Connect. cloudflared is downloaded and managed by the plugin itself — no manual install, no package manager, no restart.

Quick start

1. Install — one command:

``sh dsh plugin --profile web add git+https://github.com/weicy98/dsh-cftunnel ``

2. Restart dsh web, open Settings → Cloudflare Tunnel. 3. Pick a mode, fill in your Cloudflare info, click Connect. Copy the access URL (it embeds a token on first use) and share it.

> The first Connect downloads cloudflared (~40 MB) once — on slow networks this can take several minutes; the Settings panel shows a progress bar while it downloads.

Modes

Pick the mode that matches what you have:

ModeInputsURLUse when
Quick tunnelnonerandom https://<name>.trycloudflare.comyou want a URL right now, no Cloudflare account setup
Tunnel tokentunnel token + hostnamehttps://<hostname>you already have a tunnel from the Zero Trust dashboard
API tokenAPI token + tunnel name + hostnamehttps://<hostname>you want the plugin to create the tunnel, DNS, and (optionally) email OTP for you

How it works, end to end

1. The Settings section shows a mode picker (Quick / Tunnel token / API token) and a Connect button. 2. On Connect the plugin first self-provisions cloudflared — if $DSH_HOME/cloudflared/cloudflared is missing it downloads the official GitHub release asset for your platform, extracts it, and caches it. 3. It starts the tunnel: - Quick: cloudflared tunnel --url http://127.0.0.1:<proxyPort> → random https://<name>.trycloudflare.com. - Token: cloudflared tunnel run with a remotely-managed token (routing/ingress configured in the Zero Trust dashboard). - API: create the tunnel + ingress + DNS CNAME through the Cloudflare HTTP API, then cloudflared tunnel run with the returned token (no cert.pem, no local config.yml). 4. All tunnel traffic flows through a loopback reverse proxy (127.0.0.1:<proxyPort>) that rewrites the Host header to loopback and drops Origin, so the harness trust fence accepts every request — privileged methods included. Access-token verification (default on) and optional Cloudflare Access email OTP gate the proxy. 5. Secrets (API token, tunnel token, proxy access token) live in the harness credential store (~/.dsh/.credentials.yaml); non-secret preferences (mode, hostname, tunnel name, account/zone ids, email-OTP settings) live in a cloudflare-tunnel settings namespace (~/.dsh/settings.yaml) and prefill the form on the next open.

Install

From this checkout's directory:

dsh plugin --profile web add file:.

Or from a git / npm source:

dsh plugin --profile web add git+https://github.com/weicy98/dsh-cftunnel
dsh plugin --profile web add dsh-cftunnel

dsh plugin runs pnpm in the profile directory, then reconciles dsh.profile.bundles — because this package declares dsh.bundle.patch, it is appended to the bundle stack automatically. Restart dsh web and open Settings → Cloudflare Tunnel.

> Reinstalling after editing the source: dsh plugin --profile web remove dsh-cftunnel && dsh plugin --profile web add file:. — pnpm treats file: dependencies as immutable, so remove + add (or a version bump) is the reliable refresh path.

Configuration

Deployment-level options are set through the profile's own cordis.patch.yml (~/.dsh/profiles/web/cordis.patch.yml) or a --patch overlay. Everything else is a Settings-panel field and persists in the settings namespace.

- id: cloudflare-tunnel
  config:
    cloudflaredPath: 'cloudflared'   # explicit binary path (default: auto-download to $DSH_HOME/cloudflared)
    port: 3080                       # local harness port to proxy
    downloadMirror: ''               # optional ghproxy-style prefix, e.g. 'https://ghproxy.example/'
    proxyPort: 3091                  # loopback port of the reverse proxy
    proxyAuth: 'token'               # 'token' (default) | 'none'
    proxyToken: ''                   # optional fixed access token; empty = auto-generate and persist
    accessPolicy: 'none'             # 'none' | 'emailOtp'
    accessEmailDomains: []           # e.g. ['yourcompany.com'] — restrict email OTP delivery
    accessAccountId: ''              # optional explicit Cloudflare account id
    accessZoneId: ''                 # optional explicit Cloudflare zone id

Notes:

  • downloadMirror controls how the one-time cloudflared download happens. Empty (the default) downloads through the GitHub API → CDN — authoritative, but slow on some networks (measured ~80 KB/s, so ~8 minutes for the 40 MB binary). Set a mirror prefix to speed it up; the plugin then downloads from <mirror>https://github.com/cloudflare/cloudflared/releases/latest/download/<asset>. A mirror is a third party you are trusting to serve an unmodified binary.
  • accessAccountId / accessZoneId are also fillable in the Settings panel; setting them there avoids needing Account Settings:Read / Zone:Read API-token permissions.
  • proxyAuth: 'none' disables the access-token gate (the UI confirms before turning it off).

Security model (important)

All tunnel traffic goes through a loopback reverse proxy (127.0.0.1:<proxyPort>) that rewrites requests to look loopback (Host → 127.0.0.1:<port>, Origin dropped). The harness trust fence therefore accepts them fully — privileged methods included — so a remote visitor gets the complete local experience.

The proxy binds 127.0.0.1 only; its sole entry point is cloudflared (Cloudflare edge). Two independent gates sit in front of it:

  • Access token (default ON, proxyAuth: 'token'): every request must carry the token — sent once as ?token=… (the proxy then plants an HttpOnly; Secure cookie), or as the cookie afterwards. The UI shows the ready-to-share access URL and can toggle the gate live.
  • Cloudflare Access email OTP (optional, accessPolicy: 'emailOtp'): edge-level email verification before the request even reaches the proxy.

Recommendation: expose the public URL with both gates on. Disabling both means anyone holding the URL has full control of the harness (settings, credentials, directory picker) — only acceptable on a trusted network.

Local use (http://127.0.0.1:3080) bypasses the proxy entirely and is unaffected.

Access control (email OTP)

Set accessPolicy: 'emailOtp' (or tick the checkbox in the panel) to gate the tunnel behind Cloudflare Access One-time PIN. On connect, the plugin uses the same API token to create a self-hosted Access application bound to the hostname plus an allow policy:

  • No whitelist: any visitor enters an email, receives a 6-digit PIN, and only gets through after entering it.
  • Whitelist with full emails (user@example.com): exact-email rules (Access "Emails").
  • Whitelist with domains (yourcompany.com): email-domain rules (Access "Emails ending in").

Requirements:

  • API-token mode only (quick and token tunnels cannot be gated automatically — their hostname isn't programmatically managed by this plugin).
  • The API token additionally needs Access: Organizations, Identity Providers, and Groups:Edit, and (unless accessAccountId is set) Account Settings:Read to resolve the account id.

API token permissions

API-token mode needs (see also the hint inside the Settings panel):

ScopePermissionUsed for
AccountCloudflare Tunnel — Writecreate tunnel, set ingress
ZoneDNS — Editcreate the CNAME record
AccountAccount Settings — Readresolve account id (skip by filling Account ID)
ZoneZone — Readresolve zone id (skip by filling Zone ID)
AccountAccess: Organizations, Identity Providers, and Groups — Writeemail OTP only

Prerequisites

  • A profile with the web surface (dsh web / --profile web).
  • Tunnel token mode: a tunnel token from the Zero Trust dashboard, with a public hostname routing to http://127.0.0.1:3091 (the loopback proxy port — routing to 3080 bypasses the access-token gate).
  • API token mode: an API token with the permissions above, plus a hostname whose DNS zone is on that account.
  • Outbound network access to github.com on first connect (only to fetch cloudflared; it is then cached).

Development

pnpm install   # dsh-settings (dev/peer), schemastery
pnpm test      # node --test (cfapi, cloudflared, host lifecycle, and proxy suites)

Architecture

ConcernWhereMechanism
Provision cloudflaredlib/cloudflared.jsGitHub release download + tar extract + cache under $DSH_HOME/cloudflared
Run/manage tunnellib/index.jsnode:child_process spawn + ctx.effect teardown
Cloudflare APIlib/cfapi.jscfd_tunnel create + configurations ingress + dns_records CNAME + Access app/policy
Loopback proxylib/proxy.jsHost rewrite to loopback + Origin drop + access-token gate (cookie / ?token=) + WS forward — zero dependencies (node:http + node:net, no http-proxy / DEP0060)
Dependenciespackage.jsonschemastery as a leaf dependency; host seams (dsh-settings) as peerDependencies; engines.node >= 20.3
Secretslib/index.jsctx.credentials (CLOUDFLARE_API_TOKEN, CLOUDFLARE_TUNNEL_TOKEN, CLOUDFLARE_TUNNEL_PROXY_TOKEN)
Preferenceslib/index.jsctx.settings namespace cloudflare-tunnel
Client→Host RPCboth halvesctx.connection.rpc.handle('/cloudflare-tunnel', …) / ctx.get('connection').rpc.call(…)
Settings UIlib/client.jsctx.slots.inject('settings.section', …) + locale dictionaries
Bundle wiringcordis.patch.yml + package.json dshone insert row, auto-added to dsh.profile.bundles

RPC endpoints on /cloudflare-tunnel: status, start, stop, setProxyAuth. start accepts an AbortSignal, so cancelling from the UI aborts in-flight downloads and API setup.

Limitations

  • Web-profile only (the node half waits for connection, credentials, settings, and webServer; in headless/TUI it stays dormant).
  • Process-local: the running tunnel does not survive a harness restart, but the cached binary, the stored secrets and preferences do. The tunnel, ingress, and DNS records persist on Cloudflare.
  • The API token is used for Cloudflare API setup calls (tunnel/ingress/DNS/Access); the running cloudflared authenticates with the tunnel token, not the API token.