Releases: jyshnkr/claudesync
Releases · jyshnkr/claudesync
v0.3.0 — Security hardening, reliability fixes, pair & autostart
What's New in v0.3.0
Security
- Allowlist sanitizer —
.claude.jsonsync switched from blocklist to allowlist: only explicitly safe fields are transferred; unknown future fields stay local by default, preventing silent data exfiltration - Dedicated
known_hosts— SSH host keys stored in~/.claudesync/known_hosts, separate from~/.ssh/known_hosts; rogue re-keys are caught on subsequent connections - XML-escaped plist generation — All user-supplied values are XML-escaped before being written to launchd plists
- Absolute
launchctlpath —/bin/launchctlused throughout to prevent CWE-426 PATH hijacking - Recursive sensitive-key stripping —
env,apiKey,token,secret,password, etc. are recursively stripped frommcpServersandprojectsin.claude.jsonsanitization, including from lists autostartinput validation — Remote name validated with strict regex; rejects..,/, and empty values to prevent path traversal and XML injection
Added
claudesync pair --name <n> --address user@host— One-command two-machine setup: tests SSH, auto-detects remote home, saves config, and runs an initial pushclaudesync autostart enable/disable <remote>(macOS) — Installs/removes a launchd plist (~/Library/LaunchAgents/com.claudesync.<remote>.plist) to auto-pull every N seconds (default 5 min)- Human-readable conflict output — Conflict resolution now shows
← LOST / ← WONlabels with relative timestamps (3 days ago,2 hours ago)
Fixed
- Manifest file locking —
fcntl.LOCK_EXprevents lost updates when two concurrent syncs race on the manifest file - Versioned remote agent — SSH Python one-liner replaced with a standalone
remote_agent.pydeployed to~/.claudesync/on the remote; eliminates shell quoting edge cases and SSH banner pollution history.jsonlconfig parsing —bool("false")wasTrue; replaced with_parse_bool()helperinclude_historyforwarded to post-pull manifest rebuildpairSSH probe hardened —echo $HOMEprobe wrapped intry/exceptforTimeoutExpired/OSErrorpaircelebration guard — "✓ Paired!" shown only when push has no errorsuninstall_plist— CatchesFileNotFoundErrorwhen launchctl binary is missingmerge_pulled_claude_json— Strips sensitive nested keys from remote overlay before merging- CLI autostart handlers — Catch
ValueErroralongsideCalledProcessError - Exception chaining —
raise ... from eused throughout for correct tracebacks remote_agent.hash_files— Skips unreadable files rather than crashingremote_agent.__main__— Validates JSON arg islist[str]; exits 1 for bad input
Changed
history.jsonlis opt-in — Conversation history no longer syncs by default. Enable withsync.include_history = truein~/.claudesync/config.tomlinclude_historyis keyword-only onEngine.push,Engine.pull,Engine._sync,Engine.dry_runto prevent positional misuse
Stats
- Tests: 108 → 155 (+47 new tests)
v0.2.0 — Security fixes, bug fixes, documentation
Security
- Fix bulk restore path traversal bypass in
restore_backup()— the restore-all branch now checks both that source files resolve inside the backup archive and that destinations resolve inside$HOME - Preserve file permissions on atomic replace in
merge_pulled_claude_json(),save_manifest(), andsave_config() - Validate
backup_idas a single safe path segment; rejects..,., multi-segment values like../../etcthat could escapeBACKUP_DIR - Atomic restore writes via
_atomic_copyhelper: writes to a temp file in the same directory thenos.replace()(POSIX-atomic rename), blocking symlink write races is_dir()guard on backup entry lookup — rejects a regular file atBACKUP_DIR/<backup_id>that would silently passexists()- Close TOCTOU window in
_atomic_copyviadirfdinode verification: opensdest.parentwithO_NOFOLLOW|O_DIRECTORY, compares(st_ino, st_dev)to the precedinglstat, then creates the temp file against the held fd viaos.openat
Fixed
- Rebuild local manifest after pull to record post-sync file state
- Translate local paths to remote equivalents in
_build_manifests()before querying remote file hashes - Handle
FileNotFoundErrorwhen SSH binary is missing →SyncError - Validate JSON root is a dict in
sanitize_claude_json(),merge_pulled_claude_json(), andload_manifest() - Count
.claude.jsontransfers inSyncSummary.files_transferred
Changed
SyncedFileEntry.last_syncedis now optional (TypedDict compat for legacy manifests)dry_runis keyword-only on_rsync_global/_rsync_project- Validate
sync.strategyat config load time
Added
README.md,CHANGELOG.md, updatedSECURITY.md- 20 new tests (88 → 108 total), including 18 covering backup creation, listing, restore, path-traversal guards,
backup_idvalidation,is_dirguard, and symlink rejection in_atomic_copy
Full changelog: https://github.com/jyshnkr/claudesync/blob/main/CHANGELOG.md
v0.1.0 — Initial release
Added
- Bi-directional Claude Code context sync over SSH
- CLI commands:
init,push,pull,status,diff - Remote management:
remote add,remote list - Project registration:
project add,project list - Conflict detection with last-write-wins resolution; automatic backup of the losing side with configurable rotation
- Per-remote SHA-256 + mtime manifest for accurate change detection
~/.claude.jsonsanitization — strips OAuth tokens, API keys, and other auth fields before transfer; merge on pull preserves local auth fields- Rsync-based file transfer with SSH key authentication
- Atomic writes on all config/manifest updates (temp file + rename)
- Path-traversal guards on backup restore
- 88 tests
Full changelog: https://github.com/jyshnkr/claudesync/blob/main/CHANGELOG.md