DeepSeek Harness plugin

dsh-mobile-access-kk99668

dsh bundle: serve the DeepSeek Harness Web GUI to a phone through your own domain and server — token gate + supervised SSH reverse tunnel

Jump to install

Source facts

Repository
kk99668/dsh-mobile-access
Latest update
Aug 15, 2026
Category
Security & Permissions
GitHub stars
0
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/kk99668/dsh-mobile-access
Plugin: dsh-mobile-access-kk99668
Author: kk99668

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-mobile-access

English | 中文

The dsh mobile-access bundle: serve the Web GUI to a phone through your own domain and server. [cordis.patch.yml](cordis.patch.yml) rides over the web surface: it extends the connection row's /api browser-trust fence with the public authority the tunnel serves, and inserts this package's mobile-access glue plugin. That plugin starts a local token gate (src/gate.ts) in front of the webserver and a supervised SSH reverse tunnel (src/tunnel.ts) to the relay server, provides the mobileAccess service (the public host the fence accepts), registers the mobile-surface prompt section and the bash-visible DSH_WEB_PUBLIC_URL runtime variable when surfaceContext is true, and prints the public URL (plus a terminal QR code when qrCode is true) once the tunnel is established. On the relay server, Caddy terminates HTTPS for the domain and reverse-proxies into the tunnel; [scripts/setup-server.sh](scripts/setup-server.sh) installs that side. The step-by-step deployment runbook is [DEPLOYMENT.md](DEPLOYMENT.md).

How it works

phone ── https://dsh.example.com ──> Caddy (HTTPS, on your server)
                                        └──> 127.0.0.1:<relay-port>  (sshd reverse tunnel)
                                                  └──> local token gate (dsh process)
                                                        └──> http://127.0.0.1:<web-port>

The tunnel is an ssh -N -R child owned by the plugin: it reconnects with exponential backoff, resets the backoff after a stable uptime, and dies with its fiber. The gate is the authentication layer the fence deliberately is not: every request and WebSocket upgrade must present the token (a cookie set by the login form, or Basic/Bearer credentials) before it is forwarded to the webserver. The public authority reaches the fence through the mobileAccess service, which the patch injects into the connection row so the fence's config expression can never resolve before the authority exists.

Weak-network behavior

The phone link is the constrained hop (a subway commute, for example); the tunnel and the relay sit on the two stable ends. Three mechanisms keep the GUI usable when that link degrades:

  • The event streams compress when the host supports it. The WebSocket downlinks negotiate permessage-deflate (shipped in dsh web builds), so the session event stream (repetitive JSON) shrinks several-fold on the phone's link. The browser negotiates the extension automatically.
  • The browser reconnects. The web client's connection controller re-establishes both event streams with exponential backoff (500 ms doubling to a 10 s cap) whenever the signal drops, so a tunnel outage or a dead zone recovers without a page reload.
  • The tunnel heals itself. keepAliveIntervalSec / keepAliveCountMax make ssh notice a dead relay fast (default 30 s × 3 probes), and the supervisor restarts the child with jittered backoff (backoffBaseMs..backoffMaxMs). On a flaky home uplink, lowering keepAliveIntervalSec to 10 and backoffBaseMs to 1000 shortens the blackout window at the cost of a few extra probe packets.

Install

The bundle is opt-in, on top of the shipped web profile. Install from this repository (a prepare script builds the plugin from source; pnpm ≥10 asks you to allow the build the first time — copy the package key pnpm prints into the profile's pnpm-workspace.yaml under allowBuilds and re-run), or from npm when published:

dsh plugin --profile web add github:kk99668/dsh-mobile-access
# or, once published to npm:
dsh plugin --profile web add dsh-mobile-access

The inserted row reads its config from environment variables (the app boot loads .env from the invoking directory and the Harness home):

VariableMeaning
DSH_MOBILE_PUBLIC_HOSTPublic origin, bare host or host:port (required)
DSH_MOBILE_RELAY_HOSTRelay server address (required)
DSH_MOBILE_RELAY_USERSSH login user on the relay server (required)
DSH_MOBILE_TOKENGate secret, at least 8 characters (required)

Unset required variables fail the boot loud with the field names. Every other setting is a validated plugin config field with a default: relayPort 22, remoteBindHost 127.0.0.1, remoteBindPort 3080, localPort (the active webserver port), gatePort 0 (OS-assigned), sshBin ssh, identityFile unset, keepAliveIntervalSec 30, keepAliveCountMax 3, connectTimeoutSec 10, backoff 2s..30s, stableUptimeMs 60s, runningAfterMs 3s, printUrl true, qrCode true, surfaceContext true. To change one, override the row's config in $DSH_HOME/profiles/web/cordis.patch.yml (a patch replaces the whole config, so restate every key):

- id: mobile-access
  config:
    publicHost: dsh.example.com
    relayHost: vps.example.com
    relayUser: dsh-relay
    token: a-long-random-secret
    remoteBindPort: 8443
    identityFile: C:\Users\you\.ssh\id_ed25519

Server setup

Run once on the relay server (root): sudo ./scripts/setup-server.sh dsh.example.com 3080 ~/dsh-relay.pub. The script installs Caddy with automatic HTTPS for the domain, creates the dsh-relay user, installs the given public key with authorized_keys restrictions that allow only the reverse listen (permitlisten="127.0.0.1:3080", no shell, no agent, no X11), warns when GatewayPorts is enabled, and opens 22/80/443 in ufw. The key file argument is optional: any SSH login that can reach the server works, though the restricted relay user is the recommended shape. After the script, set the environment variables above, start dsh --profile web, and scan the QR code with the phone.

Security

  • The token gate is the only thing between the public internet and the agent. Anyone who knows the token can control this agent — treat it as a credential.
  • The gate's login cookie is HttpOnly and SameSite=Lax; the token never enters the URL, logs, or referrers.
  • The relay server binds the tunnel port to loopback only; the phone never talks to the tunnel directly, only to Caddy over TLS.
  • The fence's privileged methods (settings.*, credentials.*, host.*, ...) stay pinned to loopback even on a trusted-host deployment, so the configuration plane is not reachable through the tunnel.
  • Reconsider before exposing the harness from a shared or untrusted network. The web app itself has no user accounts; the gate is the boundary.

Model Experience

Mobile-surface context

#### What the model sees

When surfaceContext is true, the app:mobile-surface global section (order −97, right after the web-surface section) orients the model to the public origin, and DSH_WEB_PUBLIC_URL appears in the managed bash environment with its description, resolved per invocation:

##### Verbatim text for this field

The DeepSeek Harness Web GUI is also served at https://dsh.example.com through a secure tunnel. Users arriving there interact with the same harness and sessions as this local GUI.

#### Token effect

One prompt paragraph per session plus one managed-environment variable line, constant per process.

#### KV Cache effect

The section sits near the system prompt's head and is stable for the life of the process (the public host is a boot fact), so it does not invalidate the cache across turns.

Known Limitations and Deferred Work

  • The tunnel requires an SSH clientsshBin must resolve on the machine running dsh; a missing binary fails loud at spawn and retries with backoff, and the README's manual ssh -N -R command is the diagnosis path.
  • The phone needs a browser login per device — the session cookie has no Max-Age, so each phone authenticates once per browser profile; there is no persistent-device registry or per-device revocation.
  • Windows autostart is the user's own — the tunnel lives inside the dsh --profile web process; keeping the GUI reachable means keeping that process running (a service wrapper is not shipped).
  • No in-GUI status surface — tunnel status is visible in the console log and through the mobileAccess service only; a settings card is deferred work.