SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor

dsh plugins: wetin dem fit do before you install

dsh plugins dey run another person code with your agent permissions. See wetin dem fit reach and the checks to do before you install any one.

Wetin be dsh plugin, and wetin e fit do?

dsh plugins na Node packages wey DeepSeek Harness dey load inside im own process. When you install one, e go run another person code with your agent permissions, for the machine wey your agent fit already reach. Nothing dey between loaded plugin and the rest of the harness. So before you install one, ask wetin that code fit touch, and how you fit keep the access small.

dsh (DeepSeek Harness) na DeepSeek AI open-source agent harness, wey dem build with plugin framework wey dem call Cordis. Na that middle word carry the main matter, because agent harness na the program wey surround the model and control the loop, tools, and permissions. Plugin join am for exactly that level. The project own README talk say everything na plugin. The model adapter na plugin. The web interface wey you dey type into na plugin. Anything wey you install from outside the project go enter the same tree, with the same trust level as the parts wey come with am. If you never set one up yet, start with DeepSeek Harness for VPS and come back before you add anything to am.

The extension points wey plugin fit reach dey listed for repository AGENTS.md. As of August 2026, dem cover:

  • LLM (large language model): provider wey your API key dey pay for
  • Shell: bash capability, with local and pwsh providers
  • Filesystem: file access wey policy dey control
  • Web: search and fetch providers
  • Subprocess: process-tree provider
  • Workflow: worker threads
  • Subagent: delegation go further agents
  • Settings and credentials: your saved configuration and environment variables

Plugin still registers tools for ctx.tools, and the docs clear say registered tool schema join prompt assembly. Na this second part many people dey miss. Plugin fit change wetin your agent decide to do even when im own code no do anything unusual, because the description wey e contribute become text wey model dey read. Na the same kind problem as prompt injection against coding agents, but one difference dey: this text enter when you install am, and e go remain until you remove the plugin.

How dsh dey find and load plugins?

No global plugin directory dey. A running dsh na plugin tree wey e compose during boot from ordered layers, and the unit wey hold your choices na profile. $DSH_HOME default na ~/.dsh, and every profile dey inside $DSH_HOME/profiles/<name>. The web and headless profiles dey create themselves the first time you use dem from shipped templates.

A profile directory get two files wey decide everything:

  • package.json, wey get the out-of-tree plugin dependencies plus one dsh.profile manifest wey carry the ordered bundles list
  • cordis.patch.yml, your own patch layer on top of those bundles
ls ~/.dsh
ls ~/.dsh/profiles/web

During boot, e apply the layers for this order, and later layers dey override earlier ones:

  1. one empty root
  2. the profile bundles, for the order wey the manifest list dem
  3. the profile's cordis.patch.yml
  4. $DSH_HOME/cordis.patch.yml
  5. any --patch <path> overlays wey you pass for the command line

Two flags fit print the composition result without starting anything:

dsh --profile web --dump-default-config
dsh --profile web --dump-config

--dump-default-config print the composed tree by itself. --dump-config add the profile and home patch layers, so na the closest way to get a correct inventory of wetin your next boot go load. Read am before you trust machine wey you inherit.

One warning about those patch files. Config no be inactive data here, because the format allow !!js tagged values under a plugin's config block. One cordis.patch.yml snippet wey you copy from forum post na code, so treat am the same way you go treat shell script from that source.

Wetin dsh plugin add actually dey run?

dsh plugin --profile <name> <args> dey forward im arguments go pnpm inside that profile directory, so pnpm must dey for PATH. The verbs na pnpm verbs:

dsh plugin --profile web add '<package-or-git-spec>'
dsh plugin --profile web remove '<package-name>'
dsh plugin --profile web why '<package-name>'
dsh plugin --profile web update

The security model for installing dsh plugin na the same security model for installing any npm-style dependency, plus one extra step wey loads the result inside your agent. The package carry im own dependency tree, and every package for that tree go end up for the same process. Everything for how npm supply chain attacks fit reach server apply here without change.

pnpm 10 and later no dey run dependency build scripts by default, and approval dey per package through pnpm approve-builds or the allowBuilds setting. pnpm 11 replace the older onlyBuiltDependencies setting with allowBuilds, so any guide or old config wey still use onlyBuiltDependencies no dey approve anything for current pnpm. Check the pnpm wey you get:

pnpm --version

That default good to keep, but na also the safety feature wey people dey misunderstand pass for the ecosystem. Blocked build scripts stop code from running during installation. Dem no do anything about the plugin itself, because the main purpose of plugin na for the harness to import am and call am for the next boot. Plugin no need postinstall hook. You don invite am in.

Wetín to read before you install dsh plugin

Download the published tarball and read am. Nothing go execute when you unpack archive.

npm pack '<package-name>@<version>'
tar -tzf '<package-name>-<version>.tgz'
tar -xzf '<package-name>-<version>.tgz'
less package/package.json

Four fields for that package.json go tell you most of wetin you need. Read scripts for preinstall, install and postinstall entries. Read dependencies for names wey you no recognise, or names wey dey just one character different from ones wey you recognise. Read bin for anything wey the package want for your PATH. Read main or exports for the entry file, then open that file and follow am.

Then read the code wey go actually load. Plugin wey advertise notification tool no get reason to read ~/.ssh, call host wey you never hear about, or spawn shell. If the package ships only bundled or minified JavaScript and no matching source dey for public repository, na your answer be that. Prefer plugins wey you fit read their source, and prefer small ones.

You fit also question the registry without installing anything:

pnpm view '<package-name>' dependencies
pnpm view '<package-name>' versions

Package wey publish last week, get one version, no repository field and get name wey dey shadow popular thing, na the oldest trick for any registry. Verify downloads with checksums na the habit wey dey next door: know exactly wetin you fetch before you allow am run.

Pin version, and keep the lockfile

Floating version range mean say code wey dey inside your agent process fit change for any install or update without you decide anything. Pin am.

dsh plugin --profile web add --save-exact '<package-name>@<version>'

Flag placement dey vary between pnpm versions, so check the result instead of trusting the command. Open profile's package.json afterwards and confirm say dependency dey show as bare version without any ^ or ~ before am. Na that file dey decide wetin go install.

Then keep the lockfile, wey dey pin the whole transitive tree instead of only the top-level name:

find ~/.dsh -maxdepth 3 -name 'pnpm-lock.yaml'

Copy am go somewhere wey you dey back up, together with profile's package.json. Those two files fit rebuild the same tree for new box. Run dsh plugin --profile web update only when you don decide to move versions, never as routine tidying, and read the lockfile diff afterwards.

For plugin wey you install from git instead of registry, pin the commit instead of the branch. Spec wey get form github:owner/repo#<full commit sha> gives you fixed tree. Branch name gives you anything wey that branch hold the next time pnpm resolve am. Na decision wey you don hand over to another person. The harness itself want the same discipline, because every dsh build wey don publish so far na pre-release (alpha or release candidate), and unpinned install fit resolve to different one on any given day. Na from there most dsh install and version errors dey come.

Plugin market, and wetin “curated” mean

dsh get marketplace, and e dey install as plugin. This one show you something about how the architecture work:

dsh plugin --profile web add dshmarket

After restart, e go show under Settings, then Plugin Market. The README talk clearly about the limits. Install dey restricted to sources wey dey listed for a curated registry, and e go reject anything else. Build scripts dey blocked by default. To enable one, you need approve each package. E go flag terminal plugins before dem enter web profile. The sentence wey matter pass be say listing no mean endorsement, because third-party code dey inside the plugins.

Curated list dey set a minimum standard. E no read the code for you, and e no fit tell you wetin the next plugin version go do after maintainer account change owner. Treat one-click install the same way you go treat curl | bash from the same author. Another line from that README worth repeating be this: exported backup fit contain credentials from your profile config, so never attach one to public issue or paste site. If you want starting list instead of method, dsh plugins wey worth installing na companion post for this one.

Dey run dsh with im own user, no be root

Vetting dey reduce how often bad thing fit enter. Least privilege decide wetin e fit reach when e enter. For VPS, to set up this second part no hard.

Give the harness im own unix account and im own home directory, and never run am as root:

sudo adduser --disabled-password --gecos "" dshrun
sudo -iu dshrun

Inside that session, start the harness so e go write im home under that account:

npx @deepseek-ai/dsh web

The web UI dey serve for http://127.0.0.1:3080 by default. Leave am there. If you don ever click that printed link from another machine and nothing come back, wetin dsh mean when e print that address explain why. The shipped web composition dey check Host and Origin headers and e require browser-session authentication for every Host API route and stream, while dsh web dey refuse --host 0.0.0.0. See that login as second lock, no be reason to publish 3080. Agent dey behind am, and that agent get shell. So if session token leak or login bug happen on public port, stranger fit get root-less remote shell through friendly interface. Reach am from your laptop through SSH tunnel instead:

ssh -L 3080:127.0.0.1:3080 you@your-vps

Then confirm say nothing dey listen on public address:

ss -lnt | grep 3080

The local address suppose read 127.0.0.1:3080. If e read 0.0.0.0:3080, something apart from the shipped dsh web defaults put am there, like different composition or config change, and that composition get im own authentication. The project docs talk say underlying web server no bring authentication or Origin policy by itself. So until you know wetin dey protect that port, assume say na your firewall alone dey between stranger and your agent, then move am back to loopback. The reason behind how to run Claude Code safely for VPS still apply to dsh. Give the agent one workspace directory wey e allowed to spoil, and keep anything wey you no fit rebuild away from that machine. Better still, treat the box like disposable VM for coding agents, because rebuilding VPS fit take one hour, while auditing one fit take one week.

Wia your keys dey, and why file permissions no fit do everything

dsh dey keep API keys for $DSH_HOME/.credentials.yaml and environment values for $DSH_HOME/.env, while model settings dey for $DSH_HOME/settings.yaml and session logs dey under $DSH_HOME/sessions. Which key suppose dey for which file, and wetin actually comot from the box for each mode, na the topic of how to configure dsh API keys, models and endpoints. E good make you settle this before you add plugin, because every key wey you connect na another thing wey plugin fit read. Lock down the two sensitive files:

chmod 600 ~/.dsh/.credentials.yaml ~/.dsh/.env
ls -l ~/.dsh

Mode 600 give the owner permission to read and write, and e no give anybody else anything. This matter for both notations (numeric versus symbolic chmod modes). Make we clear about wetin e really provide. File modes protect those files from other accounts for the box. Dem no protect against plugin, because plugin dey run as the user wey own the files, inside the process wey dey read dem. Na why how to keep secrets away from AI agent mean say you no suppose put dem for the machine at all. dsh box suppose hold only the one model key wey e need. Your cloud credentials and signing keys suppose dey somewhere else.

Why plugin wey dey read web dey change threat model

Web seam dey give plugins search and fetch providers. Plugin wey pull page enter your session dey pull text wey attacker fit write. Model prompt no separate instructions from data, so fetched page fit carry line wey address your agent, and harness wey hold shell capability dey one obedient step away from running am.

The control dey already inside harness. dsh-base, the shared first bundle of the web, headless, sdk and acp profiles, ships the sandbox and approval policy (the sdk-minimal profile deliberately leaves am out). Use am. Session wey fit fetch untrusted pages suppose require approval for anything wey write or execute, so fetched instruction no fit become action by itself. Putting agent actions behind approvals explain how to reason about where that line suppose dey. The relationship dey work for both directions too, because your own server na page wey another person's agent go fetch, as e happen for blocking AI crawlers for your server.

How I fit check wetin plugin change?

Make snapshot before, install am, make another snapshot after, then read the difference.

dsh --profile web --dump-config > /tmp/dsh-before.txt
dsh plugin --profile web add '<package-name>@<version>'
dsh --profile web --dump-config > /tmp/dsh-after.txt
diff -u /tmp/dsh-before.txt /tmp/dsh-after.txt

The diff dey show which plugin entries the install add to the composed tree. If plugin wey you install for one small feature add several entries wey you no fit explain, stop and read the source before you boot am. dsh plugin --profile web why <package-name> answer the other question: which of your direct dependencies pull a particular package enter.

Installed packages dey enter the profile own node_modules, for $DSH_HOME/profiles/<name>/node_modules, so you fit also check the tree for disk:

ls ~/.dsh/profiles/web/node_modules

Keep one second profile wey you never use for experiments. If install spoil the harness, booting dsh --profile <clean-name> go tell you within seconds whether na the plugin cause am.

How I fit remove a dsh plugin?

dsh plugin --profile web remove '<package-name>'
dsh --profile web --dump-config > /tmp/dsh-after-removal.txt
diff -u /tmp/dsh-before.txt /tmp/dsh-after-removal.txt

If you remove dependency, e no always remove configuration. Entries wey dem write inside the profile cordis.patch.yml go still remain, because na your file and the harness no go rewrite am for you. Open the file and delete any block wey name the package wey you remove.

less ~/.dsh/profiles/web/cordis.patch.yml

Then face the part wey no uninstall command fit fix. If you remove plugin because you no trust am again, anything wey e fit read, e don already read am. Rotate the DeepSeek API key for the provider console, and rotate anything else wey dey inside $DSH_HOME. Then find out wetin the unix account wey run am fit reach for the rest of your network.

De short version

  • Read the tarball wey dem publish before installation, start from scripts and the entry file
  • Pin the exact version, or the exact commit for a git spec, and keep the lockfile
  • Install am inside one profile, and keep one clean profile wey you fit boot when something spoil
  • Diff --dump-config before and after every installation
  • Run the harness with its own unix user, for loopback, and reach am through SSH
  • Keep one API key for the box, and rotate am the same day wey you remove plugin wey you no trust again

None of dis mean say you suppose avoid plugins. Na the plugin model make dsh useful, and harness wey you no fit extend na harness wey you go replace. E mean say you suppose know wetin you install, who provide am, the version wey you use, and run everything for place wey you fit rebuild.

FAQ

dsh dey sandbox plugins from each other?

No. Cordis dey load plugin inside harness process, and plugin fit reach the documented capability seams, including shell, filesystem, web, subprocess, subagent and credentials. dsh-base, wey be the shared first bundle of the web, headless, sdk and acp profiles, dey ship the sandbox and approval policy wey control wetin agent tools fit do, and na that policy dey protect you. No per-plugin permission boundary dey, so the honest model be say when you install plugin, you extend your trust to the author and every package for its dependency tree.

I fit install dsh plugin without running its install scripts?

pnpm 10 and later dey block dependency build scripts by default, and dsh plugin ... add dey forward to pnpm, so for current pnpm, install no go run package scripts unless you approve that package. Confirm your version with pnpm --version. This no mean say unread plugin safe. Plugin own code go run for the next boot because harness deliberately loads am, and no install-time restriction fit stop that.

Where dsh plugins and their config actually dey?

$DSH_HOME dey default to ~/.dsh. Profiles dey for $DSH_HOME/profiles/<name>. Each profile get one package.json with its plugin dependencies, plus dsh.profile manifest of ordered bundles, and cordis.patch.yml patch layer. Installed packages dey inside each profile own node_modules, for $DSH_HOME/profiles/<name>/node_modules. Keys dey for $DSH_HOME/.credentials.yaml, environment values dey for $DSH_HOME/.env, and home-level $DSH_HOME/cordis.patch.yml dey apply over every profile. Run dsh --profile web --dump-config to see the composed result without booting.

E safe to install from dsh plugin market?

The market restricts installs to sources for curated registry and blocks build scripts unless you approve dem per package. This na real improvement over pasting package name from chat window. But the market own README still talk say listing no be endorsement, because the plugins na third-party code from other people. Read the source and pin the version. Keep the harness for user account, and ideally one machine, wey you fit afford to lose.