Skip to main content

Cloud Sync (cmem.ai Pro)

Cloud sync backs up your local memory database to your cmem.ai account. It is built into the worker: there is no daemon — the worker syncs on write. The same process that records every observation also uploads it, so there is no second process to install, monitor, or restart.
The database is the queue. Every synced table carries a synced_at column (NULL = not in the cloud yet). After each write, the worker nudges a debounced flusher that drains WHERE synced_at IS NULL in batches to cmem.ai and stamps rows on success. That one mechanism is live sync, backfill, offline catch-up, and retry — a never-synced install simply has everything NULL, and anything that fails to upload stays NULL until a later flush picks it up.

What syncs

Three kinds of rows are uploaded to your cmem.ai account:
  • Observations — the compressed memories claude-mem generates from your sessions.
  • Session summaries — the per-session overview records.
  • User prompts — the prompts you typed (clamped to 200 KB each).
Privacy: cloud sync uploads your observation narratives and your full prompt text to your cmem.ai account. Don’t enable it if that content must stay on your machine.

Quick start

Run the /cloud-sync skill in Claude Code. It checks sync status and, on first run, walks you through setup:
  1. Grab your sync token and user id from cmem.ai → Connect and paste them when asked. The skill writes them into ~/.claude-mem/settings.json (mode 0600) without ever echoing the token.
  2. The skill restarts the worker, which immediately starts draining unsynced rows, and polls status until the pending counts fall.
If you previously used the legacy standalone sync client, the skill migrates it automatically: your token is read from the old .cloud-sync.env, your device identity carries over, and nothing re-uploads — rows the old client already pushed are stamped as synced during migration. See Migrating from the standalone client.

How the flusher behaves

  • Debounced: write bursts coalesce; the flusher runs ~1.5 s after the last write, and only one flush runs at a time.
  • Batched: rows drain in batches of up to 200 per kind, packed into request bodies of at most 2 MB.
  • Timeboxed: every request has a 30 s timeout — a dead network can never hang the worker.
  • Retrying: a failed upload leaves rows NULL and retries on the next write plus a capped exponential backoff (30 s → 10 min). The write path is never blocked — sync failures cost nothing but delay.
  • Startup drain: the worker kicks one flush at startup, which doubles as the initial backfill of your existing database.

Settings

Cloud sync is configured in ~/.claude-mem/settings.json. It is active when both the token and the user id are non-empty — there is no separate enable flag; blank the token to turn sync off.
KeyDefaultMeaning
CLAUDE_MEM_CLOUD_SYNC_TOKEN''Your cmem.ai sync token (from cmem.ai → Connect). Sent as Authorization: Bearer.
CLAUDE_MEM_CLOUD_SYNC_USER_ID''Your cmem.ai user id (from cmem.ai → Connect).
CLAUDE_MEM_CLOUD_SYNC_URLhttps://cmem.ai/api/pro/syncSync endpoint base URL. Only change this if you’re pointing at a non-production server.
CLAUDE_MEM_CLOUD_SYNC_DEVICE_ID''Stable identity of this machine in the cloud. Resolved at first start — adopted from the legacy client’s state file if present, otherwise a fresh UUID — then persisted back here. Don’t edit it: the server keys rows on it, and a changed id forks every cloud row into a duplicate.
CLAUDE_MEM_CLOUD_SYNC_DEVICE_NAMEmachine hostnameHuman-readable label for this device in the cmem.ai Devices panel.

Status endpoint

The worker exposes GET /api/sync/status (on the same port as the rest of the worker HTTP API). It is registered unconditionally, so an unconfigured install answers 200 rather than a 404 — callers can tell “not set up” apart from “worker down”.
curl -s "http://127.0.0.1:${CLAUDE_MEM_WORKER_PORT}/api/sync/status"
Configured:
{
  "configured": true,
  "deviceId": "2f6b1c9e-7d41-4c1a-9b0e-3d5f8a2c6e10",
  "pending": { "observations": 0, "summaries": 0, "prompts": 2 },
  "lastFlushAt": 1783981042731,
  "lastError": null
}
  • pending — rows per kind still waiting to upload (synced_at IS NULL). Counts near 0 mean the cloud copy is current.
  • lastFlushAt — epoch ms of the last successful flush, null before the first.
  • lastError — message from the most recent failed flush, null when healthy. It never contains the token.
Not configured:
{ "configured": false }

Migrating from the standalone client

Before sync moved into the worker, cloud sync ran as a separate standalone client (cloud-sync.mjs plus a .cloud-sync.env credentials file and a cloud-sync.pid file). The worker supersedes it entirely — if the old client kept running it would double-upload — so the /cloud-sync skill retires it during setup:
  • Stopped: if cloud-sync.pid points at a live daemon, it is killed.
  • Retired: cloud-sync.mjs, .cloud-sync.env, and cloud-sync.pid are renamed with a .retired suffix (archived, not deleted). Your token is copied from .cloud-sync.env into settings.json first, so setup needs no re-pasting.
  • Preserved: ~/.claude-mem/cloud-sync-state.json is deliberately left in place. The worker reads two things from it: its upload cursors (used once, by a database migration, to stamp everything the old client already pushed — this is why migration re-uploads nothing) and its deviceId (adopted into CLAUDE_MEM_CLOUD_SYNC_DEVICE_ID so the cloud sees the same device before and after). Renaming or deleting that file would mint a new device identity and fork every previously synced row into a duplicate.
The standalone client remains available from cmem.ai as an out-of-repo utility for non-plugin users. The plugin no longer depends on it.