dsh-backup
One-command backup and restore for DeepSeek Harness user data — sessions,
settings, credentials, skills, and plugin config under ~/.dsh, excluding
reinstallable node_modules — with sha256 checksums, integrity verification,
automatic rotation, and scheduled auto-backup that survives restarts. Works on
macOS, Linux, and Windows.
Commands
/backup— immediately back up~/.dshto~/Desktop/dsh-backups/dsh-<timestamp>.tar.gz/backup list— list existing backups (name + size) and auto-backup status/backup verify [prefix|all]— validate archive checksums (default: the newest)/backup restore <prefix|latest> [--dry-run]— restore~/.dshfrom an archive/backup auto <N>|off|status— auto-backup every N hours (1–720; keeps 3 copies below 24h, 7 otherwise; persisted across restarts)/backup --keep N— override the rotation count (default 7)/backup github status|sync— GitHub sync status / push nowbackup_dshtool — same capability for the model (mode=backup|list|verify|restore|auto)
GitHub sync
With config.githubRepo set, every backup (manual, automatic, or panel) is
also pushed to a Git repository — archives, checksum sidecars, and rotation
deletions stay in sync:
- id: dsh-backup
name: 'dsh-backup'
config:
githubRepo: 'your-name/dsh-backups' # owner/repo, full URL, or a local path
Use a private repository — archives contain plaintext credentials. For an
https remote, set the token in the environment (DSH_BACKUP_GITHUB_TOKEN or
GITHUB_TOKEN); it is only written into the sync worktree's credential file
(never process args). Push is HEAD:main --force-with-lease; archives over
90 MB are skipped with a notice. State (last push, last error) lives in
<destination>/auto.json and shows in the panel and /backup github status.
Settings panel (Web)
The same controls have a visual entry: a Backup tab inside Settings → Plugins
(dsh web). It shows the destination, auto-backup state, GitHub sync status, and
every archive with its size, and offers one-click back-up-now, per-archive
verify, download, and restore with a dry-run preview plus explicit confirmation.
Downloads stream from the loopback-only route GET /backup-download/<name>.
The tab talks to the host through the backupPanel Typert Remote namespace
(/api RPC); the browser bundle ships prebuilt in lib/client.js — no build
step at install time.
How restore works
Restore is safe by construction:
- The archive's sha256 is verified first — a corrupt archive never touches existing data.
- Entries are listed and any path outside the backup root rejects the restore (tar path-traversal guard).
- The current
~/.dshis snapshotted, then moved aside to~/.dsh.pre-restore-<timestamp>— restore replaces rather than merges. - The archive is extracted; restart
dshafterwards so restored sessions and settings take effect.
--dry-run shows the archive summary without writing anything.
Configuration (optional)
Plugin config in the active cordis profile:
- id: dsh-backup
name: 'dsh-backup'
config:
destination: '~/Backups/dsh' # default ~/Desktop/dsh-backups
keep: 10 # default rotation count
exclude: # extra tar --exclude patterns
- '*cache*'
githubRepo: 'name/dsh-backups' # optional GitHub sync (see below)
Auto-backup state lives in <destination>/auto.json and resumes after restart.
Security note
Backups contain plaintext credentials (.credentials.yaml, qq-bridge/config.json).
Archives and checksum sidecars are chmod 600 on POSIX (Windows relies on
per-user profile ACLs), but do not sync the backup directory to untrusted
locations, and treat archives as sensitive as your API keys.
Storage note: the plugin writes its own data (archives, checksum sidecars,
auto.json) directly through node:fs, the same pattern as DSH's own session
persistence — the ctx.fs capability is the model-facing sandboxed surface and
does not apply to host-owned storage.
Install
dsh plugin --profile web add dsh-backup
Then restart dsh web (plugin discovery is cached per process) and run /backup,
or open Settings → Plugins → Backup.
Requirements
- macOS, Linux, or Windows 10+ with
tarin PATH (Windows ships bsdtar in System32; Git Bash's GNU tar also works — checksums prefersha256sum/shasumand fall back to an in-process hash on Windows) - DSH
0.1.0-rc.6or compatible
Development
Zero runtime dependencies — the host plugin is lib/index.js. The browser half
lives in src/ and is bundled (zod inlined, React/Cordis external) into
lib/client.js, which is committed so git installs never build:
node scripts/build-client.mjs # rebuild the client bundle after editing src/
node scripts/smoke.mjs # host smoke suite (real temp dir, mocked DSH services)
node scripts/smoke-client.mjs # client bundle: handshake, schemas, tab registration, SSR
License
MIT
No comments yet. Be the first to write one.