claude‑sync — Secure, Incremental Sync for Claude Code Conversations

30 Jan 2026 • 5 min read

Project Overview

claude‑sync is a command‑line tool that lets you keep your Claude Code conversations, agents, and settings in sync across any number of machines.
Instead of relying on a proprietary cloud service, the tool encrypts everything locally and stores the ciphertext in a backend you choose—Git, Amazon S3, Google Cloud Storage, or Cloudflare R2.
Because the data is encrypted before it leaves your computer, you retain full control over your private coding context while still enjoying a seamless multi‑device workflow.

Key Features

FeatureWhy it matters
Multi‑backend supportUse the storage you already trust (Git repos, S3 buckets, etc.).
End‑to‑end encryptionAES‑256‑CBC encryption with a locally generated key guarantees that the storage provider never sees plaintext.
Incremental synchronizationOnly resources whose content has changed since the last sync are uploaded, saving bandwidth and time.
Conflict detection & resolutionWhen the same resource is edited on two devices, hashes are compared and the CLI prompts you to choose a resolution.
Generic resource syncSessions, agents, and user settings are treated uniformly, keeping your whole Claude Code environment consistent.
Extensive test coverage172 automated tests give confidence that sync, encryption, and conflict handling work reliably.

Architecture Overview

TEXT
+----------------+        +----------------+        +-------------------+
| Claude Code    |  -->   | claude‑sync CLI|  -->   | Encryption Module |
+----------------+        +----------------+        +-------------------+
                                                   |
                                                   v
                                          +-------------------+
                                          | Storage Backend   |
                                          | (Git / S3 / GCS / |
                                          |  R2)              |
                                          +-------------------+

The CLI orchestrates the flow, the Encryption Module handles all cryptographic work, and the chosen backend stores only encrypted blobs.

How It Works

1. Discovering Local Resources

findLocal() walks the workspace, reads the sync‑state file, and returns only those resources that have changed since the previous sync.
The change detection is based on a hash of the file’s content, not just modification timestamps.

TS
// src/commands/push.ts (excerpt)
import { readFileSync, statSync } from "fs";
import { glob } from "glob";
import { getStoredHash, setStoredHash } from "../utils/syncState.js";

/**
 * Returns paths of resources whose content hash differs from the last sync.
 */
export async function findLocal(): Promise<string[]> {
  const files = await glob("**/*.json", {
    ignore: ["**/node_modules/**", "**/.git/**"],
  });

  return files.filter((path) => {
    const content = readFileSync(path);
    const currentHash = hashContent(content);
    const previousHash = getStoredHash(path);
    return currentHash !== previousHash;
  });
}

hashContent creates a SHA‑256 digest of the raw bytes; the digest is stored locally after a successful push.

2. Encrypting Data

Encryption lives in src/crypto/encrypt.ts. The module exports both encrypt (which returns ciphertext prefixed with the IV) and hashContent (used by the sync‑state logic).

TS
// src/crypto/encrypt.ts
import { createCipheriv, randomBytes, createHash } from "crypto";
import { getKey } from "./key.js";

export async function encrypt(data: Buffer): Promise<Buffer> {
  const key = await getKey();               // 32‑byte AES‑256 key
  const iv  = randomBytes(16);              // 16‑byte initialization vector
  const cipher = createCipheriv("aes-256-cbc", key, iv);
  const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
  // Store IV in front of ciphertext for later decryption
  return Buffer.concat([iv, encrypted]);
}

/** Simple SHA‑256 hash used for incremental sync */
export function hashContent(data: Buffer): string {
  return createHash("sha256").update(data).digest("hex");
}

3. Pushing a Single Resource

The pushResource() helper reads a file, encrypts it, uploads the ciphertext, and finally updates the stored hash.

TS
// src/commands/push.ts (excerpt)
import { readFileSync } from "fs";
import { encrypt, hashContent } from "../crypto/encrypt.js";
import { backend } from "../backends/index.js";
import { setStoredHash } from "../utils/syncState.js";

export async function pushResource(path: string): Promise<void> {
  const plain = readFileSync(path);
  const encrypted = await encrypt(plain);
  await backend.pushResource(path, encrypted);
  setStoredHash(path, hashContent(plain));
}

4. Pulling with Conflict Detection

When pulling, the CLI compares remote and local hashes. If they differ, the user is prompted to keep the local version, accept the remote version, or merge manually.

TS
// src/commands/pull.ts (excerpt)
import ora from "ora";
import chalk from "chalk";
import { backend } from "../backends/index.js";
import { decrypt } from "../crypto/decrypt.js";
import { getStoredHash, setStoredHash } from "../utils/syncState.js";

async function pullResource(path: string, verbose = false) {
  const remote = await backend.pullResource(path);
  const decrypted = await decrypt(remote);
  const remoteHash = hashContent(decrypted);
  const localHash = getStoredHash(path);

  if (localHash && localHash !== remoteHash) {
    // Conflict – ask the user (simplified for brevity)
    console.log(chalk.yellow(`Conflict detected for ${path}`));
    // ...prompt logic would go here...
  }

  // No conflict or user chose remote version
  // Write file and update stored hash
  // (writeFileSync omitted for clarity)
  setStoredHash(path, remoteHash);
}

The --verbose flag (added in v0.4.0) prints the spinner text and any conflict details.

Getting Started

  1. Install the CLI

    BASH
    npm install -g @xxdesmus/claude-sync
  2. Initialize a backend

    BASH
    claude-sync init

    You’ll be asked to select a storage provider and to generate an encryption key.

  3. Add the Git‑hook integration (optional but recommended)

    BASH
    claude-sync install

    This registers pre‑commit and post‑checkout hooks so that changes are synced automatically.

  4. Push your first changes

    BASH
    claude-sync push

    Only resources that have changed since the last sync are uploaded.

  5. Pull updates on another machine

    BASH
    claude-sync pull

Recent Developments

Version 0.4.0 introduced two notable improvements:

  • --verbose flag for pull – provides detailed progress output and prints conflict information, making troubleshooting easier.
  • Incremental sync refinements – the push pipeline now hashes each file before upload, stores the hash in a local sync‑state file, and skips uploading unchanged resources. Empty files are also ignored, reducing unnecessary network traffic.

These updates continue the project’s focus on efficiency and a smooth developer experience while keeping the core privacy guarantees intact.

Conclusion

claude‑sync empowers developers to work with Claude Code on any machine without sacrificing privacy or productivity. By encrypting data locally, supporting multiple storage backends, and only moving what truly changed, it offers a lightweight yet robust solution for synchronizing AI‑assisted coding sessions.

The project is open‑source, well‑tested, and welcomes contributions—check out the repository, read the CONTRIBUTING.md, and start keeping your AI‑driven workflow in perfect sync.


Start searching

Enter keywords to search articles.