← Back to home@stephenlstrange2

dsh-remote-dashboard

Read-only wall dashboard for DeepSeek Harness sessions across several machines: pull or push, one hub, tablet page that fits the screen.

Stars
0
Language
JavaScript
Created
Oct 1, 2026
Updated
Oct 7, 2026
GitHub repo

Introduction

dsh-remote-dashboard

Read-only, multi-machine session monitor for DeepSeek Harness, built for a wall/tablet screen.

Tablet view: four machines, running sessions with their current tool, an approval banner, and an offline machine

Phone layout The same dashboard on a phone: one column

Screenshots use made-up data (docs/screenshots.mjs renders them from fake sessions; run it again after changing the page). The page shows, per machine: online state and latency (or push · 5s for machines that push), running count, and for each session: running LED, title, time since activity, the tool executing now, folder, turn, tokens, background jobs and goal progress. Pending approvals show in the orange banner. An unreachable machine greys out with its error.

 machine A (DSH)  ──┐   agent  GET /v1/snapshot  (Bearer token)
 machine B (DSH)  ──┼──────────────►  HUB (one DSH)  ──►  tablet browser  http://hub:3090/?key=VIEWER_KEY
 machine C (DSH)  ──┘   polled every pollMs        serves the page + /api/fleet

[!WARNING] No TLS, and the viewer key travels in a URL. Read this before you expose it.

  • Traffic is plain HTTP. Anyone who can sniff the network between the tablet, hub and agents can read session titles, folder names, approval text, and the tokens/keys. Run it on Tailscale (or another VPN) or a LAN you trust; never port-forward it to the internet.
  • You open the page once with ?key=VIEWER_KEY. The hub swaps it for an HttpOnly cookie and redirects to a clean URL, but the key has still been typed into an address bar, so it can sit in that browser's history, in a bookmark, or in a proxy/router log. Treat it like a password and rotate it if it leaks: change viewerKey, then open the page again.
  • Everything is read-only, but a leaked key shows your whole fleet. Use tokenFile (chmod 600) so secrets stay out of config files you might commit.

No dependencies, no build step. Nothing here can write to DSH: the server answers GET/HEAD, plus one POST /v1/push that only accepts a snapshot from a configured, authenticated machine and only stores it in the hub's memory.

Modes

modeservesuse
agent/v1/snapshot (token)every machine you want to watch
hubthe tablet page + /api/fleeta machine that only aggregates
botheverything, plus this machine's own sessionsthe machine hosting the dashboard
pushnothing: no host, no portbehind NAT / VM / no inbound: the machine sends its snapshot to the hub

Peer tokens stay on the hub. The tablet only holds the viewer key, which becomes an HttpOnly cookie on first visit.

Install

dsh plugin --profile web add /path/to/DSH-Dashboard

Then add config to the profile's cordis.patch.yml (a top-level YAML list).

Each watched machine (agent), reachable over Tailscale or LAN:

- id: dsh-remote-dashboard
  config:
    mode: agent
    machine: lab-pc
    host: 100.64.0.10        # this machine's Tailscale/LAN IP, never a public one
    allowRemote: true        # required for any non-loopback host
    port: 3090
    token: <random 16+ chars>

The hub machine:

- id: dsh-remote-dashboard
  config:
    mode: both
    machine: hub-pc
    host: 0.0.0.0            # or its LAN/Tailscale IP
    allowRemote: true
    port: 3090
    viewerKey: <random 8+ chars>
    pollMs: 3000
    peers:
      - { name: lab-pc,   url: 'http://100.64.0.10:3090', token: <that agent's token> }
      - { name: home-nuc, url: 'http://100.64.0.11:3090', token: <...> }

A machine that can't be reached (VM, NAT, a different tailnet): push mode. It binds nothing and just POSTs to the hub, so the only requirement is that it can reach the hub:

# on the pushing machine
- id: dsh-remote-dashboard
  config:
    mode: push
    machine: otx-box
    hub: http://10.0.2.2:3090      # QEMU/Boxes user-mode NAT: the host is 10.0.2.2. Otherwise use the default gateway.
    tokenFile: /home/me/.dsh-remote-dashboard-token   # chmod 600; or token: <16+ chars>
    pushMs: 3000
# on the hub, in peers: (next to any polled peers)
      - { name: otx-box, push: true, tokenFile: /home/me/.otx-box-token, pushMs: 3000 }

The hub names a pushing machine from its own config (matched by token), never from the payload, so one machine can't pose as another. A push peer is shown offline after max(10 s, 3 × pushMs) without a push. Poll and push peers can be mixed freely.

After git pull on the plugin, restart dsh web. patchReload: live re-reads the config but not the plugin code, so a config that uses a newer feature (e.g. a push peer) is rejected by the old code until the restart.

Open http://<hub>:3090/?key=<viewerKey> on the tablet once, then add it to the home screen. Generate secrets with openssl rand -hex 16.

Safety

  • Non-loopback binds are refused unless allowRemote: true, and then a token (agent) or viewerKey (hub) is mandatory.
  • Constant-time comparison; peer tokens never reach the browser.
  • POST /v1/push checks the token before reading the body, caps it at 1 MB (413), answers a wrong token with a plain 401 that doesn't say whether the name exists, and re-bounds everything it stores (200 sessions, 100 jobs, clipped strings, recomputed totals). The viewer key is never accepted there and a push token never opens the page.
  • tokenFile keeps secrets out of a patch file you might commit; the plugin warns if the file is readable by group/other.
  • mode: push refuses host, port, allowRemote, viewerKey and peers so a wrong setup fails loudly.
  • No TLS: keep this on Tailscale or a trusted LAN. Don't port-forward it.
  • Snapshots contain session titles, working directories, tool names and approval text. Treat the viewer key like a password.

What the page shows

Per machine: online state and round-trip time (or time since last contact when offline), running count. Per session: running LED, title, time since last activity, the tool executing now, working-directory name, turn, tokens in/out, active background jobs, goal progress. Pending approvals and questions from any machine appear in a banner and in the tab title.

Limits

  • Live metrics (turn, tokens, current tool) count events seen since the plugin started.
  • Sessions that exist only on disk and aren't loaded are not listed.
  • The page uses plain ES5 for old tablet browsers. Keep-awake needs HTTPS or localhost, so on plain HTTP set the tablet's screen timeout to "never" or use a kiosk browser.

Planned work, such as multiple names per machine, is listed in ROADMAP.md.

Migrating from dsh-monitor

This plugin was called dsh-monitor before 0.2.0 (that npm name belongs to an unrelated plugin). Per machine: dsh plugin --profile web remove dsh-monitor, dsh plugin --profile web add dsh-remote-dashboard (or your clone's full path), then python3 scripts/apply-config.py YOUR_ENTRY.yml --migrate-from dsh-monitor to swap the old - id: dsh-monitor block for - id: dsh-remote-dashboard. Restart dsh web. Your tokens and keys do not change.

Development

Read AGENTS.md before changing anything, and add a CHANGELOG.md entry with a visual. node docs/screenshots.mjs regenerates the README screenshots from fake data (needs Playwright; set PLAYWRIGHT_DIR and CHROME).

npm test covers config validation, auth, hub aggregation, offline detection, the collector, and push mode (auth, size limits, sanitising, impersonation, offline, tokenFile).