The incidents behind dsh-guard. Read before touching a profile, a plugin, or
a restart in a container.
dsh web resolves every bundle name to a real installed package with a
dsh.bundle.patch and fails loud on the first bad entry — no
skip-fallback. Symptom:
dsh: cannot resolve profile bundle "@linxin666/dsh-web-ui-all" from the dsh
installation or /root/.dsh/profiles/web; run 'dsh plugin --profile web install'
Cause seen in the wild: a market install wrote a bundle name whose npm package
has no usable bundle. Until package.json is fixed, every restart
crash-loops the container.
Rule: after any plugin add/remove, run
sh ~/.dsh/dsh-guard/bundle-validate.sh --profile web BEFORE restarting.
--fix removes bad entries; reset-profile.sh restores a known-good profile
(base + web-app + dsh-pet) with a backup. Never hand-delete package.json
when the guard can do it safely.
/app/entrypoint.sh starts dsh web --port 3079 in the background, then
exec node index.js (the proxy on :3080 → 127.0.0.1:3079). Kill dsh web and
it stays dead — you must relaunch it. The proxy itself may throw
ECONNREFUSED on proxied requests while the backend is down, which restarts
the whole container (--restart unless-stopped).
Rule: restart with dsh-guard/restart-web.sh (detached, delayed, waits for
readiness), never by just killing the process from an agent's own tool call —
that can kill the very turn running it.
Use /proc/*/cmdline scans or Node (fetch/net) instead. The guard scripts
already do.
The kernel reports "microsoft", so DSH's native opener picks the WSL path and
spawns wslpath → spawn wslpath ENOENT (and powershell.exe would be next).
Fix is a patch-layer override, not a shim:
# ~/.dsh/profiles/web/cordis.patch.yml
- id: api-gateway
config:
nativeOpen: falseThe UI then shows produced paths as copyable text instead of a dead button.
export default makes DSH's loader unwrap to the bare function and silently
drop inject/name → cannot get property "webServer" without inject at
boot. Use export function apply + export const inject + export const name. Clients must never declare an inject they don't require
(pending (waiting for service: …) forever). Full plugin-authoring lessons:
see the dsh-video-player
repo's PITFALLS.md.
dsh plugin add <local-dir> symlinks the package; Node resolves its imports
from the realpath, outside the profile's node_modules. Bridge
@deepseek-ai/dsh-settings (from ~/.dsh/profiles/node_modules/@deepseek-ai)
and schemastery (from ~/.dsh/profiles/web/node_modules) into the plugin's
own node_modules/.
dsh web --port 3099 --no-open # same profile, captured logs, no risk to :3079(dsh web does not accept --profile; the subcommand already implies web.)