{"id":51555982,"url":"https://github.com/b1rdex/mnemon-sync","last_synced_at":"2026-07-10T05:01:29.347Z","repository":{"id":363117865,"uuid":"1262013711","full_name":"b1rdex/mnemon-sync","owner":"b1rdex","description":"Code-free cross-machine sync for mnemon memory stores — SQLite snapshots + Syncthing + Claude Code hooks","archived":false,"fork":false,"pushed_at":"2026-06-07T14:09:27.000Z","size":17,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T15:16:09.755Z","etag":null,"topics":["claude-code","crdt","mnemon","sqlite","syncthing"],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/b1rdex.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-07T13:09:27.000Z","updated_at":"2026-06-07T14:09:31.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/b1rdex/mnemon-sync","commit_stats":null,"previous_names":["b1rdex/mnemon-sync"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/b1rdex/mnemon-sync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b1rdex%2Fmnemon-sync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b1rdex%2Fmnemon-sync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b1rdex%2Fmnemon-sync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b1rdex%2Fmnemon-sync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/b1rdex","download_url":"https://codeload.github.com/b1rdex/mnemon-sync/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b1rdex%2Fmnemon-sync/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35321224,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-10T02:00:06.465Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["claude-code","crdt","mnemon","sqlite","syncthing"],"created_at":"2026-07-10T05:01:28.508Z","updated_at":"2026-07-10T05:01:29.338Z","avatar_url":"https://github.com/b1rdex.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mnemon-sync\n\n[![CI](https://github.com/b1rdex/mnemon-sync/actions/workflows/ci.yml/badge.svg)](https://github.com/b1rdex/mnemon-sync/actions/workflows/ci.yml)\n\nSync your [mnemon](https://github.com/mnemon-dev/mnemon) memory stores across\nmultiple machines — with **zero changes to mnemon's code**. Just the `sqlite3`\nCLI, [Syncthing](https://syncthing.net/), and Claude Code hooks.\n\n## The problem\n\nA mnemon store is a single SQLite database (`~/.mnemon/data/\u003cstore\u003e/mnemon.db`).\nIf you use Claude Code (or any mnemon-backed agent) on more than one machine, you\nwant the same memory everywhere. But you **cannot** just file-sync the `.db`:\n\n- WAL/SHM side files make a live database inconsistent mid-copy;\n- a file sync can grab a torn, half-written database;\n- worst of all, a sync tool can't *merge* a binary file — two machines writing\n  produce conflicts and silently lose data.\n\n## The approach\n\n`mnemon-sync` treats the live DB as local-only and syncs a **logical merge**\ninstead:\n\n1. **Snapshot** — each machine exports a consistent snapshot of every store with\n   `sqlite3 VACUUM INTO`, written as `sync/\u003cstore\u003e/\u003chost\u003e.db`.\n2. **Ship** — one shared folder (`~/.mnemon/sync/`) is synced between machines by\n   Syncthing. Each machine only ever writes *its own* `\u003chost\u003e.db`, so there are\n   never two writers on one file — Syncthing never makes a conflict file.\n3. **Merge** — each machine imports peers' snapshots into its live DB with a\n   **last-writer-wins** merge on the `insights` table (keyed by `updated_at`),\n   merges the usage fields (`access_count`, `last_accessed_at`) by **max** so\n   recall activity travels too, unions the edges, and drops edges of deleted\n   insights. Merge columns are derived from the live schemas at merge time, so\n   devices on different mnemon versions still sync their common columns.\n\nThis is a state-based CRDT that converges: new memories and deletions are never\nlost; a concurrent edit of the *same* memory resolves to the latest one.\n\nIt works because of how mnemon stores data: UUID primary keys (no insert\ncollisions), soft-delete tombstones (`forget`/`gc` never hard-delete), and an\n`updated_at` on every row.\n\n## How it runs\n\nMemory only changes when an agent runs, so syncing is driven by Claude Code hooks:\n\n- **`SessionStart`** → `import` (pull peers before you start)\n- **`Stop` + `SubagentStop`** → `export` (publish after each turn / memory write)\n\nThe hooks are silent, non-blocking, and skip work when nothing changed (an mtime\nguard), so they add no noticeable overhead and never interrupt a session.\n\n## Requirements\n\n- [mnemon](https://github.com/mnemon-dev/mnemon)\n- `sqlite3` CLI — macOS: preinstalled; Debian/Ubuntu: `sudo apt install sqlite3`\n- `python3` (only for `install-hooks`)\n- [Syncthing](https://syncthing.net/) between your machines\n- [Claude Code](https://claude.com/claude-code) (for the lifecycle hooks)\n\n## Quick start\n\nFull walkthrough: **[INSTALL.md](INSTALL.md)**. In short, on each machine:\n\n```bash\ngit clone https://github.com/b1rdex/mnemon-sync \u0026\u0026 cd mnemon-sync\n\n# put the script where Syncthing will share it\nmkdir -p ~/.mnemon/sync/bin\ncp mnemon-sync mnemon-sync.test.sh ~/.mnemon/sync/bin/\nchmod +x ~/.mnemon/sync/bin/mnemon-sync\n\n# share ONLY ~/.mnemon/sync between your machines in Syncthing, then:\n~/.mnemon/sync/bin/mnemon-sync install-hooks\n```\n\n## Commands\n\n| command | what it does |\n|---|---|\n| `export` | snapshot each live store to `$SYNC_DIR/\u003cstore\u003e/\u003chost\u003e.db` |\n| `import` | merge peers' snapshots into each live store (LWW) |\n| `sync` | `export` then `import` |\n| `status` | per-store counts + known snapshots |\n| `install-hooks` | add the SessionStart / Stop / SubagentStop hooks to Claude settings |\n| `uninstall-hooks` | remove them (only its own entries) |\n\nEnvironment overrides: `MNEMON_DATA_DIR`, `MNEMON_SYNC_DIR`, `MNEMON_SYNC_HOST`\n(device id, defaults to `hostname -s`), `CLAUDE_SETTINGS`.\n\n## Safety \u0026 testing\n\n- Live `~/.mnemon/data/` is **never** placed in the synced folder — it can't leak.\n- `install-hooks` / `uninstall-hooks` are idempotent and touch only their own hook\n  entries; your other Claude settings are preserved (and backed up first).\n- `./mnemon-sync.test.sh` runs an isolated two-device convergence / idempotency /\n  safety / bootstrap / hook test against the real mnemon schema.\n\nFull rationale and trade-offs: **[DESIGN.md](DESIGN.md)**.\n\n## Development\n\n```bash\nmake lint   # bash -n + shellcheck\nmake test   # run the test harness\n```\n\nCI runs the same on every push: lint, plus the harness on Linux and macOS against\na store created by the **real upstream mnemon binary** (`go install …@latest`) —\na weekly run re-checks against the latest mnemon release to catch schema drift.\n\n## Limitations (by design)\n\n- Graph **edges are unioned**, not recomputed (recall still works; a few edge\n  weights may differ from a fresh build — recomputing would need mnemon's engine).\n- Soft-delete **tombstones accumulate** over time.\n- A concurrent edit of the *same* insight on two machines resolves by\n  last-writer-wins (the older revision of that one insight is dropped).\n- **Usage counters converge to max, not sum**: if both machines recall the same\n  insight before syncing, the merged `access_count` is the larger of the two,\n  not the total. The usage signal is never lost, but it can undercount.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fb1rdex%2Fmnemon-sync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fb1rdex%2Fmnemon-sync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fb1rdex%2Fmnemon-sync/lists"}