mnemox 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. mnemox-0.2.0/LICENSE +21 -0
  2. mnemox-0.2.0/PKG-INFO +329 -0
  3. mnemox-0.2.0/README.md +308 -0
  4. mnemox-0.2.0/ember/__init__.py +3 -0
  5. mnemox-0.2.0/ember/__main__.py +129 -0
  6. mnemox-0.2.0/ember/capture/__init__.py +6 -0
  7. mnemox-0.2.0/ember/capture/drain.py +341 -0
  8. mnemox-0.2.0/ember/capture/secrets.py +137 -0
  9. mnemox-0.2.0/ember/cli.py +309 -0
  10. mnemox-0.2.0/ember/config.py +180 -0
  11. mnemox-0.2.0/ember/crypto/__init__.py +35 -0
  12. mnemox-0.2.0/ember/crypto/_blind.py +70 -0
  13. mnemox-0.2.0/ember/crypto/_cipher.py +122 -0
  14. mnemox-0.2.0/ember/crypto/_kdf.py +58 -0
  15. mnemox-0.2.0/ember/crypto/errors.py +13 -0
  16. mnemox-0.2.0/ember/db/__init__.py +25 -0
  17. mnemox-0.2.0/ember/db/migrate.py +32 -0
  18. mnemox-0.2.0/ember/db/migrations/__init__.py +1 -0
  19. mnemox-0.2.0/ember/db/migrations/_migration_0001.py +51 -0
  20. mnemox-0.2.0/ember/db/migrations/_migration_0002.py +192 -0
  21. mnemox-0.2.0/ember/embeddings/__init__.py +8 -0
  22. mnemox-0.2.0/ember/embeddings/engine.py +201 -0
  23. mnemox-0.2.0/ember/embeddings/tokenizer.py +89 -0
  24. mnemox-0.2.0/ember/extraction/__init__.py +5 -0
  25. mnemox-0.2.0/ember/extraction/local.py +81 -0
  26. mnemox-0.2.0/ember/invalidation/__init__.py +15 -0
  27. mnemox-0.2.0/ember/invalidation/cosine.py +140 -0
  28. mnemox-0.2.0/ember/keystore/__init__.py +27 -0
  29. mnemox-0.2.0/ember/keystore/_backend.py +256 -0
  30. mnemox-0.2.0/ember/keystore/_salt.py +40 -0
  31. mnemox-0.2.0/ember/keystore/errors.py +9 -0
  32. mnemox-0.2.0/ember/mcp/__init__.py +5 -0
  33. mnemox-0.2.0/ember/mcp/tools.py +510 -0
  34. mnemox-0.2.0/ember/mirror/__init__.py +5 -0
  35. mnemox-0.2.0/ember/mirror/render.py +143 -0
  36. mnemox-0.2.0/ember/models/__init__.py +1 -0
  37. mnemox-0.2.0/ember/models/cache.py +40 -0
  38. mnemox-0.2.0/ember/models/loader.py +54 -0
  39. mnemox-0.2.0/ember/modes/__init__.py +19 -0
  40. mnemox-0.2.0/ember/modes/scope.py +243 -0
  41. mnemox-0.2.0/ember/oplog/__init__.py +19 -0
  42. mnemox-0.2.0/ember/oplog/backfill.py +98 -0
  43. mnemox-0.2.0/ember/oplog/codec.py +87 -0
  44. mnemox-0.2.0/ember/oplog/entry.py +35 -0
  45. mnemox-0.2.0/ember/oplog/producer.py +192 -0
  46. mnemox-0.2.0/ember/ranking/__init__.py +7 -0
  47. mnemox-0.2.0/ember/ranking/cosine.py +12 -0
  48. mnemox-0.2.0/ember/ranking/fusion.py +78 -0
  49. mnemox-0.2.0/ember/ranking/result.py +18 -0
  50. mnemox-0.2.0/ember/retrieval/__init__.py +5 -0
  51. mnemox-0.2.0/ember/retrieval/_search.py +143 -0
  52. mnemox-0.2.0/ember/retrieval/bm25.py +88 -0
  53. mnemox-0.2.0/ember/retrieval/tiers.py +101 -0
  54. mnemox-0.2.0/ember/schema/__init__.py +1 -0
  55. mnemox-0.2.0/ember/server.py +156 -0
  56. mnemox-0.2.0/ember/storage/__init__.py +31 -0
  57. mnemox-0.2.0/ember/storage/paths.py +33 -0
  58. mnemox-0.2.0/ember/storage/store.py +210 -0
  59. mnemox-0.2.0/ember/sync/__init__.py +32 -0
  60. mnemox-0.2.0/ember/sync/_aad.py +23 -0
  61. mnemox-0.2.0/ember/sync/client.py +382 -0
  62. mnemox-0.2.0/ember/sync/cursor.py +76 -0
  63. mnemox-0.2.0/ember/sync/errors.py +22 -0
  64. mnemox-0.2.0/ember/sync/merge.py +197 -0
  65. mnemox-0.2.0/ember/sync/oplog_store.py +169 -0
  66. mnemox-0.2.0/ember/sync/pull.py +53 -0
  67. mnemox-0.2.0/ember/sync/push.py +42 -0
  68. mnemox-0.2.0/ember/sync/transport.py +65 -0
  69. mnemox-0.2.0/ember/vec/__init__.py +19 -0
  70. mnemox-0.2.0/ember/vec/_build.py +151 -0
  71. mnemox-0.2.0/ember/vec/_gate.py +66 -0
  72. mnemox-0.2.0/ember/vec/_meta.py +67 -0
  73. mnemox-0.2.0/ember/vec/_query.py +86 -0
  74. mnemox-0.2.0/ember/vec/index.py +98 -0
  75. mnemox-0.2.0/ember/write/__init__.py +6 -0
  76. mnemox-0.2.0/ember/write/flush.py +197 -0
  77. mnemox-0.2.0/ember/write/pipeline.py +104 -0
  78. mnemox-0.2.0/mnemox.egg-info/PKG-INFO +329 -0
  79. mnemox-0.2.0/mnemox.egg-info/SOURCES.txt +84 -0
  80. mnemox-0.2.0/mnemox.egg-info/dependency_links.txt +1 -0
  81. mnemox-0.2.0/mnemox.egg-info/entry_points.txt +2 -0
  82. mnemox-0.2.0/mnemox.egg-info/requires.txt +10 -0
  83. mnemox-0.2.0/mnemox.egg-info/top_level.txt +1 -0
  84. mnemox-0.2.0/pyproject.toml +37 -0
  85. mnemox-0.2.0/setup.cfg +4 -0
  86. mnemox-0.2.0/tests/test_config.py +150 -0
mnemox-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 David Sandler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
mnemox-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,329 @@
1
+ Metadata-Version: 2.4
2
+ Name: mnemox
3
+ Version: 0.2.0
4
+ Summary: mnemox local-first agent memory system with MCP server. Atom-based context for AI agents.
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/ajadi/mnemox
7
+ Keywords: mcp,memory,llm,claude,ai-agent
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: mcp<2,>=1.10
12
+ Requires-Dist: onnxruntime~=1.20
13
+ Requires-Dist: tokenizers~=0.21
14
+ Requires-Dist: numpy~=2.1
15
+ Requires-Dist: argon2-cffi<24,>=23.1
16
+ Requires-Dist: cryptography<46,>=42
17
+ Requires-Dist: keyring<26,>=24
18
+ Provides-Extra: vec
19
+ Requires-Dist: sqlite-vec>=0.1.6; extra == "vec"
20
+ Dynamic: license-file
21
+
22
+ <div align="center">
23
+
24
+ <img src="assets/logo.svg" alt="Ember" width="420">
25
+
26
+ ### Your Claude Code sessions never forget again.
27
+
28
+ Local-first memory via MCP — one SQLite file, on-device embeddings,<br>
29
+ encrypted cross-machine sync. No cloud. No Docker.
30
+
31
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
32
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
33
+ [![MCP](https://img.shields.io/badge/MCP-native-8A2BE2.svg)](https://modelcontextprotocol.io/)
34
+ [![tests](https://img.shields.io/badge/tests-727%20passing-brightgreen.svg)](#status)
35
+
36
+ <img src="assets/demo.svg" alt="Ember recalls a past decision in a fresh Claude Code session" width="760">
37
+
38
+ </div>
39
+
40
+ ---
41
+
42
+ ## The problem
43
+
44
+ - **Claude forgets between sessions.** Every `/clear` or new window throws away the context you built — decisions, trade-offs, gotchas — and you re-explain them from scratch.
45
+ - **Cloud memory pulls your context off-machine and costs money.** mem0 & co. ship your project history to someone else's servers and meter it.
46
+ - **Self-hosted memory means heavy infra.** Qdrant + Neo4j + Docker is a lot of moving parts just to remember what you decided yesterday.
47
+
48
+ **Ember solves it with one SQLite file on your machine.**
49
+
50
+ ---
51
+
52
+ ## Why Ember
53
+
54
+ | | **Ember 🔥** | mem0 (cloud) | OpenMemory | Self-hosted stack |
55
+ |---------------------------------------|:------------:|:------------:|:----------:|:-----------------:|
56
+ | Runs fully local (no cloud) | ✅ | ❌ | ⚠️ partial | ✅ |
57
+ | No heavy infra (no Docker/Qdrant/Neo4j) | ✅ | ✅ | ❌ | ❌ |
58
+ | Single-file store (SQLite) | ✅ | ❌ | ❌ | ❌ |
59
+ | MCP-native | ✅ | ⚠️ | ⚠️ | ⚠️ |
60
+ | Zero-knowledge E2E sync | ✅ | ❌ | ❌ | ❌ |
61
+
62
+ Ember is the only one that is **local, single-file, MCP-native, zero-infra, and privately synced** at the same time.
63
+
64
+ > ¹ mem0 "no heavy infra" = managed SaaS — you run nothing, but your context goes to their cloud.
65
+
66
+ ---
67
+
68
+ Already using Claude Code? Just tell it: *"set up Ember for me"* — it can run the installer and wire up your `.mcp.json`.
69
+
70
+ ---
71
+
72
+ ## 60-second quickstart
73
+
74
+ **1. Install** (creates a dedicated venv at `~/.ember/venv` and fetches the embedding model):
75
+
76
+ ```sh
77
+ git clone https://github.com/ajadi/ember && cd ember
78
+ ./install.sh
79
+ ```
80
+
81
+ **2. Connect to Claude.** Add one entry to your project's `.mcp.json`
82
+ (replace `<your-project-root>` with the absolute path to your project):
83
+
84
+ ```json
85
+ {
86
+ "mcpServers": {
87
+ "ember": {
88
+ "command": "~/.ember/venv/bin/python",
89
+ "args": ["-m", "ember", "serve"],
90
+ "env": {
91
+ "EMBER_MODE": "project",
92
+ "EMBER_PROJECT_ROOT": "<your-project-root>"
93
+ }
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ **3. Verify.** Restart Claude Code and ask it to write and recall a memory — the
100
+ `ember_write`, `ember_search`, and `ember_list` tools appear in the MCP tool list.
101
+
102
+ > `ember init` prints the exact `.mcp.json` snippet for your platform, including
103
+ > the absolute Python path. See [Reference](#reference) for details.
104
+
105
+ ---
106
+
107
+ ## How it works
108
+
109
+ ```mermaid
110
+ flowchart LR
111
+ A["atoms<br/>(decisions, notes,<br/>questions, retros)"] --> B["SQLite<br/>(one file)"]
112
+ B --> C["MCP server<br/>(python -m ember serve)"]
113
+ C --> D["Claude Code"]
114
+ D -- "ember_write / ember_search" --> C
115
+ ```
116
+
117
+ Claude writes **atoms** (decisions, observations, questions, retrospectives) into a
118
+ single local SQLite file. On recall, Ember ranks them with multi-tier retrieval
119
+ (FTS5 BM25 + on-device cosine fusion) and serves them straight back over MCP.
120
+
121
+ - **All local.** Memory lives in one SQLite file on your machine — nothing leaves it.
122
+ - **On-device embeddings.** EmbeddingGemma-300m runs locally via ONNX; no embedding API calls.
123
+ - **Private by design.** Cross-machine sync is end-to-end encrypted — the relay sees only ciphertext. Your memories never leave your control in plaintext.
124
+
125
+ ---
126
+
127
+ ## Status
128
+
129
+ **✅ Works today (Phase 1–3)**
130
+ - Local-first MCP memory server (`python -m ember serve`) with 7 tools: write, search, get, list, invalidate, flush, stats
131
+ - On-device EmbeddingGemma-300m embeddings (ONNX, no network)
132
+ - Single-file SQLite store, `project` and `global` modes
133
+ - Multi-tier retrieval R0–R3 (exact, cosine, FTS5 BM25, fusion)
134
+ - Auto-invalidation of superseded memories by cosine similarity
135
+ - Transparent `sqlite-vec` auto-upgrade for large stores (graceful fallback to pure-Python cosine)
136
+ - Universal native-hook capture loop with durable spool + `ember drain`
137
+ - CLI: `init` / `serve` / `mirror` / `drain` / `backfill`
138
+ - Capture memory from Forge workflows (optional adapter)
139
+ - **Encrypted, zero-knowledge cross-machine sync** via a self-hostable hub — your memories never leave your control in plaintext (real-world multi-machine-over-network validation in progress)
140
+ - Oplog backfill — retroactively sync memories written before sync was enabled (`ember backfill`)
141
+
142
+ **📋 Planned**
143
+ - Hosted zero-knowledge sync hub (managed option)
144
+
145
+ > Actively developed — **star to follow.**
146
+
147
+ ---
148
+
149
+ ## Reference
150
+
151
+ <details>
152
+ <summary><strong>CLI commands</strong></summary>
153
+
154
+ ```
155
+ ember [--version]
156
+ init [--mode {project,global,server}] # initialise store + write config.json
157
+ serve # start the MCP server over stdio
158
+ mirror --project-id <id> [--output-dir memory] [--db-path PATH]
159
+ drain [--queue-path PATH] [--db-path PATH] [--project-id ID]
160
+ ```
161
+
162
+ - **`init`** — creates the store and writes `config.json`. Idempotent: re-running preserves `project_id` and `created`, refreshes `ember_version` / `last_init`. Prints the exact `.mcp.json` snippet for your platform. `--mode server` initialises a server-mode store and config (`client_id` + `hub_url`) for hub sync; set `EMBER_HUB_TOKEN` in the environment.
163
+ - **`serve`** — starts the Ember MCP server over stdio (register this in `.mcp.json`).
164
+ - **`mirror`** — renders stored atoms to Markdown under `memory/` (or `--output-dir`).
165
+ - **`drain`** — drains the capture queue into the store (used by the capture loop).
166
+
167
+ | Mode | DB location | config.json |
168
+ |------|-------------|-------------|
169
+ | `project` (default) | `<project-root>/.ember/ember.db` | `<project-root>/.ember/config.json` |
170
+ | `global` | `~/.ember/global.db` | `~/.ember/config.json` |
171
+ | `server` | `<project-root>/.ember/ember.db` | `<project-root>/.ember/config.json` (server: client_id + hub_url) |
172
+
173
+ </details>
174
+
175
+ <details>
176
+ <summary><strong>Environment variables</strong></summary>
177
+
178
+ | Variable | Description | Default |
179
+ |----------|-------------|---------|
180
+ | `EMBER_MODE` | `project` or `global` | `project` |
181
+ | `EMBER_PROJECT_ROOT` | Root directory for project-mode DB | `cwd` |
182
+ | `EMBER_DB_PATH` | Explicit DB path (overrides mode routing) | — |
183
+ | `EMBER_MODEL_DIR` | Override for ONNX model cache directory | `~/.ember/models/embeddinggemma-300m/` |
184
+ | `EMBER_INVALIDATION_THRESHOLD` | Cosine threshold for auto-invalidation (0–1] | `0.92` |
185
+ | `ANTHROPIC_API_KEY` | Enables optional `ember_flush` enrichment via Claude Haiku; absent ⇒ enrichment skipped | — |
186
+
187
+ </details>
188
+
189
+ <details>
190
+ <summary><strong>Global mode .mcp.json</strong></summary>
191
+
192
+ One shared DB for all projects:
193
+
194
+ ```json
195
+ {
196
+ "mcpServers": {
197
+ "ember": {
198
+ "command": "~/.ember/venv/bin/python",
199
+ "args": ["-m", "ember", "serve"],
200
+ "env": { "EMBER_MODE": "global" }
201
+ }
202
+ }
203
+ }
204
+ ```
205
+
206
+ </details>
207
+
208
+ <details>
209
+ <summary><strong>Model cache</strong></summary>
210
+
211
+ EmbeddingGemma-300m ONNX weights are stored at
212
+ `~/.ember/models/embeddinggemma-300m/` (overridable via `EMBER_MODEL_DIR`).
213
+ `install.sh` fetches them on first run and skips the download afterwards.
214
+
215
+ **One-time setup (gated model):** EmbeddingGemma-300m requires a Hugging Face
216
+ account and license acceptance before the first download:
217
+
218
+ 1. Accept the license at <https://huggingface.co/google/embeddinggemma-300m>
219
+ 2. Run `huggingface-cli login` (token from <https://huggingface.co/settings/tokens>)
220
+ 3. Re-run the installer (or fetch manually below).
221
+
222
+ To fetch manually:
223
+
224
+ ```sh
225
+ huggingface-cli download google/embeddinggemma-300m \
226
+ --local-dir ~/.ember/models/embeddinggemma-300m
227
+ ```
228
+
229
+ </details>
230
+
231
+ <details>
232
+ <summary><strong>MCP tools</strong></summary>
233
+
234
+ | Tool | Purpose |
235
+ |------|---------|
236
+ | `ember_write` | Write a memory record (atom). |
237
+ | `ember_search` | Multi-tier retrieval (BM25 + cosine fusion); tier `R0`–`R3`. |
238
+ | `ember_get` | Fetch a single record by ID (project-scoped). |
239
+ | `ember_list` | List records for a project, newest first. |
240
+ | `ember_invalidate` | Soft-delete (invalidate) a record. |
241
+ | `ember_flush` | Optional Haiku enrichment pass (requires `ANTHROPIC_API_KEY`; skipped otherwise). |
242
+ | `ember_stats` | Record counts and last-flush timestamp. |
243
+
244
+ </details>
245
+
246
+ <details>
247
+ <summary><strong>Optional dependencies</strong></summary>
248
+
249
+ Vector-search acceleration (transparent; activates automatically for large stores):
250
+
251
+ ```sh
252
+ pip install "ember[vec]"
253
+ ```
254
+
255
+ Without `sqlite-vec`, Ember stays on pure-Python cosine — no configuration needed.
256
+
257
+ </details>
258
+
259
+ <details>
260
+ <summary><strong>Platform support</strong></summary>
261
+
262
+ | Platform | Status |
263
+ |----------|--------|
264
+ | macOS (Apple Silicon / Intel) | Supported |
265
+ | Linux (x86-64, arm64) | Supported |
266
+ | Windows | Best-effort |
267
+
268
+ See [Windows setup (best-effort)](#windows-setup-best-effort) below.
269
+
270
+ </details>
271
+
272
+ ---
273
+
274
+ ## Windows setup (best-effort)
275
+
276
+ > Windows support is best-effort. The native installer (`install.ps1`) covers the common case; edge cases (antivirus, execution-policy restrictions, PATH quirks) may need manual adjustment.
277
+
278
+ **Prerequisites**
279
+
280
+ - Python 3.10+ — download from [python.org](https://www.python.org/) or run:
281
+ ```
282
+ winget install Python.Python.3.12
283
+ ```
284
+ Ensure Python is on your `PATH` (the installer wizard has an "Add Python to PATH" checkbox).
285
+
286
+ **Recommended shell**
287
+
288
+ - **PowerShell** — use `install.ps1` (native Windows, no git-bash required):
289
+ ```powershell
290
+ Set-ExecutionPolicy -Scope CurrentUser RemoteSigned # once, if needed
291
+ .\install.ps1
292
+ ```
293
+ - **git-bash** — alternatively run the POSIX `install.sh` inside git-bash.
294
+
295
+ **After install — `.mcp.json` command path**
296
+
297
+ On Windows the Python executable is `Scripts\python.exe`, not `bin/python`:
298
+
299
+ ```json
300
+ {
301
+ "mcpServers": {
302
+ "ember": {
303
+ "command": "%USERPROFILE%\\.ember\\venv\\Scripts\\python.exe",
304
+ "args": ["-m", "ember", "serve"],
305
+ "env": {
306
+ "EMBER_MODE": "project",
307
+ "EMBER_PROJECT_ROOT": "<your-project-root>"
308
+ }
309
+ }
310
+ }
311
+ }
312
+ ```
313
+
314
+ `install.ps1` prints the exact snippet (with your resolved `%USERPROFILE%` path) at the end of the install.
315
+
316
+ **Model download (gated)**
317
+
318
+ EmbeddingGemma-300m requires a one-time license acceptance and `huggingface-cli login` before the download will succeed. See the [Reference](#reference) note above.
319
+
320
+ ---
321
+
322
+ ## Contributing
323
+
324
+ Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). Issues
325
+ labelled **`good first issue`** are a friendly place to start.
326
+
327
+ ## License
328
+
329
+ [MIT](LICENSE) © 2026 David Sandler
mnemox-0.2.0/README.md ADDED
@@ -0,0 +1,308 @@
1
+ <div align="center">
2
+
3
+ <img src="assets/logo.svg" alt="Ember" width="420">
4
+
5
+ ### Your Claude Code sessions never forget again.
6
+
7
+ Local-first memory via MCP — one SQLite file, on-device embeddings,<br>
8
+ encrypted cross-machine sync. No cloud. No Docker.
9
+
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
11
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
12
+ [![MCP](https://img.shields.io/badge/MCP-native-8A2BE2.svg)](https://modelcontextprotocol.io/)
13
+ [![tests](https://img.shields.io/badge/tests-727%20passing-brightgreen.svg)](#status)
14
+
15
+ <img src="assets/demo.svg" alt="Ember recalls a past decision in a fresh Claude Code session" width="760">
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ## The problem
22
+
23
+ - **Claude forgets between sessions.** Every `/clear` or new window throws away the context you built — decisions, trade-offs, gotchas — and you re-explain them from scratch.
24
+ - **Cloud memory pulls your context off-machine and costs money.** mem0 & co. ship your project history to someone else's servers and meter it.
25
+ - **Self-hosted memory means heavy infra.** Qdrant + Neo4j + Docker is a lot of moving parts just to remember what you decided yesterday.
26
+
27
+ **Ember solves it with one SQLite file on your machine.**
28
+
29
+ ---
30
+
31
+ ## Why Ember
32
+
33
+ | | **Ember 🔥** | mem0 (cloud) | OpenMemory | Self-hosted stack |
34
+ |---------------------------------------|:------------:|:------------:|:----------:|:-----------------:|
35
+ | Runs fully local (no cloud) | ✅ | ❌ | ⚠️ partial | ✅ |
36
+ | No heavy infra (no Docker/Qdrant/Neo4j) | ✅ | ✅ | ❌ | ❌ |
37
+ | Single-file store (SQLite) | ✅ | ❌ | ❌ | ❌ |
38
+ | MCP-native | ✅ | ⚠️ | ⚠️ | ⚠️ |
39
+ | Zero-knowledge E2E sync | ✅ | ❌ | ❌ | ❌ |
40
+
41
+ Ember is the only one that is **local, single-file, MCP-native, zero-infra, and privately synced** at the same time.
42
+
43
+ > ¹ mem0 "no heavy infra" = managed SaaS — you run nothing, but your context goes to their cloud.
44
+
45
+ ---
46
+
47
+ Already using Claude Code? Just tell it: *"set up Ember for me"* — it can run the installer and wire up your `.mcp.json`.
48
+
49
+ ---
50
+
51
+ ## 60-second quickstart
52
+
53
+ **1. Install** (creates a dedicated venv at `~/.ember/venv` and fetches the embedding model):
54
+
55
+ ```sh
56
+ git clone https://github.com/ajadi/ember && cd ember
57
+ ./install.sh
58
+ ```
59
+
60
+ **2. Connect to Claude.** Add one entry to your project's `.mcp.json`
61
+ (replace `<your-project-root>` with the absolute path to your project):
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "ember": {
67
+ "command": "~/.ember/venv/bin/python",
68
+ "args": ["-m", "ember", "serve"],
69
+ "env": {
70
+ "EMBER_MODE": "project",
71
+ "EMBER_PROJECT_ROOT": "<your-project-root>"
72
+ }
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ **3. Verify.** Restart Claude Code and ask it to write and recall a memory — the
79
+ `ember_write`, `ember_search`, and `ember_list` tools appear in the MCP tool list.
80
+
81
+ > `ember init` prints the exact `.mcp.json` snippet for your platform, including
82
+ > the absolute Python path. See [Reference](#reference) for details.
83
+
84
+ ---
85
+
86
+ ## How it works
87
+
88
+ ```mermaid
89
+ flowchart LR
90
+ A["atoms<br/>(decisions, notes,<br/>questions, retros)"] --> B["SQLite<br/>(one file)"]
91
+ B --> C["MCP server<br/>(python -m ember serve)"]
92
+ C --> D["Claude Code"]
93
+ D -- "ember_write / ember_search" --> C
94
+ ```
95
+
96
+ Claude writes **atoms** (decisions, observations, questions, retrospectives) into a
97
+ single local SQLite file. On recall, Ember ranks them with multi-tier retrieval
98
+ (FTS5 BM25 + on-device cosine fusion) and serves them straight back over MCP.
99
+
100
+ - **All local.** Memory lives in one SQLite file on your machine — nothing leaves it.
101
+ - **On-device embeddings.** EmbeddingGemma-300m runs locally via ONNX; no embedding API calls.
102
+ - **Private by design.** Cross-machine sync is end-to-end encrypted — the relay sees only ciphertext. Your memories never leave your control in plaintext.
103
+
104
+ ---
105
+
106
+ ## Status
107
+
108
+ **✅ Works today (Phase 1–3)**
109
+ - Local-first MCP memory server (`python -m ember serve`) with 7 tools: write, search, get, list, invalidate, flush, stats
110
+ - On-device EmbeddingGemma-300m embeddings (ONNX, no network)
111
+ - Single-file SQLite store, `project` and `global` modes
112
+ - Multi-tier retrieval R0–R3 (exact, cosine, FTS5 BM25, fusion)
113
+ - Auto-invalidation of superseded memories by cosine similarity
114
+ - Transparent `sqlite-vec` auto-upgrade for large stores (graceful fallback to pure-Python cosine)
115
+ - Universal native-hook capture loop with durable spool + `ember drain`
116
+ - CLI: `init` / `serve` / `mirror` / `drain` / `backfill`
117
+ - Capture memory from Forge workflows (optional adapter)
118
+ - **Encrypted, zero-knowledge cross-machine sync** via a self-hostable hub — your memories never leave your control in plaintext (real-world multi-machine-over-network validation in progress)
119
+ - Oplog backfill — retroactively sync memories written before sync was enabled (`ember backfill`)
120
+
121
+ **📋 Planned**
122
+ - Hosted zero-knowledge sync hub (managed option)
123
+
124
+ > Actively developed — **star to follow.**
125
+
126
+ ---
127
+
128
+ ## Reference
129
+
130
+ <details>
131
+ <summary><strong>CLI commands</strong></summary>
132
+
133
+ ```
134
+ ember [--version]
135
+ init [--mode {project,global,server}] # initialise store + write config.json
136
+ serve # start the MCP server over stdio
137
+ mirror --project-id <id> [--output-dir memory] [--db-path PATH]
138
+ drain [--queue-path PATH] [--db-path PATH] [--project-id ID]
139
+ ```
140
+
141
+ - **`init`** — creates the store and writes `config.json`. Idempotent: re-running preserves `project_id` and `created`, refreshes `ember_version` / `last_init`. Prints the exact `.mcp.json` snippet for your platform. `--mode server` initialises a server-mode store and config (`client_id` + `hub_url`) for hub sync; set `EMBER_HUB_TOKEN` in the environment.
142
+ - **`serve`** — starts the Ember MCP server over stdio (register this in `.mcp.json`).
143
+ - **`mirror`** — renders stored atoms to Markdown under `memory/` (or `--output-dir`).
144
+ - **`drain`** — drains the capture queue into the store (used by the capture loop).
145
+
146
+ | Mode | DB location | config.json |
147
+ |------|-------------|-------------|
148
+ | `project` (default) | `<project-root>/.ember/ember.db` | `<project-root>/.ember/config.json` |
149
+ | `global` | `~/.ember/global.db` | `~/.ember/config.json` |
150
+ | `server` | `<project-root>/.ember/ember.db` | `<project-root>/.ember/config.json` (server: client_id + hub_url) |
151
+
152
+ </details>
153
+
154
+ <details>
155
+ <summary><strong>Environment variables</strong></summary>
156
+
157
+ | Variable | Description | Default |
158
+ |----------|-------------|---------|
159
+ | `EMBER_MODE` | `project` or `global` | `project` |
160
+ | `EMBER_PROJECT_ROOT` | Root directory for project-mode DB | `cwd` |
161
+ | `EMBER_DB_PATH` | Explicit DB path (overrides mode routing) | — |
162
+ | `EMBER_MODEL_DIR` | Override for ONNX model cache directory | `~/.ember/models/embeddinggemma-300m/` |
163
+ | `EMBER_INVALIDATION_THRESHOLD` | Cosine threshold for auto-invalidation (0–1] | `0.92` |
164
+ | `ANTHROPIC_API_KEY` | Enables optional `ember_flush` enrichment via Claude Haiku; absent ⇒ enrichment skipped | — |
165
+
166
+ </details>
167
+
168
+ <details>
169
+ <summary><strong>Global mode .mcp.json</strong></summary>
170
+
171
+ One shared DB for all projects:
172
+
173
+ ```json
174
+ {
175
+ "mcpServers": {
176
+ "ember": {
177
+ "command": "~/.ember/venv/bin/python",
178
+ "args": ["-m", "ember", "serve"],
179
+ "env": { "EMBER_MODE": "global" }
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
185
+ </details>
186
+
187
+ <details>
188
+ <summary><strong>Model cache</strong></summary>
189
+
190
+ EmbeddingGemma-300m ONNX weights are stored at
191
+ `~/.ember/models/embeddinggemma-300m/` (overridable via `EMBER_MODEL_DIR`).
192
+ `install.sh` fetches them on first run and skips the download afterwards.
193
+
194
+ **One-time setup (gated model):** EmbeddingGemma-300m requires a Hugging Face
195
+ account and license acceptance before the first download:
196
+
197
+ 1. Accept the license at <https://huggingface.co/google/embeddinggemma-300m>
198
+ 2. Run `huggingface-cli login` (token from <https://huggingface.co/settings/tokens>)
199
+ 3. Re-run the installer (or fetch manually below).
200
+
201
+ To fetch manually:
202
+
203
+ ```sh
204
+ huggingface-cli download google/embeddinggemma-300m \
205
+ --local-dir ~/.ember/models/embeddinggemma-300m
206
+ ```
207
+
208
+ </details>
209
+
210
+ <details>
211
+ <summary><strong>MCP tools</strong></summary>
212
+
213
+ | Tool | Purpose |
214
+ |------|---------|
215
+ | `ember_write` | Write a memory record (atom). |
216
+ | `ember_search` | Multi-tier retrieval (BM25 + cosine fusion); tier `R0`–`R3`. |
217
+ | `ember_get` | Fetch a single record by ID (project-scoped). |
218
+ | `ember_list` | List records for a project, newest first. |
219
+ | `ember_invalidate` | Soft-delete (invalidate) a record. |
220
+ | `ember_flush` | Optional Haiku enrichment pass (requires `ANTHROPIC_API_KEY`; skipped otherwise). |
221
+ | `ember_stats` | Record counts and last-flush timestamp. |
222
+
223
+ </details>
224
+
225
+ <details>
226
+ <summary><strong>Optional dependencies</strong></summary>
227
+
228
+ Vector-search acceleration (transparent; activates automatically for large stores):
229
+
230
+ ```sh
231
+ pip install "ember[vec]"
232
+ ```
233
+
234
+ Without `sqlite-vec`, Ember stays on pure-Python cosine — no configuration needed.
235
+
236
+ </details>
237
+
238
+ <details>
239
+ <summary><strong>Platform support</strong></summary>
240
+
241
+ | Platform | Status |
242
+ |----------|--------|
243
+ | macOS (Apple Silicon / Intel) | Supported |
244
+ | Linux (x86-64, arm64) | Supported |
245
+ | Windows | Best-effort |
246
+
247
+ See [Windows setup (best-effort)](#windows-setup-best-effort) below.
248
+
249
+ </details>
250
+
251
+ ---
252
+
253
+ ## Windows setup (best-effort)
254
+
255
+ > Windows support is best-effort. The native installer (`install.ps1`) covers the common case; edge cases (antivirus, execution-policy restrictions, PATH quirks) may need manual adjustment.
256
+
257
+ **Prerequisites**
258
+
259
+ - Python 3.10+ — download from [python.org](https://www.python.org/) or run:
260
+ ```
261
+ winget install Python.Python.3.12
262
+ ```
263
+ Ensure Python is on your `PATH` (the installer wizard has an "Add Python to PATH" checkbox).
264
+
265
+ **Recommended shell**
266
+
267
+ - **PowerShell** — use `install.ps1` (native Windows, no git-bash required):
268
+ ```powershell
269
+ Set-ExecutionPolicy -Scope CurrentUser RemoteSigned # once, if needed
270
+ .\install.ps1
271
+ ```
272
+ - **git-bash** — alternatively run the POSIX `install.sh` inside git-bash.
273
+
274
+ **After install — `.mcp.json` command path**
275
+
276
+ On Windows the Python executable is `Scripts\python.exe`, not `bin/python`:
277
+
278
+ ```json
279
+ {
280
+ "mcpServers": {
281
+ "ember": {
282
+ "command": "%USERPROFILE%\\.ember\\venv\\Scripts\\python.exe",
283
+ "args": ["-m", "ember", "serve"],
284
+ "env": {
285
+ "EMBER_MODE": "project",
286
+ "EMBER_PROJECT_ROOT": "<your-project-root>"
287
+ }
288
+ }
289
+ }
290
+ }
291
+ ```
292
+
293
+ `install.ps1` prints the exact snippet (with your resolved `%USERPROFILE%` path) at the end of the install.
294
+
295
+ **Model download (gated)**
296
+
297
+ EmbeddingGemma-300m requires a one-time license acceptance and `huggingface-cli login` before the download will succeed. See the [Reference](#reference) note above.
298
+
299
+ ---
300
+
301
+ ## Contributing
302
+
303
+ Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). Issues
304
+ labelled **`good first issue`** are a friendly place to start.
305
+
306
+ ## License
307
+
308
+ [MIT](LICENSE) © 2026 David Sandler
@@ -0,0 +1,3 @@
1
+ """Ember — local-first memory system."""
2
+
3
+ __version__ = "0.2.0"