Back to home@zetaluolang-cyber

deepseek-harness-phone-remote

DeepSeek Harness phone remote control via Tailscale - persistent file/workspace plugin - tested on OPPO Find X8 Ultra

Stars
9
Language
JavaScript
Created
Aug 15, 2026
Updated
Aug 17, 2026

Introduction

DeepSeek Harness Phone Remote

Don't watch your agent. Keep its pulse.

A secure, zero-app remote workspace for DeepSeek Harness. Reach the real Harness web UI from your phone over Tailscale (or LAN), manage files and workspaces, and keep an ambient eye on your agents through Agent Presence — the floating Orb, Task Board and notifications.

  • 🎈 Agent Presence — floating brand Orb (drag anywhere): Needs You / Failed / Possibly Stalled / Running / Done / Idle / Disconnected, with a Task Board and browser notifications for what needs you.
  • 📁 Files & workspaces — start/resume an agent in any approved folder; read, write, upload, download files.
  • ↔ Handoff — leave the desk and continue the same session from your phone (sessions.open — never a replacement session).
  • 🔐 Device pairing — one-time code, per-device credentials, revocation.

This project does not replace the Harness UI. It turns Harness itself into a remote work environment: the phone opens the real DeepSeek Harness web UI over Tailscale (or your LAN), and a persistent plugin bridges the gaps a browser can't close remotely — authenticated file/workspace access and Agent Presence (Orb / Task Board / notifications).

English | 中文 · Architecture · Security · Contributing

Why

  • Harness binds 127.0.0.1 — a deliberate, sane default. The Harness process itself stays loopback-bound; only the opt-in forwarders (Tailscale IP and, when enabled, the LAN IP) expose selected interfaces.
  • A phone browser still can't reach loopback, and the GUI's directory picker is a loopback-only privileged method — so this plugin adds a secure path (Tailscale + LAN forwarders) and a filesystem/workspace bridge for exactly those two gaps.
  • Sessions normally die with the page — this plugin is a persistent loader entry, so the workbench loads on every page automatically, no per-session "run" needed.

Architecture

flowchart LR
  P[Phone / remote browser] -->|Tailscale HTTPS| S[tailscale serve]
  P -->|Tailscale IP| T[TCP forwarder]
  P -->|same Wi-Fi: LAN IP| L[LAN forwarder]
  S --> H[DeepSeek Harness Web<br/>127.0.0.1:3080]
  T --> H
  L --> H
  H --> R[/remfs RPC channel<br/>trusted-host fence/]
  R --> A[Device authentication<br/>pairing + per-device credential]
  A --> F[Filesystem capability layer<br/>allowlist + protected paths + realpath]
  F --> W[(Approved workspace)]

Three independent layers:

  1. Transport — who can reach the channel: Tailscale membership or your LAN (forwarders only bind the Tailscale IP and the LAN IP; never 0.0.0.0).
  2. Application — who may use it: device pairing + per-device credentials.
  3. Capabilitywhat they may touch: the allowlist + protected paths.

trusted-host and the tailnet are transport trusts. They are not authentication. Pairing and the filesystem capability layer are.

Features

  • One-click deploy (auto-installs prerequisites)install.ps1 validates the Node version (^22.19 || >=24), installs missing Node.js / Tailscale (winget), guides the one-time Tailscale sign-in (re-reads the real MagicDNS name — never a fabricated one), writes the launcher, enables HTTPS Serve, installs the plugin, registers auto-start on login.
  • Walk-on-LAN (opt-in) — off by default. Create %USERPROFILE%\.dsh\lan-on (or set DSH_REMFS_LAN=1) to trust the LAN IP and start the LAN forwarder; on the same Wi-Fi the phone can then skip Tailscale (http://192.168.x.x:3080). /remfs stays device-authenticated. Enabling it widens the network exposure, so it is an explicit choice.
  • Persistent plugin — loader entry; host channel registers at startup, the client module loads on every page. No re-running after refresh.
  • Device pairing & management — one-time pairing code (10 min TTL, single use); list / revoke / revoke-all devices; credentials stored only as hashes.
  • Mobile-first workbench — New Session / Files tabs, breadcrumbs, preview / edit / upload / download, workspace badges, floating ball, auto-collapsed sidebar, bilingual UI (EN/zh).
  • Host-enforced protected paths — system dirs, AppData, credential/key files (.credentials.yaml, .ssh, .aws, .gnupg, .env, id_rsa, *.pem …) and private data dirs (WeChat/WPS) are blocked regardless of the allowlist.

Security model

  • Tailscale ≠ authentication. It proves which network you are on, not who you are. Device pairing is the application boundary.
  • trusted-host ≠ authentication. It is the browser-trust fence (Host header + cross-site checks). Pairing is the boundary.
  • The Harness process stays loopback-only. Exposing the web service on a network interface happens ONLY through the explicit forwarders (the Tailscale IP, and the LAN IP when walk-on-LAN is opted in) — those bind specific addresses, never 0.0.0.0.
  • Pairing protects /remfs only, not the native Harness /api. The GUI's own API surface has no user login; keep the network boundary (tailnet / LAN) tight and review which devices can reach it.
  • The filesystem allowlist is the primary file-permission boundary. Remote clients can only narrow it; widening (C:\, new drives) requires editing .remfs-roots.json on the PC.
  • Path escape is defended twice: raw paths with ../UNC are rejected, and the canonical realpath must stay inside the allowlist (symlink/junction escapes fail).
  • See SECURITY.md for the full threat model (what we do and do not protect).

Positioning

This project is a secure remote workspace & filesystem bridge for DeepSeek Harness: it keeps the native web UI and adds authenticated remote access plus a capability-bounded file/workspace layer. It is not a UI replacement, skin, or alternative frontend — the ecosystem has other community projects for those directions, and they are complementary rather than competing.

Installation

Advanced users (npm):

dsh plugin --profile web add @zetaluolang/remfs-persistent
# append to %USERPROFILE%\.dsh\profiles\web\cordis.patch.yml:
#   - insert:
#       - id: remfs-persistent
#         name: '@zetaluolang/remfs-persistent'
#         inject: [connection, fs]
# restart dsh web

Windows users (one-click): double-click 一键部署.cmd — it validates the Node version (^22.19 || >=24), auto-installs Node.js + Tailscale, guides the Tailscale sign-in, writes the launcher, registers the self-healing watchdog, enables HTTPS Serve, installs the plugin and prints the phone URLs (HTTPS, Tailscale IP, and the LAN IP when walk-on-LAN is enabled).

First use on the phone (pairing)

  1. Open the phone URL (https://<pc-name>.<tailnet>.ts.net, or the LAN URL when walk-on-LAN is enabled and you are on the same Wi-Fi).
  2. The workbench shows the pairing screen.
  3. On the PC, read the pairing code from %USERPROFILE%\.dsh\remfs-pairing.txt (or the harness log).
  4. Enter the code + a device name on the phone → paired. Credentials are stored on the phone; the PC stores only the hash.
  5. Revoke devices anytime from the workbench ⋯ → Devices.

Self-healing watchdog

install.ps1 registers a Task Scheduler task (dsh_harness_watchdog, every 5 minutes, current user, hidden window) that runs %USERPROFILE%\.dsh\launcher\watchdog.ps1. Each run:

  1. Verifies our dsh process actually owns 127.0.0.1:3080 — the owning process's command line must contain the deployed dsh bin path (the watchdog reuses the launcher's Get-OwnedHarnessPid ownership check; a bare open port is never trusted, so an unrelated localhost service is never mistaken for the harness).
  2. If our harness is down and the port is free, restarts it headlessly via restart_harness_once.ps1 (DSH_HEADLESS=1 — no browser, no dialogs) and appends every step to %USERPROFILE%\.dsh\launcher\watchdog.log.
  3. If a foreign process occupies the port, it logs the conflict and stands down — it never kills or restarts over a process it does not own.

Re-run install.ps1 (or 一键部署.cmd) to update the task definition; the watchdog itself checks in with one log line every 5 minutes when healthy.

Threat model

We defend against: unauthenticated RPC, remote allowlist widening, path escape, credential theft at rest, accidental LAN/public exposure of the GUI.

We do not (yet) defend against: the harness GUI /api itself having no user login (pairing protects /remfs, not the GUI — keep the network boundary tight), a compromised host, or a compromised Tailscale account. Details in SECURITY.md.

Troubleshooting

SymptomFix
Phone shows the pairing screen foreverRead the code from %USERPROFILE%\.dsh\remfs-pairing.txt; codes expire after 10 min — restart the harness to generate a new one
Device revoked / re-pairing failsPairing codes are single-use; restart the harness for a fresh code
Phone gets 403Use the printed HTTPS/Tailscale/LAN URL; the GUI must run with those hosts trusted (one-click deploy does it)
LAN URL unreachablePhone must be on the same Wi-Fi; re-run the launcher so the current LAN IP is detected
npm.ps1 blocked by execution policyUse npm.cmd, or Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
npm view 404s right after a publishCDN edge cache — wait a minute or query with Cache-Control: no-cache
Plugin never appears after dsh plugin addYou must also append the loader row and restart dsh web
PC sleepskeep_awake + power plan are handled by the deploy; see keep_awake.ps1
Harness keeps dying / phone unreachableCheck %USERPROFILE%\.dsh\launcher\watchdog.log; re-run install.ps1 to (re)register the watchdog task

Tested devices

  • OPPO Find X8 Ultra (real hardware).
  • Emulated matrix: iPhone 16 Pro/SE, Pixel 8, Galaxy S24, Redmi Note, iPad Air, iPhone landscape — sidebar collapse, floating ball, panel width, no overflow. See docs/device-tests/.

Roadmap

  • Tailscale HTTPS + IP access, walk-on-LAN
  • Persistent plugin (no per-session run)
  • Device pairing + credential auth + revocation
  • Capability-bounded allowlist + protected paths + path-escape tests
  • Bilingual UI, security tests, CI
  • More device resolutions validation
  • Tailscale ACL hardening guide
  • Upstream contributions (see below)

License

MIT