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).
Quick start
Run the/cloud-sync skill in Claude Code. It checks sync status and, on first
run, walks you through setup:
- 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(mode0600) without ever echoing the token. - The skill restarts the worker, which immediately starts draining unsynced rows, and polls status until the pending counts fall.
.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
NULLand 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.
| Key | Default | Meaning |
|---|---|---|
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_URL | https://cmem.ai/api/pro/sync | Sync 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_NAME | machine hostname | Human-readable label for this device in the cmem.ai Devices panel. |
Status endpoint
The worker exposesGET /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”.
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,nullbefore the first.lastError— message from the most recent failed flush,nullwhen healthy. It never contains the token.
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.pidpoints at a live daemon, it is killed. - Retired:
cloud-sync.mjs,.cloud-sync.env, andcloud-sync.pidare renamed with a.retiredsuffix (archived, not deleted). Your token is copied from.cloud-sync.envintosettings.jsonfirst, so setup needs no re-pasting. - Preserved:
~/.claude-mem/cloud-sync-state.jsonis 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 itsdeviceId(adopted intoCLAUDE_MEM_CLOUD_SYNC_DEVICE_IDso 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.

