Sync Claude Code sessions across devices using Cloudflare R2 with end-to-end encryption.
> /plugin marketplace add tawanorg/claude-sync> /plugin install claude-sync@claude-sync
Encrypted with age • R2 / S3 / GCS / WebDAV supported
Quick Start • Setup Guide • Commands • Shell Integration • Security
--scope sessions to sync only conversation data (skip plugins/node_modules)claude-sync update downloads and verifies SHA256 checksumspush, pull, status, diff, conflicts commands# Install
npm install -g @tawandotorg/claude-sync
# Set up (interactive wizard)
claude-sync init
# Push your sessions
claude-sync push
# Install
npm install -g @tawandotorg/claude-sync
# Set up with SAME storage credentials
claude-sync init
# Select same provider (R2/S3/GCS/WebDAV)
# Enter same bucket name and credentials
# Choose "Passphrase" for encryption
# Enter the SAME passphrase as first device
# ✓ Encryption key verified <-- confirms passphrase matches!
# Preview what would be synced
claude-sync pull --dry-run
# Pull sessions (creates backup if you have existing files)
claude-sync pull
Same passphrase = same encryption key. The init verifies your passphrase can decrypt remote files before completing.
| Provider | Free Tier | Best For |
|---|---|---|
| Cloudflare R2 | 10GB storage | Personal use (recommended) |
| AWS S3 | 5GB (12 months) | AWS users |
| Google Cloud Storage | 5GB | GCP users |
| S3-compatible | varies | Backblaze B2, MinIO, Wasabi, DigitalOcean Spaces, self-hosted |
| WebDAV | Self-hosted (unlimited) | Nextcloud/ownCloud users |
claude-syncYou'll need: Account ID, Access Key ID, Secret Access Key
You'll need: Access Key ID, Secret Access Key, Region
You'll need: Project ID, Service Account JSON file (or use gcloud auth application-default login)
Any provider exposing an S3-compatible API works through the S3-compatible (custom endpoint) option. Create a bucket and an application key with your provider, then supply its S3 endpoint URL.
Example (Backblaze B2):
claude-sync init --provider s3-compatible --endpoint https://s3.us-west-004.backblazeb2.com
You'll need: Endpoint URL, Access Key ID, Secret Access Key, Bucket. The signing region is auto-detected from the endpoint (e.g. us-west-004); for providers that ignore it, auto is used.
For servers that don't resolve buckets as subdomains (e.g. Ceph RGW, or MinIO without wildcard DNS), add --use-path-style to address objects as endpoint/bucket/key instead of bucket.endpoint/key:
claude-sync init --provider s3-compatible --endpoint https://ceph.example.com --use-path-style
It's off by default and unnecessary for Backblaze B2, Wasabi, and DigitalOcean Spaces, which all support virtual-hosted addressing.
Custom endpoints automatically relax the AWS SDK's default integrity-checksum headers, which some S3-compatible providers reject. AWS S3 behavior is unchanged.
No bucket to create — just point at your existing WebDAV server.
https://your-server/remote.php/dav/files/USERNAME/You'll need: WebDAV URL, Username, App password
The wizard will create a claude-sync subdirectory automatically.
claude-sync init
The interactive wizard will guide you through:
~/.claude-sync/age-key.txt to other devices# Upload local changes
claude-sync push
# Download remote changes
claude-sync pull
| Path | Content |
|---|---|
~/.claude/projects/ | Session files, auto-memory |
~/.claude/plans/ | Implementation plans from plan mode |
~/.claude/tasks/ | Task tracking state |
~/.claude/history.jsonl | Command history |
~/.claude/agents/ | Custom agents |
~/.claude/skills/ | Custom skills |
~/.claude/plugins/ | Plugins |
~/.claude/rules/ | Custom rules |
~/.claude/settings.json | Settings |
~/.claude/settings.local.json | Local settings |
~/.claude/CLAUDE.md | Global instructions |
init asks whether to sync everything or just conversation data; you can also set it with --scope:
| Scope | Syncs | Use when |
|---|---|---|
full (default) | everything in the table above | you want settings, skills, agents, and plugins mirrored too |
sessions | projects/, history.jsonl, tasks/, plans/ only | you just want claude --resume to work across machines |
claude-sync init --scope sessions
Why sessions exists: full includes plugins/, whose plugin caches bundle node_modules and Python .venv trees — thousands of large, machine-/arch-specific files that are regenerated on demand and should not be synced. sessions skips them, keeping syncs small, fast, and portable. The scope is saved in ~/.claude-sync/config.yaml and applies to every push/pull.
Claude Code indexes project sessions by absolute filesystem path:
/Users/alice/my-app → ~/.claude/projects/-Users-alice-my-app/
/Users/bob/my-app → ~/.claude/projects/-Users-bob-my-app/
Synced verbatim, those would be different projects and claude --resume on the second machine would never find the first machine's sessions. claude-sync solves this by translating paths during sync:
Home directories are mapped automatically. Sessions are stored remotely under a portable ${HOME} token (in both remote keys and transcript content), then rewritten to each device's real home on pull. Different usernames across machines just work.
Other layout differences are configurable. If one machine keeps projects in ~/work and another in ~/Projects, point both at the same token in ~/.claude-sync/config.yaml:
# machine 1
path_map:
~/work: WORK
# machine 2
path_map:
~/Projects: WORK
Sessions under either directory sync to the shared ${WORK} namespace and resume correctly on both machines.
Upgrading from an older version? Run claude-sync migrate once on each device to convert existing remote data to portable keys. Paths the current device doesn't own are left for the other device's migrate run.
claude-sync init # Set up configuration (interactive wizard)
claude-sync push # Upload local changes to cloud storage
claude-sync pull # Download remote changes from cloud storage
claude-sync status # Show pending local changes
claude-sync diff # Show differences between local and remote
claude-sync conflicts # List and resolve conflicts
claude-sync rebuild-history # Rebuild ~/.claude/history.jsonl from session files
claude-sync reset # Reset configuration (forgot passphrase)
claude-sync migrate # Convert legacy remote keys to portable path-mapped keys
claude-sync update # Update to latest version (verifies release checksums)
claude-sync changelog # Show release history
claude-sync --help # Show all commands
claude-sync pull # Normal pull (prompts if existing files)
claude-sync pull --dry-run # Preview what would change
claude-sync pull --force # Skip confirmation prompts
claude-sync pull --rebuild-history # Also rebuild history.jsonl after pulling
history.jsonl is synced as a single file, so pushes from two devices are
last-writer-wins and one device's prompt-history entries can be lost — which
breaks the /resume session picker. Session files sync cleanly (one file per
session), so the history can always be reconstructed from them:
claude-sync rebuild-history # One-off rebuild
claude-sync pull --rebuild-history # Rebuild automatically after a pull
Every existing entry is preserved, recovered prompts are merged in and sorted by
timestamp, and the previous file is kept as history.jsonl.bak.
claude-sync init # Full setup wizard
claude-sync init --passphrase # Re-enter passphrase only (keeps storage config)
claude-sync init --force # Reset everything, start fresh
claude-sync push -q # No output (for scripts)
claude-sync pull -q
claude-sync update --check # Check without installing
claude-sync update # Download and install latest version
claude-sync changelog # Show recent releases
claude-sync changelog --limit 5 # Show last 5 releases
Skip specific files or directories during sync by adding exclude patterns to your config (~/.claude-sync/config.yaml):
exclude:
- "*.tmp"
- "projects/*/node_modules/*"
- "projects/*/.git/*"
Patterns use glob syntax and are matched against paths relative to ~/.claude.
Add to ~/.zshrc or ~/.bashrc:
claude-sync is a Claude Code plugin, indexed on Flowy in the CLI Tools category. Install it with the command on its page. Free and open source.
Is this plugin yours?
Claim it with GitHubSubmit a pluginPromote it