Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mnemon-sync

CI

Sync your mnemon memory stores across multiple machines — with zero changes to mnemon's code. Just the sqlite3 CLI, Syncthing, and Claude Code hooks.

The problem

A mnemon store is a single SQLite database (~/.mnemon/data/<store>/mnemon.db). If you use Claude Code (or any mnemon-backed agent) on more than one machine, you want the same memory everywhere. But you cannot just file-sync the .db:

  • WAL/SHM side files make a live database inconsistent mid-copy;
  • a file sync can grab a torn, half-written database;
  • worst of all, a sync tool can't merge a binary file — two machines writing produce conflicts and silently lose data.

The approach

mnemon-sync treats the live DB as local-only and syncs a logical merge instead:

  1. Snapshot — each machine exports a consistent snapshot of every store with sqlite3 VACUUM INTO, written as sync/<store>/<host>.db.
  2. Ship — one shared folder (~/.mnemon/sync/) is synced between machines by Syncthing. Each machine only ever writes its own <host>.db, so there are never two writers on one file — Syncthing never makes a conflict file.
  3. Merge — each machine imports peers' snapshots into its live DB with a last-writer-wins merge on the insights table (keyed by updated_at), merges the usage fields (access_count, last_accessed_at) by max so recall activity travels too, unions the edges, and drops edges of deleted insights. Merge columns are derived from the live schemas at merge time, so devices on different mnemon versions still sync their common columns.

This is a state-based CRDT that converges: new memories and deletions are never lost; a concurrent edit of the same memory resolves to the latest one.

It works because of how mnemon stores data: UUID primary keys (no insert collisions), soft-delete tombstones (forget/gc never hard-delete), and an updated_at on every row.

How it runs

Memory only changes when an agent runs, so syncing is driven by Claude Code hooks:

  • SessionStartimport (pull peers before you start)
  • Stop + SubagentStopexport (publish after each turn / memory write)

The hooks are silent, non-blocking, and skip work when nothing changed (an mtime guard), so they add no noticeable overhead and never interrupt a session.

Requirements

  • mnemon
  • sqlite3 CLI — macOS: preinstalled; Debian/Ubuntu: sudo apt install sqlite3
  • python3 (only for install-hooks)
  • Syncthing between your machines
  • Claude Code (for the lifecycle hooks)

Quick start

Full walkthrough: INSTALL.md. In short, on each machine:

git clone https://github.com/b1rdex/mnemon-sync && cd mnemon-sync

# put the script where Syncthing will share it
mkdir -p ~/.mnemon/sync/bin
cp mnemon-sync mnemon-sync.test.sh ~/.mnemon/sync/bin/
chmod +x ~/.mnemon/sync/bin/mnemon-sync

# share ONLY ~/.mnemon/sync between your machines in Syncthing, then:
~/.mnemon/sync/bin/mnemon-sync install-hooks

Commands

command what it does
export snapshot each live store to $SYNC_DIR/<store>/<host>.db
import merge peers' snapshots into each live store (LWW)
sync export then import
status per-store counts + known snapshots
install-hooks add the SessionStart / Stop / SubagentStop hooks to Claude settings
uninstall-hooks remove them (only its own entries)

Environment overrides: MNEMON_DATA_DIR, MNEMON_SYNC_DIR, MNEMON_SYNC_HOST (device id, defaults to hostname -s), CLAUDE_SETTINGS.

Safety & testing

  • Live ~/.mnemon/data/ is never placed in the synced folder — it can't leak.
  • install-hooks / uninstall-hooks are idempotent and touch only their own hook entries; your other Claude settings are preserved (and backed up first).
  • ./mnemon-sync.test.sh runs an isolated two-device convergence / idempotency / safety / bootstrap / hook test against the real mnemon schema.

Full rationale and trade-offs: DESIGN.md.

Development

make lint   # bash -n + shellcheck
make test   # run the test harness

CI runs the same on every push: lint, plus the harness on Linux and macOS against a store created by the real upstream mnemon binary (go install …@latest) — a weekly run re-checks against the latest mnemon release to catch schema drift.

Limitations (by design)

  • Graph edges are unioned, not recomputed (recall still works; a few edge weights may differ from a fresh build — recomputing would need mnemon's engine).
  • Soft-delete tombstones accumulate over time.
  • A concurrent edit of the same insight on two machines resolves by last-writer-wins (the older revision of that one insight is dropped).
  • Usage counters converge to max, not sum: if both machines recall the same insight before syncing, the merged access_count is the larger of the two, not the total. The usage signal is never lost, but it can undercount.

License

MIT

About

Code-free cross-machine sync for mnemon memory stores — SQLite snapshots + Syncthing + Claude Code hooks

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages