If you’re like me, you’ve probably fallen in love with Claude Code for its powerful coding assistance. But there’s a frustrating limitation: your Claude Code conversations are stored locally. Switch machines, close your laptop, or even reinstall Claude Code, and poof – your history is gone.
That’s where claude‑sync comes in. It’s an open‑source tool that automatically syncs your Claude Code sessions across all your devices, while keeping your data private and under your control.
The Problem with Local‑Only Storage
Claude Code keeps everything on the local file system for simplicity, which is fine for a single‑machine workflow. In a multi‑device world, however, you constantly lose context when you move to a new computer or need to reinstall. Re‑explaining a half‑finished refactor or digging through old prompts wastes precious time.
How claude‑sync Solves It
claude‑sync backs up every Claude Code session to a remote backend before it ever hits the network, encrypting the data on your machine with AES‑256‑GCM. The encryption key never leaves your computer, so only you can decrypt the backup.
Key Features
| Feature | What it means for you |
|---|---|
| End‑to‑End Encryption | Sessions are encrypted locally with a 256‑bit key you generate during claude‑sync init. The key is never stored in the cloud. |
| Pluggable Backends | Choose where the encrypted blobs live – private Git repo, AWS S3, Google Cloud Storage, Cloudflare R2, or any S3‑compatible service. |
| Incremental (hash‑based) Sync | Since v0.4.0 claude‑sync tracks a hash of each resource’s content. On push it calls findLocal({ modifiedSinceLastSync }), filters out unchanged files, hashes the new content with hashContent(), encrypts it, and finally calls updateResourceHashBatch() to persist the new hash. Only files that actually changed are uploaded. |
| Pull with Conflict Detection | When you run claude‑sync pull --all, the CLI compares local and remote hashes. If a mismatch is found, you get an interactive prompt to resolve the conflict. |
| Verbose Logging | Both pull and push now accept a --verbose flag. With it enabled you see detailed error messages, timing information, and a list of resources that failed to sync. |
| Dynamic Version Reporting | The CLI now reads its version from package.json via createRequire(import.meta.url), so the --version output always matches the published package. |
| Testable Sync Logic | Unit tests now mock hashContent and updateResourceHashBatch, ensuring the incremental‑sync path is exercised without hitting a real backend. |
What Changed in the Latest Release (v0.4.0)
--verboseflag forpull– Previously onlypushhad a verbose mode. The new flag gives you the same level of insight when pulling, making debugging network or decryption issues painless.Hash‑based incremental push – The push pipeline now looks like:
findLocal({ modifiedSinceLastSync }) → filter by stored hash → read() → hashContent() → encrypt() → backend.pushResource() → updateResourceHashBatch()This reduces bandwidth and speeds up sync dramatically, especially for large repositories.
Sync state tracking – After a successful push the CLI records the new hash in a local “sync state” file. The next run only uploads resources whose hash differs from the stored value.
CLI version handling –
src/cli.tsnow usescreateRequireto import the version frompackage.jsoninstead of a hard‑coded string.Documentation updates – The README now clarifies that
claude‑sync pushonly pushes modified resources, and it introduces thepush --allshortcut to force a full upload.
Getting Started
Install the CLI
npm install -g @xxdesmus/claude-sync(If you use GitHub Packages you may need to add an npm token – the README walks you through that.)
Initialize a backend
claude-sync init --git https://github.com/yourusername/claude-sessions-privateReplace the URL with the backend of your choice (
--s3,--gcs,--r2, …). During this step you’ll generate an encryption key and store it locally (~/.claude-sync/key).Install the Claude Code hook
claude-sync install --globalThis adds a small script that triggers a
pushafter every Claude Code session ends.Sync your data
Normal workflow: every time you finish a session the hook runs
claude-sync push. Only the files that changed since the last successful push are uploaded.Force a full upload:
claude-sync push --allDebug a problematic sync:
claude-sync pull --verboseor
claude-sync push --verbose
Behind the Scenes: A Quick Look at the Code
src/commands/push.ts– ImportshashContentandupdateResourceHashBatch. After encrypting a resource it callsupdateResourceHashBatch({ type, id, hash })so the next run knows the file is up‑to‑date.src/commands/pull.ts– Adds averboseoption, collects any pull‑time errors inpullErrors, and prints them when--verboseis set.src/crypto/encrypt.ts– Exposes a purehashContent(data: Buffer): stringhelper that returns a SHA‑256 hex digest used for sync‑state comparison.src/cli.ts– UsescreateRequire(import.meta.url)to pull the version frompackage.json, ensuring the--versionflag is always accurate.- Tests –
push.test.tsnow mockshashContentandupdateResourceHashBatchso the incremental‑sync logic can be unit‑tested without hitting a real backend.
Important Disclaimer
claude‑sync is a community‑built tool and is not affiliated with Anthropic. Please review Anthropic’s Terms of Service and your organization’s data‑handling policies before using this tool.
Looking Ahead
We’re actively iterating on reliability, performance, and UX. Upcoming ideas include:
- Selective conflict resolution UI (graphical prompt instead of terminal text)
- Automatic key rotation with forward‑compatible re‑encryption
- Metrics dashboard to visualize sync latency and bandwidth usage
If you’re interested in contributing, feel free to open an issue or submit a PR on the GitHub repository. Your feedback helps shape the next release.
Happy coding—and never lose