Introduction

dsh runs on a machine at home or work, while you are away. You want to check the progress of a session or assign a task to a workspace temporarily. The conventional approach involves SSH port forwarding or exposing the web UI to the public—either of which requires you to route to that machine, or requires a public IP or reverse proxy, or else you have to run a tunnel service that holds the keys to your machine.

dsh-discord-bot takes a different route: using Discord as the control plane. The machine initiates a single outbound WebSocket connection to the Discord Gateway and maintains it, with commands arriving via this existing connection. No ports are listened to, no ports are forwarded, and no new inbound attack surface is introduced. Before introducing what this plugin can do, how to install it, and what switches to consider, here is the introduction.

What is this

dsh-discord-bot is a community plugin for DeepSeek Harness (hereinafter referred to as dsh), maintained by Oliver0804, licensed under MIT, current version 0.3.4, depending on discord.js ^14.27.0, and requiring Node >= 20. It projects a dsh profile onto a Discord guild: under a category, each workspace corresponds to a text channel, accompanied by a set of /dsh … commands that allow you to read session trajectories, view live subagents, and issue work within the channel. The philosophy of dsh is “everything is a plugin,” and remote access has also been implemented as a plugin here.

Core Functions

Outbound Only

The plugin opens a WebSocket to the Discord Gateway and maintains it. Every command is delivered via this existing connection, so the harness can respond from behind a NAT, CGNAT, hotel network, or corporate firewall—without needing port forwarding, dynamic DNS, reverse proxies, or tunnel services that hold the keys to your machine. As long as the machine can access discord.com, you can check it on your phone. Nothing is listening, so there are no new inbound attack surfaces.

Read & Monitor

Read side covers: sessions, trajectories, raw event timelines, subagents, lineage. Inside a workspace channel, use /dsh … commands to read session trajectories and live subagents.

The monitor side is mirror: true: every round of the harness—whether triggered by the web UI, TUI, or cron—appears in real-time in the corresponding workspace channel. Disabled by default because it implies continuously exporting session content to a chat platform.

Write & Run

Write includes registering workspaces and switching profile configurations:

  • /dsh workspace: Register a workspace
  • /dsh model: Switch default model
  • /dsh preset: Switch agent preset
  • /dsh permission: Switch permission preset

The run side has only one command: /dsh run <prompt>—delivering a prompt to the workspace agent and streaming back that round. Disabled by default (allowRun: false)—all other commands are for describing the workspace; only this one causes actual work to happen on your machine.

Channels are Private by Default

Categories and channels reject @everyone, granting access only to the bot and users in allowedUserIds; when the list is empty, only the guild owner remains. The same rule determines who can run commands, so what a person can read and what they can ask won’t diverge.

Privacy settings are reapplied on every sync, not just when created: existing categories, or those created before this feature was enabled, are also locked. If the bot lacks ‘Manage Channels’, it cannot enforce restrictions; it will explicitly state this in logs and /dsh sync rather than letting you assume it’s locked.

Additionally, the bot responds in the language used by the clicker.

Installation & Enablement

Prepare the bot on the Discord side first, then install it into the dsh profile.

  1. Create a bot. In the Discord Developer Portal (https://discord.com/developers/applications), go to New Application -> Bot -> Reset Token, and copy the token. No special intents are needed; keep Message Content disabled.

  2. Invite the bot to the server. Use bot and applications.commands as the scope:

https://discord.com/oauth2/authorize?client_id=<APP_ID>&permissions=268487696&scope=bot+applications.commands

Replace <APP_ID> with your Application ID. permissions=268487696 corresponds to View Channels, Send Messages, Embed Links, Attach Files, Manage Channels, and Manage Roles. The last one is easy to miss: writing channel permission overrides—i.e., making the channel private—requires Manage Roles, and Discord only reports “Missing Access” when it is missing. Without it, the bot can still work, but channels will remain publicly readable, and a prompt will appear on every sync.

  1. Install into the dsh profile:
npx dsh-discord-bot-setup --profile web

The setup script installs the package, registers the bundle with the profile, writes the token to $DSH_HOME/discord-bot.token (mode 600), and appends a configuration override to the profile’s patch layer. Unpassed parameters are asked interactively; if the corresponding configuration line already exists in the profile, the script refuses to run and leaves the profile unchanged.

Non-interactive style:

npx dsh-discord-bot-setup --profile web --guild 123456789012345678 --token "$TOKEN" --yes

Add --print to only preview the lines that would be written, making no changes.

  1. Restart dsh and verify:
dsh --profile web

Open the newly appearing dsh category and run /dsh status.

Manual Installation & Configuration

If you don’t want to use the setup script:

dsh plugin --profile web add dsh-discord-bot

This step installs the package and mounts the plugin into the profile’s bundle layer; the plugin’s built-in cordis.patch.yml thus becomes a layer of the composite configuration tree. At this point, the plugin will start in an offline state and explain this in the logs—because no one has yet told it which guild to bind. In the profile’s own cordis.patch.yml, add a targeted id override:

- id: discord-bot
  config:
    guildId: '123456789012345678'
    tokenFile: '/Users/you/.dsh/discord-bot.token'
    categoryName: 'dsh'

Note that this is an override, not a second insert. The bundle layer already mounts this line; two layers with the same ID won’t merge but compose two instances—two bots on the same guild, answering commands twice. Use the following command to check the composite configuration tree and confirm that id: discord-bot appears only once:

dsh --profile web --dump-config

By the way, this plugin belongs to the host plane; do not put it into an agent preset: it serves all workspaces and all sessions; duplicating it in a second session will cause a conflict.

Upgrading from version 0.3.1 or earlier

Versions 0.3.1 and earlier did not have a bundle layer; the setup script then wrote complete insert: lines into the profile. Now that the package mounts itself, that line will compose a second bot. Replace the old line with the id override line above, keeping your own values:

# Before                             # After
- insert:                         - id: discord-bot
    - id: discord-bot               config:
      name: 'dsh-discord-bot'         guildId: '…'
      config:                         tokenFile: '…'
        guildId: '…'

Not changing it for now won’t break anything: the line in the bundle has no configuration and will only log a missing guildId message and stay offline, preventing the composition from failing. After changing, use --dump-config to confirm there is only one id: discord-bot.

Typical Usage

After the steps above, a dsh category will appear in the guild. The structure is roughly:

dsh                    ← Category
├── dsh                ← Workspace /Users/you/Documents/dsh
├── sweepbot-home      ← Workspace /Users/you/code/game/godot/sweepbot_home
└── my-api             ← Workspace /Users/you/work/my-api

Send /dsh run <prompt> in the corresponding channel. A run card will appear in the channel, indicating which tool is currently executing, with buttons for Trace, Timeline, Subagents, Todos, Export, Steer, Stop, etc.; Trace contains every tool call and its result, with duration, tokens, and cache hit rate attached at the bottom. You can see exactly which step the session has reached and what was done at each step directly on your phone.

Security Boundaries and Two Defaults-Off Switches

mirror and allowRun are off by default, for the same reason: content export.

  • mirror: true will continuously export session content to Discord.
  • allowRun: true: after this, anyone in allowedUserIds can make the agent edit files or run commands on the harness machine—from their phone. At this point, the list is no longer just a privacy boundary but becomes a shell access list. The README suggests keeping only yourself on the list; when enabled, prefer the ask permission preset over danger-full-access.

Regarding boundaries, the bot does not weaken dsh’s own sandbox and approval policies: operations rejected locally by the harness are also rejected here, and it cannot approve on your behalf. It also cannot cross workspace boundaries—it cannot access sessions in other workspace channels.

Suitable Scenarios & Notes

Suitable for:

  • dsh running on a machine behind NAT, CGNAT, or corporate firewall without a public entry point;
  • Needing to view and drive sessions on a phone or any Discord-enabled device;
  • Small-scale use: add allowed people to allowedUserIds, keeping the channel read scope and command permissions consistent.

Unsuitable for:

  • Scenarios that cannot accept session content passing through Discord—the command responses and mirror both pass through Discord;
  • Scenarios where access rights need to be opened to people outside the list—this permission model is not designed for that.

Two reminders: The plugin runs with the permissions of the current dsh process; its reach is exactly what the dsh process can reach. Before installing any third-party plugin, it is recommended to read the source code and confirm the license (this project is MIT).

Conclusion

A one-line summary: a single outbound WebSocket brings workspaces, session trajectories, and harness commands to Discord, keeping inbound ports closed throughout.

Project address and community directory page:

  • GitHub: https://github.com/Oliver0804/dsh-discord-bot
  • Community directory page: https://www.skillhub.cn/plugins/Oliver0804/dsh-discord-bot

The community directory is an independent site and has no official affiliation with DeepSeek or HF.