Bundle
dsh-single-instance-guard
DSH_HOME single-instance lock plugin: aborts startup loudly when another dsh server already uses the same data directory, preventing concurrent session-log writes (the 'seq gap in committed region' corruption class)
- Source
- Tang-mm95
- stars
- 1 stars
- License
- MIT
- Updated
- Updated yesterday
Readme
# dsh-single-instance-guard
A zero-dependency [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that takes an exclusive lock on the `DSH_HOME` data directory at startup and **aborts loudly** when another live dsh server already uses it.
[中文说明](README.zh.md)
## Why
The JSONL session persistence backend documents **"One live writer per session"** and has no cross-process defense. Two dsh servers sharing one `DSH_HOME` (a desktop wrapper spawning its own server, or two `dsh web` processes) append batches with stale sequence cursors, corrupting session logs:
```
history unavailable for session "…": Error: corrupt session log: seq gap in committed region …
```
See [this report](https://github.com/deepseek-ai/deepseek-harness/discussions/2571) for the full diagnosis, a read-only scanner, and a manual repair procedure.
This plugin turns the silent corruption into a loud startup failure.
## Install
After publishing to npm:
```bash
dsh plugin --profile <profile> add dsh-single-instance-guard
```
Manual (any dsh install): add the row to the profile's `cordis.patch.yml` **before** session-related bundles:
```yaml
- insert:
- id: single-instance-guard
name: 'dsh-single-instance-guard'
```
The guard is a [profile bundle](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/bundle): its manifest declares the same patch, so the CLI installs it as a patch layer.
## How it works
1. Creates `<DSH_HOME>/.dsh-server.lock` atomically (`O_EXCL`), holding `{ pid, startedAt, hostname }`.
2. On conflict, probes the holder's pid liveness: a live holder aborts startup with a clear bilingual error; a stale lock (dead pid or unparsable file) is removed and acquisition retried once.
3. On process exit the lock is removed — only if still owned by this process.
DSH_HOME is resolved exactly like dsh itself: `$DSH_HOME` or `~/.dsh`. Servers with different data roots never conflict.
## Known limits
- The unlink+retry path has a tiny race when two processes discover the same stale lock simultaneously; the atomic `O_EXCL` write still admits exactly one winner, and the loser fails correctly on the retry.
- The lock protects against concurrent servers sharing one `DSH_HOME`. It does not protect against two dsh instances deliberately pointed at the same **session file** via different roots (unsupported anyway).
## Development
```bash
npm test # node --test
```
## License
MIT
Install
dsh plugin --profile web add github:Tang-mm95/dsh-single-instance-guard#666bad9ddedeb21d496ba96368bdbeb9d1752704
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-single-instance-guard from the hub