dsh-home-sync
overdustd7/dsh-home-sync
通过一个 git 远端同步 DSH 主目录与 Mnemon 记忆数据:拉取、推送、按间隔自动同步、跨设备迁移、初始化备份,遇冲突在推送前停下并保留本地提交;面板记录最近 100 次操作各自的备份路径。
安装
dsh plugin --profile web add github:overdustd7/dsh-home-syncREADME
dsh-home-sync
English | 简体中文
Git-based sync for DSH configuration and Mnemon memory plugin data, with automatic two-way sync, cross-device migration, initialization backups, conflict detection, and a web UI.
Version 0.2.1 completes the maintenance improvements and fixes gaps in history validation, initialization, and status reporting found during a second review.
Requirements
- Node.js 20 or newer and Git.
- A DSH host providing
webServer,timer, andconnection.requestRejection. - Local compatibility was verified with DSH 0.1.2-rc.1, Node.js 24.20.0, and Git 2.55.0.windows.4.
Installation
From npm (recommended)
dsh plugin --profile web add dsh-home-sync
Restart DSH web after the install and refresh the browser to load the UI. The published package needs no build step or third-party dependencies.
From GitHub
dsh plugin --profile web add github:OverDustD7/dsh-home-sync
Local development (from source)
dsh plugin --profile web add link:D:/Project/DSH/dsh-home-sync
A link: installation reads that source directory directly: restart DSH web after backend changes and refresh the browser for UI changes.
Offline / air-gapped devices
Build a tarball and copy it to the target machine:
npm pack --pack-destination dist
On the target, run the following from the directory containing the archive:
dsh plugin --profile web add ./dsh-home-sync-0.2.3.tgz
If the package manager cannot install a local tarball, extract it into a permanent directory and install the extracted package subdirectory using link:. Do not use a temporary extraction directory as a permanent link target.
Synchronization behavior
- Pull checks the remote and attempts a fast-forward. It fails if the branches have diverged or local changes could be overwritten, and does not create local commits. The operation explicitly disables
merge.autoStashto avoid reporting success when restoring stashed changes produces conflicts. The index is also checked after merging. - If the tracking ref (
refs/dsh-home-sync/branches/<branch>) was left corrupt by a killed host, the next sync deletes that broken ref file and re-fetches instead of failing forever; the operation message then reads "已修复损坏的跟踪引用并重新拉取。". - Sync to remote fetches and validates the remote tree, commits allowed local changes, integrates remote changes, and pushes. It retries unpublished commits even when the working tree is clean.
- Automatic sync performs the full synchronization cycle at the configured interval, including checking remote changes when there are no local changes. Saving switches or the interval immediately rebuilds the scheduled tasks. Configuration saves and other write requests return a busy response while an operation is running.
- The checked-out branch must match the configured branch. Diverged branches may be integrated with a regular Git merge. If a content conflict occurs, the plugin stops before pushing, aborts the unfinished merge, and preserves local commits. Resolve the conflict manually before retrying; the plugin does not automatically force-push.
- Status refreshes every 15 seconds and distinguishes uncommitted files, commits to push or pull, and read failures. New directories are counted by their individual files. Ahead/behind counts reflect the most recent successful fetch.
- Immediate feedback appears in a toast. The Operation history section is collapsed by default; expand it to view and copy backup paths and recovery instructions. Up to 100 recent manual and background operations are stored beside the home directory in
<DSH_HOME>.home-sync-history.json, subject to a total size limit. These records survive restarts and are excluded from Git synchronization. They contain results, timestamps, and backup paths, not synchronized file contents. A damaged history file is preserved and reported; a history write failure does not undo a successful sync. - An open panel and its floating button follow host and system theme changes without losing current input or expanded sections. The current web interface uses Chinese labels.
All HTTP data endpoints use the host's authentication and Host/Origin validation. The plugin does not activate without that service. POST requests accept JSON only. The UI script itself contains no user data.
Files included in synchronization
.gitignore,settings.yaml, and the rootcordis.patch.yml.- Under
profiles/web/:package.json,pnpm-lock.yaml,pnpm-workspace.yaml,cordis.yml,cordis.patch.yml, and.dsh-market/state.json. - Non-hidden files under
mnemon/, excluding path components nameddata,state,credential,credentials,secret, orsecrets, and files ending in.log,.wal, or.shm.
Credentials, sessions, attachments, and node_modules are not added to commits. Forbidden files already staged or tracked block synchronization.
Before pushing, the plugin audits the commits it is about to publish, together with their trees, up to 1,000 commits: when the only push destination is also the fetch remote, it audits just the range from the fetch remote's tracking ref to HEAD — the earlier ancestors already live in that repository, so this push cannot expose them anew, and a deep history no longer blocks every push. As soon as an extra push destination is configured (it may never have received that history), the plugin falls back to auditing the full history reachable from HEAD — and it also falls back when no tracking ref exists yet. A forbidden file anywhere in the audited set blocks the push even if it has since been deleted; a set exceeding the limit must first be manually reviewed and organized into a clean synchronization history.
Pushes update only the selected branch; they do not automatically follow tags or mirror other references. Pushes to multiple repositories are not a single transaction, so partial network failures may require retries. The plugin does not redact fields inside allowed files. Do not put content you do not want uploaded in synchronized configuration or memory files.
Only ordinary files are accepted. Symbolic links, hard links, submodules, paths outside the home directory, Windows reserved names, and case collisions are rejected. Git hooks and commit signing still follow your Git configuration. Git subprocesses use timeouts and non-interactive credential settings.
Device initialization and recovery
Both initialization modes create backups. The merge mode means back up, reset, then merge manually; it does not automatically combine two sets of memories.
- Fetch the remote into an independent temporary repository and validate target paths and file types.
- Check local path collisions and back up every file that may be overwritten or removed, the original Git metadata, and the plugin configuration.
- Verify that local files did not change during backup, apply the allowed files, and configure the target branch, origin, and upstream.
- Pause automatic sync and startup pull after success, allowing you to inspect the results, merge any required content manually, and re-enable automation.
Initialization supports a new directory, an independent repository that has been initialized with git init but has no first commit, and switching an existing setup to a remote with unrelated history. A successful response includes the actual configuration, and the UI immediately updates the automatic switches. Initialization explicitly resets settings, so the current form is replaced with the server configuration.
Backups are stored beside DSH_HOME at <DSH_HOME>.home-sync-backups/init-*, outside the synchronized working tree. With the default home directory, this resembles ~/.dsh.home-sync-backups/init-*.
| Backup entry | Contents |
|---|---|
files/ | Original files |
repository/ | Original .git, including local history, index, and remote configuration |
config.json | Original plugin configuration, if it existed |
manifest.json | Target, operation state, original file existence, and checksums |
MERGE.md | Recovery instructions |
Caught execution errors trigger an attempt to restore the original state. Automatic recovery cannot be guaranteed after a power failure or forced termination. Stop DSH, restore files according to the manifest, restore repository/ as .git, restore the original configuration, and remove files the manifest marks as originally absent. Do not restore while DSH is writing memory data. Initialization explicitly rejects Git worktrees and linked .git entries.
Other programs must also avoid writing synchronized files during initialization. The plugin lock coordinates its own instances; it cannot pause other plugins or manual Git operations.
Configuration and operation lock
Configuration is stored at <DSH_HOME>/dsh-home-sync.json.
| Setting | Purpose or constraint |
|---|---|
branch | Synchronization branch |
autoPullOnStartup | Pull when the plugin starts |
autoSync | Run periodic full synchronization |
syncIntervalSeconds | Integer from 15 to 86,400 |
commitMessage | Commit message, 1–1,000 characters |
sshBatch | Use SSH batch mode |
The legacy autoSyncOnStartup setting migrates to autoSync. If both are present, autoSync takes precedence.
A damaged configuration does not fall back to defaults and enable automatic tasks. Repair or restore the JSON file, then restart DSH. Configuration writes flush a temporary file before replacing the original; initialization also creates a separate configuration backup.
The cross-process lock directory is <DSH_HOME>.home-sync-lock; its owner.json records the owner pid and start time and doubles as the heartbeat. A lock left behind by a killed process or an abrupt shutdown is taken over automatically: when the owner information is missing or corrupt, the owning process no longer exists, or the heartbeat has not been refreshed for 15 minutes, the old lock is renamed to <DSH_HOME>.home-sync-lock.stale-<timestamp> and the operation proceeds (the takeover is reported in the operation message). A fresh lock held by a live process still returns busy. Recover an unfinished initialization from its backup first.
Verification
npm run check
npm test
Save documentation as UTF-8. When editing it with scripts, specify UTF-8 for both reading and writing; do not rely on a system code page. npm run check:docs validates the README text and language links, and runs as part of npm run check and the normal npm packaging lifecycle. Avoid --ignore-scripts when packing, because it skips this check.
Tests use system temporary directories, synthetic files, and local Git remotes. They do not access your real DSH home or accounts. Test repositories are retained in temporary directories for diagnosis.
Browser tests use a separate temporary Chrome/Edge profile and are skipped if no supported browser is found. Set CHROME_PATH to select a browser. Restricted environments may require running browser tests with normal user permissions.
For host compatibility tests, DSH_HOST_MODULES can point to an installed DSH node_modules/@deepseek-ai directory. These tests are skipped if no host installation is found. They reuse the real host routing and source-validation methods with a synthetic identity-cookie implementation.
Development and audit documentation (inspection / repair / review reports and audit/ evidence) is kept locally as internal material and is not distributed with this repository. test/ and npm test are the public, reproducible verification entry points.
Historical review: September 7, 2026
The following describes the 0.2.0 review. Its conclusion that no new high-risk defects had been found was superseded by the second review. Development and audit reports (including the 0.2.1 second review) are kept locally as internal material and are not distributed with this repository; this section documents the earlier independent review for traceability. Immediate logs remain visually hidden; a separate collapsed operation-history section is now available.
The earlier review covered lib/index.js, lib/sync.js, lib/ui.js, package.json, and cordis.patch.yml, comparing them with the two original reports. It reported implementations for all 17 original findings and 35 passing tests with no failures. Later independent probes found gaps that those tests did not cover.
That review also restored toast feedback instead of showing a log box inside the card after saving settings. The hidden #log remains an aria-live region for screen readers. This interaction is preserved in 0.2.1.
Both initialization modes still perform backup, reset, and pausing of automatic tasks; their messages explain the intended workflow. Migrating an existing device requires manually selecting content from the backup using MERGE.md. HTTP data endpoints require a trusted authenticated session; an unauthenticated request returning 401 is expected. Backend changes require a DSH web restart, and UI changes require a browser refresh.