DeepSeek Harness Desktop

August 16, 2026 · View on GitHub

An unofficial community desktop shell for the public DeepSeek Harness (dsh) agent runtime.

Download

macOS (Apple Silicon)Disk image · Zip

Use v0.1.4. Developer ID signed and notarized (the .app and the .dmg wrapper are both stapled). Closing the window hides to the menu bar; quit from the tray, Dock, or Cmd+Q. Nothing else to install — the agent runtime and its Node are inside the download.

Windows (x64)Installer · Portable zip

Unsigned, so SmartScreen will warn on first run. The current Windows drop is still 0.1.1.

All releases

dsh is a command-line agent runtime that also serves a full web UI. This project wraps that runtime in a desktop application: it starts the kernel as a local child process, waits until it is genuinely serving, and shows its UI in a hardened window — so the runtime can be launched by double-clicking rather than from a terminal.

Status: early development. The kernel itself is an upstream developer preview (0.1.0-rc.x) whose configuration surface is still changing. Windows and macOS (Apple Silicon and Intel) are the platforms that have been built end to end; see Roadmap for the rest.

Starting a sessionChoosing an agent preset
Starting a session in a workspaceChoosing an agent preset
Plugin settingsGeneral settings
Configuring the kernel's pluginsPresets, permissions, and appearance

Everything above is the upstream web UI, served by the kernel and rendered in the shell's window. The shell contributes the window, the process, and the security policy around them — not the interface.

What this is, and what it is not

This repository contains only the desktop shell. All agent behaviour — models, tools, sessions, permissions, the web UI — comes from the upstream kernel and its plugins.

The shell owns four things, and deliberately nothing else:

ConcernWhat the shell does
ProcessStarts dsh as a child process on a free port, and shuts down the whole process tree on exit.
ReadinessWaits for a real HTTP response from this launch before showing a window.
WindowApplies a fixed security policy: sandboxed renderer, exact-origin navigation allowlist, http/https-only external links.
ConfigurationExpresses its preferences as a patch overlay, through the kernel's own supported mechanism, without modifying upstream code.

Nothing upstream is patched or vendored-and-edited. The kernel is installed from npm at a pinned version, and every shell preference goes through --patch, which is a first-class part of the launcher's configuration layering.

Design notes

A few decisions that are easy to get wrong, and why this shell makes them the way it does.

An open port is not a ready server. The kernel's web server binds its port during startup, and a plugin failing afterwards can still bring the process down. In that window a TCP connection succeeds while nothing is being served, and a window pointed at it shows a blank page. Readiness therefore requires a real HTTP 2xx/3xx response.

Readiness is bound to one launch. Every probe attempt re-checks that the process being waited on is still the current one. Otherwise a kernel that died and left its port to another program on the machine would answer the probe perfectly well — with someone else's server.

Navigation is compared as an exact origin. startsWith('http://127.0.0.1:') also matches any other service running locally, and includes('127.0.0.1') matches http://evil.example/?x=127.0.0.1. The loaded page decides its own links; the shell does not get to assume they are benign.

Secrets are redacted as they enter the log buffer, not as they leave it. Redacting at read time still leaves the plaintext sitting in this process's memory until then. The matching is by shape and cannot be complete — it is a second line of defence, not a guarantee.

The kernel gets its own DSH_HOME, and every inherited DSH_* variable is dropped. Sharing a home directory with a dsh the user installed themselves would have the two overwrite each other's configuration, and would put the user's own stored credentials within reach of this process.

Telemetry is switched off explicitly. Upstream already defaults it to disabled; the shell states it anyway, so the default is a property of this application rather than of whichever kernel version happens to be bundled.

The kernel runs on its own bundled Node, not on Electron's. Those are different runtimes, and the difference is not theoretical. With an identical kernel and an identical configuration, launched under Electron-as-Node the kernel aborts during startup:

failed to apply loader entry … (@deepseek-ai/cordis-plugin-hmr):
  --expose-internals is required for HMR service

and on a stock Node build of the same major version it starts cleanly. Notably this happens after the web server is already answering HTTP — so even a real HTTP response is not proof that the process will stay up, which is why an unexpected kernel exit is reported rather than silently leaving a window pointed at nothing.

The bundled runtime is downloaded from nodejs.org, checked against the SHA-256 published in that release's SHASUMS256.txt, and then asked what version it is. Both checks fail the build rather than warn.

What ships is not what npm installs. An npm tree is published for developers: debug symbols, source maps, the TypeScript the JavaScript was built from, documentation, and prebuilt binaries for every platform. None of it is opened by a running application, and all of it would ship to every user — 180 MB of the installed size. tools/prune-kernel.js removes it, keeping licences and notices in every spelling, since redistributing MIT-licensed code without its licence text is a violation. The end-to-end test runs against the pruned kernel, so a size win that broke startup fails the build.

Requirements

To run a packaged build: nothing. The kernel and its Node runtime are inside the installer.

To develop: Node.js ≥ 22.15.0 — the kernel uses zlib.createZstdDecompress, which does not exist in earlier versions.

Windows and macOS ship a bundled, checksum-verified Node runtime, so a packaged build has no external requirements. Linux is expected to work but has not been exercised, and still falls back to the system Node.

Development

npm install          # shell dependencies (Electron, builder, types)
npm test             # unit tests for every load-bearing decision — no network, no Electron
npm run kernel:install   # fetch the pinned kernel into resources/kernel
npm start            # launch the shell against it
npm run dist:mac     # .dmg and .zip (Developer ID + notarize if Apple creds are set)
npm run dist:win     # NSIS installer and .zip for Windows

The kernel version is pinned in upstream.lock.json, which is the single source of truth for it. Upgrading is an explicit commit, not something a rebuild does on its own — upstream is a developer preview and documents that it will make breaking changes.

Layout

Load-bearing decisions live in plain modules with no Electron or filesystem imports, so they can be tested directly:

ModuleDecides
src/window-policy.jsWhere the window may navigate; what may be handed to the OS.
src/kernel-runtime.jsThe kernel's argument vector and environment.
src/readiness.jsWhen the kernel counts as ready.
src/log-redact.jsWhat may enter the log buffer, and how much is kept.

src/main.js does IO and orchestration only.

Roadmap

  • Kernel launch, argument and environment construction
  • Readiness probe bound to a single launch
  • Window and navigation security policy
  • Bounded, redacted log capture
  • Bundled, checksum-verified Node runtime
  • Windows installer, verified by launching it and loading the UI
  • macOS build (Apple Silicon .dmg + .zip, Developer ID signed and notarized; Intel runtime is pinned but not in the current drop)
  • Workspace picker verified working on the bundled runtime — the kernel's native Windows picker has been reported to crash under other runtimes; on this build it opens, cancels, and leaves the kernel serving. buildShellPatch({ useBrowseDirectoryPicker: true }) selects the non-native implementation if a build ever needs it, and is off by default
  • Linux builds

macOS notes

The v0.1.4 Apple Silicon build is Developer ID signed; the .app and the .dmg are notarized and stapled. Prefer that over v0.1.3 if a downloaded .dmg says it is damaged.

A Mac launched from the Dock has a minimal PATH. The shell prepends Homebrew's usual locations (/opt/homebrew/bin, /usr/local/bin) so the kernel can still find git and the rest of a developer toolchain.

Independence

Everything here is written from public sources against pinned public dependencies. It does not copy private product code, and contains no organization-specific branding, authentication clients, private endpoints, update feeds, credentials, or telemetry. The scan:leaks check in CI enforces this against the working tree and the committed history, and fails the build rather than warning.

This project is not affiliated with or endorsed by DeepSeek.

Licence

Repository-authored code is MIT — see LICENSE.

This project bundles the upstream @deepseek-ai/dsh kernel, which is also MIT-licensed. Third-party components retain their own licences, redistributed with the packaged application; see NOTICE for attribution and the trademark position.