dsh-web-search-ddg
aooyoo
Zero-token DuckDuckGo search provider for the DeepSeek Harness (DSH) web seam — local headless browser, no API key, no m…
PROJECT TOPICS
PROJECT README
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.
Install — one command:
dsh plugin --profile web add git+https://github.com/weicy98/dsh-cftunnel
Restart dsh web, open Settings → Cloudflare Tunnel.
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.
Pick the mode that matches what you have:
| Mode | Inputs | URL | Use when |
|---|---|---|---|
| Quick tunnel | none | random https://<name>.trycloudflare.com |
you want a URL right now, no Cloudflare account setup |
| Tunnel token | tunnel token + hostname | https://<hostname> |
you already have a tunnel from the Zero Trust dashboard |
| API token | API token + tunnel name + hostname | https://<hostname> |
you want the plugin to create the tunnel, DNS, and (optionally) email OTP for you |
cloudflared — if $DSH_HOME/cloudflared/cloudflared is missing it downloads the official GitHub release asset for your platform, extracts it, and caches it.cloudflared tunnel --url http://127.0.0.1:<proxyPort> → random https://<name>.trycloudflare.com.cloudflared tunnel run with a remotely-managed token (routing/ingress configured in the Zero Trust dashboard).cloudflared tunnel run with the returned token (no cert.pem, no local config.yml).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.~/.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.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 treatsfile:dependencies as immutable, so remove + add (or a version bump) is the reliable refresh path.
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).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:
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.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.
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:
user@example.com): exact-email rules (Access "Emails").yourcompany.com): email-domain rules (Access "Emails ending in").Requirements:
Access: Organizations, Identity Providers, and Groups:Edit, and (unless accessAccountId is set) Account Settings:Read to resolve the account id.API-token mode needs (see also the hint inside the Settings panel):
| Scope | Permission | Used for |
|---|---|---|
| Account | Cloudflare Tunnel — Write |
create tunnel, set ingress |
| Zone | DNS — Edit |
create the CNAME record |
| Account | Account Settings — Read |
resolve account id (skip by filling Account ID) |
| Zone | Zone — Read |
resolve zone id (skip by filling Zone ID) |
| Account | Access: Organizations, Identity Providers, and Groups — Write |
email OTP only |
dsh web / --profile web).http://127.0.0.1:3091 (the loopback proxy port — routing to 3080 bypasses the access-token gate).github.com on first connect (only to fetch cloudflared; it is then cached).pnpm install # dsh-settings (dev/peer), schemastery
pnpm test # node --test (cfapi, cloudflared, host lifecycle, and proxy suites)
| Concern | Where | Mechanism |
|---|---|---|
Provision cloudflared |
lib/cloudflared.js |
GitHub release download + tar extract + cache under $DSH_HOME/cloudflared |
| Run/manage tunnel | lib/index.js |
node:child_process spawn + ctx.effect teardown |
| Cloudflare API | lib/cfapi.js |
cfd_tunnel create + configurations ingress + dns_records CNAME + Access app/policy |
| Loopback proxy | lib/proxy.js |
Host rewrite to loopback + Origin drop + access-token gate (cookie / ?token=) + WS forward — zero dependencies (node:http + node:net, no http-proxy / DEP0060) |
| Dependencies | package.json |
schemastery as a leaf dependency; host seams (dsh-settings) as peerDependencies; engines.node >= 20.3 |
| Secrets | lib/index.js |
ctx.credentials (CLOUDFLARE_API_TOKEN, CLOUDFLARE_TUNNEL_TOKEN, CLOUDFLARE_TUNNEL_PROXY_TOKEN) |
| Preferences | lib/index.js |
ctx.settings namespace cloudflare-tunnel |
| Client→Host RPC | both halves | ctx.connection.rpc.handle('/cloudflare-tunnel', …) / ctx.get('connection').rpc.call(…) |
| Settings UI | lib/client.js |
ctx.slots.inject('settings.section', …) + locale dictionaries |
| Bundle wiring | cordis.patch.yml + package.json dsh |
one 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.
connection, credentials, settings, and webServer; in headless/TUI it stays dormant).cloudflared authenticates with the tunnel token, not the API token.CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。