engraphis 0.8.8__tar.gz → 0.9.2__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 (167) hide show
  1. {engraphis-0.8.8/engraphis.egg-info → engraphis-0.9.2}/PKG-INFO +121 -22
  2. {engraphis-0.8.8 → engraphis-0.9.2}/README.md +120 -21
  3. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/cloud_license.py +4 -2
  4. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/sync.py +37 -1
  5. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/dashboard_app.py +10 -0
  6. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/license_cloud.py +7 -1
  7. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/webhooks.py +47 -20
  8. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/licensing.py +15 -2
  9. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/redirector.py +0 -1
  10. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/routes/v2_api.py +17 -3
  11. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/routes/v2_team.py +45 -15
  12. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/service.py +200 -15
  13. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/index.html +36 -16
  14. {engraphis-0.8.8 → engraphis-0.9.2/engraphis.egg-info}/PKG-INFO +121 -22
  15. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis.egg-info/SOURCES.txt +2 -0
  16. {engraphis-0.8.8 → engraphis-0.9.2}/pyproject.toml +1 -1
  17. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_autosync.py +2 -0
  18. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_billing.py +68 -0
  19. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_cloud_license.py +103 -14
  20. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_dashboard_v2.py +68 -2
  21. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_online_only_enforcement.py +3 -3
  22. engraphis-0.9.2/tests/test_personal_folders.py +139 -0
  23. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_sync_dashboard.py +28 -0
  24. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_sync_relay.py +2 -0
  25. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_team_audit.py +7 -7
  26. engraphis-0.9.2/tests/test_v1_auto_migrate.py +126 -0
  27. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_webhook_e2e.py +4 -0
  28. {engraphis-0.8.8 → engraphis-0.9.2}/LICENSE +0 -0
  29. {engraphis-0.8.8 → engraphis-0.9.2}/NOTICE +0 -0
  30. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/__init__.py +0 -0
  31. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/analytics.py +0 -0
  32. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/app.py +0 -0
  33. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/automation.py +0 -0
  34. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/autosync.py +0 -0
  35. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/__init__.py +0 -0
  36. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/codegraph.py +0 -0
  37. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/embedder_api.py +0 -0
  38. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/embedder_deterministic.py +0 -0
  39. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/embedder_st.py +0 -0
  40. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/encrypted_db.py +0 -0
  41. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/extractor.py +0 -0
  42. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/graph_extractor.py +0 -0
  43. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/reranker.py +0 -0
  44. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/sync_folder.py +0 -0
  45. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/sync_relay.py +0 -0
  46. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/vector_numpy.py +0 -0
  47. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/vector_sqlitevec.py +0 -0
  48. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/billing.py +0 -0
  49. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/config.py +0 -0
  50. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/__init__.py +0 -0
  51. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/consolidate.py +0 -0
  52. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/engine.py +0 -0
  53. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/graphrank.py +0 -0
  54. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/grounded.py +0 -0
  55. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/ids.py +0 -0
  56. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/interfaces.py +0 -0
  57. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/recall.py +0 -0
  58. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/resolve.py +0 -0
  59. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/schema.py +0 -0
  60. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/scoring.py +0 -0
  61. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/store.py +0 -0
  62. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/textutil.py +0 -0
  63. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/__init__.py +0 -0
  64. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/embedder.py +0 -0
  65. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/ingest.py +0 -0
  66. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/intelligence.py +0 -0
  67. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/recall.py +0 -0
  68. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/reweight.py +0 -0
  69. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/thoughts.py +0 -0
  70. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/graphdata.py +0 -0
  71. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/__init__.py +0 -0
  72. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/app.py +0 -0
  73. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/auth.py +0 -0
  74. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/cloud_mount.py +0 -0
  75. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/license_registry.py +0 -0
  76. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/sync_relay.py +0 -0
  77. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/llm/__init__.py +0 -0
  78. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/llm/client.py +0 -0
  79. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/logging_setup.py +0 -0
  80. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/mcp_server.py +0 -0
  81. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/models.py +0 -0
  82. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/routes/__init__.py +0 -0
  83. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/routes/memory.py +0 -0
  84. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/routes/vault.py +0 -0
  85. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/__init__.py +0 -0
  86. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/engraphis-icon.png +0 -0
  87. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/engraphis.ico +0 -0
  88. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/favicon-32.png +0 -0
  89. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/vendor/d3.min.js +0 -0
  90. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/vendor/force-graph.LICENSE +0 -0
  91. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/vendor/force-graph.min.js +0 -0
  92. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/vendor/marked.min.js +0 -0
  93. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/vendor/purify.min.js +0 -0
  94. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/stores/__init__.py +0 -0
  95. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/stores/graph.py +0 -0
  96. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/stores/ledger.py +0 -0
  97. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/stores/vaults.py +0 -0
  98. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/stores/vectors.py +0 -0
  99. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis.egg-info/dependency_links.txt +0 -0
  100. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis.egg-info/entry_points.txt +0 -0
  101. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis.egg-info/requires.txt +0 -0
  102. {engraphis-0.8.8 → engraphis-0.9.2}/engraphis.egg-info/top_level.txt +0 -0
  103. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/__init__.py +0 -0
  104. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/auto_maintain.py +0 -0
  105. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/backfill_graph.py +0 -0
  106. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/cli.py +0 -0
  107. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/consolidate.py +0 -0
  108. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/init.py +0 -0
  109. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/inspector.py +0 -0
  110. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/install_shortcuts.py +0 -0
  111. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/license_admin.py +0 -0
  112. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/mcp_server_http.py +0 -0
  113. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/migrate_to_v2.py +0 -0
  114. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/sdk_compat.py +0 -0
  115. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/seed_from_obsidian.py +0 -0
  116. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/smoke_cloud.py +0 -0
  117. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/start_dashboard.py +0 -0
  118. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/start_server.py +0 -0
  119. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/sync.py +0 -0
  120. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/test_routes.py +0 -0
  121. {engraphis-0.8.8 → engraphis-0.9.2}/scripts/update.py +0 -0
  122. {engraphis-0.8.8 → engraphis-0.9.2}/setup.cfg +0 -0
  123. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_analytics.py +0 -0
  124. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_app_auth.py +0 -0
  125. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_backends_factories.py +0 -0
  126. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_cloud_endpoints_mounted.py +0 -0
  127. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_codegraph.py +0 -0
  128. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_config.py +0 -0
  129. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_consolidate.py +0 -0
  130. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_core_ids.py +0 -0
  131. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_core_store.py +0 -0
  132. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_encrypted_store.py +0 -0
  133. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_engine.py +0 -0
  134. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_eval_external.py +0 -0
  135. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_eval_harness.py +0 -0
  136. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_extractor.py +0 -0
  137. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_graph_extractor.py +0 -0
  138. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_graphdata.py +0 -0
  139. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_graphrank.py +0 -0
  140. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_grounded.py +0 -0
  141. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_ingest_entities.py +0 -0
  142. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_init.py +0 -0
  143. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_inspector.py +0 -0
  144. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_inspector_pro.py +0 -0
  145. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_licensing.py +0 -0
  146. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_logging_setup.py +0 -0
  147. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_mcp_server.py +0 -0
  148. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_merge.py +0 -0
  149. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_migration.py +0 -0
  150. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_password_reset.py +0 -0
  151. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_provenance_flags.py +0 -0
  152. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_ready.py +0 -0
  153. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_recall.py +0 -0
  154. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_resolve.py +0 -0
  155. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_scoring.py +0 -0
  156. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_service.py +0 -0
  157. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_service_graph.py +0 -0
  158. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_service_isolation.py +0 -0
  159. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_session_idempotent.py +0 -0
  160. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_sync.py +0 -0
  161. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_sync_cli.py +0 -0
  162. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_v1_hardening.py +0 -0
  163. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_v1_licensing.py +0 -0
  164. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_vector_numpy.py +0 -0
  165. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_webhooks.py +0 -0
  166. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_workspace_isolation.py +0 -0
  167. {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_workspace_ops.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engraphis
3
- Version: 0.8.8
3
+ Version: 0.9.2
4
4
  Summary: Local-first AI memory engine for agents — Ebbinghaus decay, interaction-aware recall, bi-temporal facts, hybrid retrieval, and an MCP server. You bring the LLM.
5
5
  Author: The Engraphis Authors
6
6
  License: Apache-2.0
@@ -67,10 +67,15 @@ Requires-Dist: sqlcipher3-binary>=0.5.0; extra == "test"
67
67
  Dynamic: license-file
68
68
 
69
69
  # Engraphis
70
- https://engraphis.com/
71
70
 
71
+ [![Version](https://img.shields.io/badge/version-0.9.0-blue.svg)](https://github.com/Coding-Dev-Tools/engraphis)
72
+ [![License](https://img.shields.io/badge/license-Apache--2.0-green.svg)](https://github.com/Coding-Dev-Tools/engraphis/blob/main/LICENSE)
72
73
  [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-support-yellow?style=for-the-badge&logo=buy-me-a-coffee)](https://buymeacoffee.com/Jaixii)
73
74
 
75
+ https://engraphis.com/
76
+
77
+ https://discord.com/invite/Wfr2ejBmY
78
+
74
79
  **Give your AI agents a memory. See it, search it, and watch it self-maintain — all in a beautiful WebUI on your own machine.**
75
80
 
76
81
  <br>
@@ -95,14 +100,14 @@ engraphis-dashboard
95
100
  Opens `http://127.0.0.1:8700` in your browser. No cloud, no signup, no API key for memory.
96
101
  Everything lives in a single SQLite file on your machine.
97
102
 
98
- **You'll see the full product** — a dark-themed, sidebar-navigated dashboard with 12 tabs:
103
+ **You'll see the full product** — a dark-themed (with multiple theme options in left sidebar), sidebar-navigated dashboard with 12 tabs:
99
104
 
100
105
  | Tab | What you see |
101
106
  |-----|-------------|
102
107
  | **Overview** | Live memory counts, memory-type mix, and a health summary at a glance |
103
108
  | **Analytics** *(Pro)* | Growth, retention distribution, decay forecast, resolver mix, and top entities — plus a one-click shareable HTML report and a cross-workspace portfolio view |
104
109
  | **Recall** | Hybrid search across the memory bank — each result shows its score breakdown (retention, semantic, lexical, graph, importance, recency) |
105
- | **Memories** | Browse and curate every memory by workspace — click into a full reader with type and retention pills |
110
+ | **Memories** | Browse and curate every memory by workspace — click into a full reader with type and retention pills, drag-to-reorder, inline title/type edits |
106
111
  | **Proactive** | "What should I know right now" — importance × recency × retention, plus the last session handoff |
107
112
  | **Why** | The current answer to a question, and the facts it superseded |
108
113
  | **Timeline** | Bi-temporal history of a topic — what was believed, and when |
@@ -110,9 +115,9 @@ Everything lives in a single SQLite file on your machine.
110
115
  | **Knowledge Graph** | Interactive force-directed graph of entities and their relationships — click any node to see every linked memory |
111
116
  | **Consolidate** | Run a consolidation sweep on demand — see what got distilled and what got pruned |
112
117
  | **Automation** *(Pro)* | Scheduled consolidation + retention policies that keep the store clean on autopilot (dashboard config, plus `scripts/auto_maintain` for cron / Task Scheduler) |
113
- | **Workspaces** | Manage workspace isolation boundaries — switch the active workspace, browse, and organize memory |
114
- | **Team** | Multi-user access with admin / member / viewer roles (Team) |
115
- | **Settings** | License activation (Pro/Team), appearance, and engine/store info |
118
+ | **Workspaces** | Create, rename, describe, copy, merge, and delete workspaces; import files & folders; drag-and-drop upload |
119
+ | **Team** | Multi-user access with PBKDF2 logins, password reset, admin / member / viewer roles, seat management, and team audit log (Team) |
120
+ | **Settings** | License activation (Pro/Team), cloud sync, appearance, and engine/store info |
116
121
 
117
122
  The dashboard is powered by the v2 engine — the same `MemoryService` that backs the MCP server
118
123
  and the Python library. What you see in the UI is what your agents get.
@@ -124,6 +129,7 @@ and the Python library. What you see in the UI is what your agents get.
124
129
  | **Windows** | Double-click **Engraphis Dashboard** on your Desktop or Start Menu (install: `engraphis-dashboard --install-shortcuts`) |
125
130
  | **macOS** | Double-click **Engraphis Dashboard.app** on your Desktop (install: same command) |
126
131
  | **Linux** | Desktop entry in Applications → Development (GNOME/KDE/etc.) |
132
+ | **Docker** | `docker compose up` — see `docker-compose.yml` for the one-command deployment |
127
133
  | **Any** | `engraphis-dashboard` in a terminal |
128
134
 
129
135
  ### Accessibility-first inspection, built in
@@ -159,6 +165,9 @@ embeddings. You bring the LLM only for optional chat/synthesis.
159
165
  - **Code-aware** — AST-powered symbol graph: `engraphis_index_repo` → `engraphis_search_code`.
160
166
  - **Sleep-time consolidation** — scheduled job distills recurring episodes, reports its compaction.
161
167
  - **Scoped** — `workspace → repo → session` hierarchy.
168
+ - **Encryption at rest** — optional SQLCipher (AES-256) whole-database encryption via `ENGRAPHIS_DB_KEY`. No plaintext fallback when a key is set.
169
+ - **Cloud sync** — cross-device and cross-team memory sync with deterministic CRDT merge (folder transport for self-hosting, managed relay for zero-setup). One-click "Sync now" or automatic cadence in the dashboard.
170
+ - **Import & ingest** — drag-and-drop file upload, server-side folder import, and LLM-powered fact extraction from raw text.
162
171
 
163
172
  ---
164
173
 
@@ -172,6 +181,8 @@ embeddings. You bring the LLM only for optional chat/synthesis.
172
181
  | Bi-temporal graph | partial | ✓ | **✓** |
173
182
  | Native multi-repo model | ✗ | ✗ | **✓ (unique)** |
174
183
  | Code-aware (AST/symbol graph) | ✗ | ✗ | **✓ (unique)** |
184
+ | Cloud sync (CRDT merge) | ✗ | ✗ | **✓ (deterministic, no conflict copies)** |
185
+ | Encryption at rest | ✗ | ✗ | **✓ (SQLCipher)** |
175
186
  | MCP-native for coding agents | ✓ | ✗ | **✓** |
176
187
 
177
188
  ---
@@ -179,10 +190,11 @@ embeddings. You bring the LLM only for optional chat/synthesis.
179
190
  ## Install
180
191
 
181
192
  ```bash
182
- pip install "engraphis[all]" # dashboard + MCP server + code graph + everything
183
- pip install "engraphis[server]" # dashboard + REST API
184
- pip install "engraphis[mcp]" # MCP server only
185
- pip install engraphis # core library — numpy only, fully offline
193
+ pip install "engraphis[all]" # dashboard + MCP server + code graph + encryption + everything
194
+ pip install "engraphis[server]" # dashboard + REST API
195
+ pip install "engraphis[mcp]" # MCP server only
196
+ pip install "engraphis[encryption]" # SQLCipher encryption-at-rest extra
197
+ pip install engraphis # core library — numpy only, fully offline
186
198
  ```
187
199
 
188
200
  > First run downloads `all-MiniLM-L6-v2` (~80 MB). Without it, the engine falls back
@@ -198,6 +210,16 @@ engraphis-dashboard # → http://127.0.0.1:8700
198
210
  engraphis-dashboard --install-shortcuts # → Desktop + Start Menu icons
199
211
  ```
200
212
 
213
+ ### Docker
214
+
215
+ ```bash
216
+ docker compose up # → http://127.0.0.1:8700
217
+ ```
218
+
219
+ The default entrypoint is `engraphis-dashboard --no-open`. Set `ENGRAPHIS_API_TOKEN` to require
220
+ authentication, `ENGRAPHIS_DB_KEY` to encrypt the database at rest, and `ENGRAPHIS_LICENSE_KEY`
221
+ to unlock Pro/Team features. See `docker-compose.yml` for all options.
222
+
201
223
  ---
202
224
 
203
225
  ## Quickstart — MCP server (for coding agents)
@@ -232,22 +254,27 @@ The same `MemoryService` backs the dashboard and the MCP server.
232
254
  ## Free forever vs. Pro
233
255
 
234
256
  The engine, dashboard, MCP server, and governance tools are free and Apache-2.0,
235
- permanently. A license key (verified **offline** — no phone-home) unlocks the paid layer.
236
- **Pro is $10/mo ($100/yr), Team is $20/seat/mo ($200/seat/yr)** — and you can unlock every
237
- Pro feature with a **3-day free trial right in the dashboard** (Settings → License), no key
238
- and no card.
257
+ permanently. A license key unlocks the paid layer — **verified offline** (no phone-home)
258
+ for self-hosted keys, or **cloud-enforced** (machine-bound lease, revocable) for
259
+ commercial deployments. **Pro is $10/mo ($100/yr), Team is $20/seat/mo ($200/seat/yr)** —
260
+ and you can unlock every Pro feature with a **3-day free trial right in the dashboard**
261
+ (Settings → License), no key and no card.
239
262
 
240
263
  | | Free (available now) | Pro — $10/mo or $100/yr | Team — $20/seat/mo or $200/seat/yr |
241
264
  |---|---|---|---|
242
265
  | Dashboard WebUI (with built-in inspector) | ✓ | ✓ | ✓ |
243
266
  | Memory engine + 18 MCP tools | ✓ | ✓ | ✓ |
244
267
  | Version-chain diffs, offline knowledge graph | ✓ | ✓ | ✓ |
268
+ | Cloud sync (folder + managed relay) | | ✓ | ✓ |
269
+ | Auto-sync (hands-off cadence) | | ✓ | ✓ |
245
270
  | Analytics: growth, retention, decay forecast + entities | | ✓ | ✓ |
246
271
  | Analytics HTML report (self-contained, shareable) | | ✓ | ✓ |
247
272
  | Automated maintenance: scheduled consolidation + retention policies | | ✓ | ✓ |
248
273
  | Signed compliance export (checksummed bi-temporal bundle) | | ✓ | ✓ |
249
274
  | Priority support | | ✓ | ✓ |
250
275
  | Multi-user dashboard: logins, roles, seat management | | | ✓ |
276
+ | Team audit log + CSV export | | | ✓ |
277
+ | Team invite emails (vendor relay, zero email setup) | | | ✓ |
251
278
 
252
279
  ---
253
280
 
@@ -258,6 +285,8 @@ and no card.
258
285
  | Write | `engraphis_remember` | Store a fact; deterministically resolved (add/reinforce/supersede) |
259
286
  | Write | `engraphis_record_event` | Append a lightweight episodic log entry |
260
287
  | Write | `engraphis_link` | Explicitly connect two related memories |
288
+ | Write | `engraphis_ingest` | Store raw text; Engraphis extracts the discrete facts worth keeping |
289
+ | Write | `engraphis_consolidate` | Run one sleep-time consolidation sweep: distill recurring episodes |
261
290
  | Read | `engraphis_recall` | Hybrid vector + lexical + graph recall |
262
291
  | Read | `engraphis_recall_grounded` | Cited answer from retrieved memories — or abstain |
263
292
  | Read | `engraphis_recall_proactive` | "What should I know right now" — no query needed |
@@ -273,6 +302,58 @@ and no card.
273
302
 
274
303
  ---
275
304
 
305
+ ## Cloud sync
306
+
307
+ Cloud sync keeps your memory store consistent across all your machines — and, on the Team
308
+ tier, across a group — without giving up local-first ownership. It ships two transports:
309
+
310
+ - **Folder transport** — any shared directory (Dropbox, iCloud, Syncthing, a git repo, a
311
+ mounted drive). Zero infrastructure.
312
+ - **Managed relay** — HTTPS against the Engraphis relay, authenticated by your license key.
313
+ One-click in the dashboard or `python -m scripts.sync --relay`.
314
+
315
+ Sync is a **state-based CRDT**: deterministic merge, no conflict copies, no data loss.
316
+ Every field resolves by a commutative, idempotent rule so `merge(A, B) == merge(B, A)`.
317
+ See [`docs/SYNC.md`](docs/SYNC.md) for architecture, security model, and CLI usage.
318
+
319
+ ---
320
+
321
+ ## Encryption at rest
322
+
323
+ Set `ENGRAPHIS_DB_KEY` (or `ENGRAPHIS_DB_KEY_FILE`) and install the extra:
324
+
325
+ ```bash
326
+ pip install "engraphis[encryption]"
327
+ ```
328
+
329
+ The entire database file is transparently encrypted with AES-256 via SQLCipher — full-text
330
+ search, the graph, and every query keep working unchanged. When a key is set, Engraphis
331
+ **fails loud** rather than silently falling back to plaintext. Generate a strong key:
332
+
333
+ ```bash
334
+ python -c "import secrets; print(secrets.token_hex(32))"
335
+ ```
336
+
337
+ > An existing plaintext database cannot be opened with a key — migrate it (dump → import
338
+ > into a fresh keyed DB). See `.env.example` for all encryption options.
339
+
340
+ ---
341
+
342
+ ## Import files & folders
343
+
344
+ Drag-and-drop or server-side import, both member-gated and bounded:
345
+
346
+ - **Dashboard upload** — the Workspaces tab's "Import files & folders" section accepts files
347
+ directly from the browser.
348
+ - **Server-side folder import** — `MemoryService.import_folder()` reads a directory on the
349
+ machine running Engraphis, one memory per file, with path-traversal guards.
350
+ - **MCP ingest** — `engraphis_ingest` accepts raw text and extracts discrete facts
351
+ (when `ENGRAPHIS_EXTRACTOR=llm` is configured; otherwise stores verbatim).
352
+
353
+ All imported memories are marked **untrusted** by default.
354
+
355
+ ---
356
+
276
357
  ## Configuration
277
358
 
278
359
  All via environment (or `.env`):
@@ -283,12 +364,24 @@ All via environment (or `.env`):
283
364
  | `ENGRAPHIS_HOST` | `127.0.0.1` | Server bind address |
284
365
  | `ENGRAPHIS_PORT` | `8700` | Dashboard port |
285
366
  | `ENGRAPHIS_API_TOKEN` | — | If set, REST API requires `Authorization: Bearer <token>` |
367
+ | `ENGRAPHIS_DB_KEY` | — | Encrypt the database at rest (SQLCipher). Or use `ENGRAPHIS_DB_KEY_FILE` |
286
368
  | `ENGRAPHIS_EMBED_MODEL` | `all-MiniLM-L6-v2` | sentence-transformers model |
287
- | `ENGRAPHIS_LLM_PROVIDER` | `openai` | `openai \| anthropic \| google \| openrouter` |
288
- | `ENGRAPHIS_LLM_API_KEY` | — | LLM API key (only for chat/synthesis) |
369
+ | `ENGRAPHIS_EXTRACTOR` | `none` | `none` = store verbatim; `llm` = extract facts via LLM before storing |
370
+ | `ENGRAPHIS_GRAPH_EXTRACTOR` | `regex` | `regex` = dependency-free NER (offline); `none` = disable graph population |
371
+ | `ENGRAPHIS_LLM_PROVIDER` | `openai` | `openai \| anthropic \| google \| openrouter \| custom` |
372
+ | `ENGRAPHIS_LLM_MODEL` | `gpt-4o-mini` | Model name (provider-specific) |
373
+ | `ENGRAPHIS_LLM_API_KEY` | — | LLM API key (only for chat/synthesis and `extractor=llm`) |
374
+ | `ENGRAPHIS_LLM_BASE_URL` | — | Base URL for openrouter / custom OpenAI-compatible endpoints |
289
375
  | `ENGRAPHIS_LICENSE_KEY` | — | Pro/Team key (or `~/.engraphis/license.key`) |
376
+ | `ENGRAPHIS_TEAM_MODE` | — | Set `1` to enable per-user logins + roles |
377
+ | `ENGRAPHIS_LOOP_INTERVAL` | `60` | Background consolidation loop interval in seconds (0 = disabled) |
378
+ | `ENGRAPHIS_DECAY_HALFLIFE_DAYS` | `7` | Ebbinghaus decay half-life (higher = memories persist longer) |
379
+ | `ENGRAPHIS_FORWARDED_ALLOW_IPS` | `127.0.0.1` | Trusted reverse-proxy IPs for TLS termination (`*` = trust all) |
380
+ | `ENGRAPHIS_RELAY_URL` | built-in | Managed sync relay URL (Pro/Team) |
381
+ | `ENGRAPHIS_AUTOSYNC_LOOP` | `1` | Kill switch for the in-process auto-sync loop (0 = off) |
290
382
 
291
- See `.env.example` for the full list.
383
+ See `.env.example` for the full list including commercial/vendor, email delivery, and
384
+ cloud-license enforcement options.
292
385
 
293
386
  ---
294
387
 
@@ -297,16 +390,22 @@ See `.env.example` for the full list.
297
390
  ```
298
391
  engraphis/
299
392
  ├── engraphis/
300
- │ ├── core/ # v2 engine — interfaces, store, recall, scoring, schema
301
- │ ├── backends/ # pluggable embedder / vector index / reranker / codegraph
393
+ │ ├── core/ # v2 engine — interfaces, store, recall, scoring, schema, sync
394
+ │ ├── backends/ # pluggable embedder / vector index / reranker / codegraph / sync transports / encryption
302
395
  │ ├── service.py # validated MemoryService facade
303
396
  │ ├── mcp_server.py # MCP server — 18 tools
304
397
  │ ├── dashboard_app.py # dashboard WebUI (FastAPI)
398
+ │ ├── autosync.py # background auto-sync loop (Pro/Team)
399
+ │ ├── licensing.py # license verification (offline + cloud)
400
+ │ ├── analytics.py # Pro analytics engine
401
+ │ ├── automation.py # scheduled maintenance policies (Pro)
402
+ │ ├── billing.py # Polar webhook fulfillment
305
403
  │ ├── config.py / app.py # env settings / REST server
306
404
  │ └── static/ # dashboard frontend
307
405
  ├── eval/ # offline retrieval eval harness + datasets
308
- ├── tests/ # pytest suite (offline, numpy-only core)
309
- ├── scripts/ # start_dashboard, inspector, cli, init, consolidate
406
+ ├── tests/ # pytest suite (300+ tests, offline numpy-only core)
407
+ ├── scripts/ # start_dashboard, inspector, cli, init, consolidate, sync
408
+ ├── docs/ # SYNC.md, KILO_CODE_INTEGRATION.md
310
409
  ├── Dockerfile / docker-compose.yml
311
410
  └── pyproject.toml
312
411
  ```
@@ -1,8 +1,13 @@
1
1
  # Engraphis
2
- https://engraphis.com/
3
2
 
3
+ [![Version](https://img.shields.io/badge/version-0.9.0-blue.svg)](https://github.com/Coding-Dev-Tools/engraphis)
4
+ [![License](https://img.shields.io/badge/license-Apache--2.0-green.svg)](https://github.com/Coding-Dev-Tools/engraphis/blob/main/LICENSE)
4
5
  [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-support-yellow?style=for-the-badge&logo=buy-me-a-coffee)](https://buymeacoffee.com/Jaixii)
5
6
 
7
+ https://engraphis.com/
8
+
9
+ https://discord.com/invite/Wfr2ejBmY
10
+
6
11
  **Give your AI agents a memory. See it, search it, and watch it self-maintain — all in a beautiful WebUI on your own machine.**
7
12
 
8
13
  <br>
@@ -27,14 +32,14 @@ engraphis-dashboard
27
32
  Opens `http://127.0.0.1:8700` in your browser. No cloud, no signup, no API key for memory.
28
33
  Everything lives in a single SQLite file on your machine.
29
34
 
30
- **You'll see the full product** — a dark-themed, sidebar-navigated dashboard with 12 tabs:
35
+ **You'll see the full product** — a dark-themed (with multiple theme options in left sidebar), sidebar-navigated dashboard with 12 tabs:
31
36
 
32
37
  | Tab | What you see |
33
38
  |-----|-------------|
34
39
  | **Overview** | Live memory counts, memory-type mix, and a health summary at a glance |
35
40
  | **Analytics** *(Pro)* | Growth, retention distribution, decay forecast, resolver mix, and top entities — plus a one-click shareable HTML report and a cross-workspace portfolio view |
36
41
  | **Recall** | Hybrid search across the memory bank — each result shows its score breakdown (retention, semantic, lexical, graph, importance, recency) |
37
- | **Memories** | Browse and curate every memory by workspace — click into a full reader with type and retention pills |
42
+ | **Memories** | Browse and curate every memory by workspace — click into a full reader with type and retention pills, drag-to-reorder, inline title/type edits |
38
43
  | **Proactive** | "What should I know right now" — importance × recency × retention, plus the last session handoff |
39
44
  | **Why** | The current answer to a question, and the facts it superseded |
40
45
  | **Timeline** | Bi-temporal history of a topic — what was believed, and when |
@@ -42,9 +47,9 @@ Everything lives in a single SQLite file on your machine.
42
47
  | **Knowledge Graph** | Interactive force-directed graph of entities and their relationships — click any node to see every linked memory |
43
48
  | **Consolidate** | Run a consolidation sweep on demand — see what got distilled and what got pruned |
44
49
  | **Automation** *(Pro)* | Scheduled consolidation + retention policies that keep the store clean on autopilot (dashboard config, plus `scripts/auto_maintain` for cron / Task Scheduler) |
45
- | **Workspaces** | Manage workspace isolation boundaries — switch the active workspace, browse, and organize memory |
46
- | **Team** | Multi-user access with admin / member / viewer roles (Team) |
47
- | **Settings** | License activation (Pro/Team), appearance, and engine/store info |
50
+ | **Workspaces** | Create, rename, describe, copy, merge, and delete workspaces; import files & folders; drag-and-drop upload |
51
+ | **Team** | Multi-user access with PBKDF2 logins, password reset, admin / member / viewer roles, seat management, and team audit log (Team) |
52
+ | **Settings** | License activation (Pro/Team), cloud sync, appearance, and engine/store info |
48
53
 
49
54
  The dashboard is powered by the v2 engine — the same `MemoryService` that backs the MCP server
50
55
  and the Python library. What you see in the UI is what your agents get.
@@ -56,6 +61,7 @@ and the Python library. What you see in the UI is what your agents get.
56
61
  | **Windows** | Double-click **Engraphis Dashboard** on your Desktop or Start Menu (install: `engraphis-dashboard --install-shortcuts`) |
57
62
  | **macOS** | Double-click **Engraphis Dashboard.app** on your Desktop (install: same command) |
58
63
  | **Linux** | Desktop entry in Applications → Development (GNOME/KDE/etc.) |
64
+ | **Docker** | `docker compose up` — see `docker-compose.yml` for the one-command deployment |
59
65
  | **Any** | `engraphis-dashboard` in a terminal |
60
66
 
61
67
  ### Accessibility-first inspection, built in
@@ -91,6 +97,9 @@ embeddings. You bring the LLM only for optional chat/synthesis.
91
97
  - **Code-aware** — AST-powered symbol graph: `engraphis_index_repo` → `engraphis_search_code`.
92
98
  - **Sleep-time consolidation** — scheduled job distills recurring episodes, reports its compaction.
93
99
  - **Scoped** — `workspace → repo → session` hierarchy.
100
+ - **Encryption at rest** — optional SQLCipher (AES-256) whole-database encryption via `ENGRAPHIS_DB_KEY`. No plaintext fallback when a key is set.
101
+ - **Cloud sync** — cross-device and cross-team memory sync with deterministic CRDT merge (folder transport for self-hosting, managed relay for zero-setup). One-click "Sync now" or automatic cadence in the dashboard.
102
+ - **Import & ingest** — drag-and-drop file upload, server-side folder import, and LLM-powered fact extraction from raw text.
94
103
 
95
104
  ---
96
105
 
@@ -104,6 +113,8 @@ embeddings. You bring the LLM only for optional chat/synthesis.
104
113
  | Bi-temporal graph | partial | ✓ | **✓** |
105
114
  | Native multi-repo model | ✗ | ✗ | **✓ (unique)** |
106
115
  | Code-aware (AST/symbol graph) | ✗ | ✗ | **✓ (unique)** |
116
+ | Cloud sync (CRDT merge) | ✗ | ✗ | **✓ (deterministic, no conflict copies)** |
117
+ | Encryption at rest | ✗ | ✗ | **✓ (SQLCipher)** |
107
118
  | MCP-native for coding agents | ✓ | ✗ | **✓** |
108
119
 
109
120
  ---
@@ -111,10 +122,11 @@ embeddings. You bring the LLM only for optional chat/synthesis.
111
122
  ## Install
112
123
 
113
124
  ```bash
114
- pip install "engraphis[all]" # dashboard + MCP server + code graph + everything
115
- pip install "engraphis[server]" # dashboard + REST API
116
- pip install "engraphis[mcp]" # MCP server only
117
- pip install engraphis # core library — numpy only, fully offline
125
+ pip install "engraphis[all]" # dashboard + MCP server + code graph + encryption + everything
126
+ pip install "engraphis[server]" # dashboard + REST API
127
+ pip install "engraphis[mcp]" # MCP server only
128
+ pip install "engraphis[encryption]" # SQLCipher encryption-at-rest extra
129
+ pip install engraphis # core library — numpy only, fully offline
118
130
  ```
119
131
 
120
132
  > First run downloads `all-MiniLM-L6-v2` (~80 MB). Without it, the engine falls back
@@ -130,6 +142,16 @@ engraphis-dashboard # → http://127.0.0.1:8700
130
142
  engraphis-dashboard --install-shortcuts # → Desktop + Start Menu icons
131
143
  ```
132
144
 
145
+ ### Docker
146
+
147
+ ```bash
148
+ docker compose up # → http://127.0.0.1:8700
149
+ ```
150
+
151
+ The default entrypoint is `engraphis-dashboard --no-open`. Set `ENGRAPHIS_API_TOKEN` to require
152
+ authentication, `ENGRAPHIS_DB_KEY` to encrypt the database at rest, and `ENGRAPHIS_LICENSE_KEY`
153
+ to unlock Pro/Team features. See `docker-compose.yml` for all options.
154
+
133
155
  ---
134
156
 
135
157
  ## Quickstart — MCP server (for coding agents)
@@ -164,22 +186,27 @@ The same `MemoryService` backs the dashboard and the MCP server.
164
186
  ## Free forever vs. Pro
165
187
 
166
188
  The engine, dashboard, MCP server, and governance tools are free and Apache-2.0,
167
- permanently. A license key (verified **offline** — no phone-home) unlocks the paid layer.
168
- **Pro is $10/mo ($100/yr), Team is $20/seat/mo ($200/seat/yr)** — and you can unlock every
169
- Pro feature with a **3-day free trial right in the dashboard** (Settings → License), no key
170
- and no card.
189
+ permanently. A license key unlocks the paid layer — **verified offline** (no phone-home)
190
+ for self-hosted keys, or **cloud-enforced** (machine-bound lease, revocable) for
191
+ commercial deployments. **Pro is $10/mo ($100/yr), Team is $20/seat/mo ($200/seat/yr)** —
192
+ and you can unlock every Pro feature with a **3-day free trial right in the dashboard**
193
+ (Settings → License), no key and no card.
171
194
 
172
195
  | | Free (available now) | Pro — $10/mo or $100/yr | Team — $20/seat/mo or $200/seat/yr |
173
196
  |---|---|---|---|
174
197
  | Dashboard WebUI (with built-in inspector) | ✓ | ✓ | ✓ |
175
198
  | Memory engine + 18 MCP tools | ✓ | ✓ | ✓ |
176
199
  | Version-chain diffs, offline knowledge graph | ✓ | ✓ | ✓ |
200
+ | Cloud sync (folder + managed relay) | | ✓ | ✓ |
201
+ | Auto-sync (hands-off cadence) | | ✓ | ✓ |
177
202
  | Analytics: growth, retention, decay forecast + entities | | ✓ | ✓ |
178
203
  | Analytics HTML report (self-contained, shareable) | | ✓ | ✓ |
179
204
  | Automated maintenance: scheduled consolidation + retention policies | | ✓ | ✓ |
180
205
  | Signed compliance export (checksummed bi-temporal bundle) | | ✓ | ✓ |
181
206
  | Priority support | | ✓ | ✓ |
182
207
  | Multi-user dashboard: logins, roles, seat management | | | ✓ |
208
+ | Team audit log + CSV export | | | ✓ |
209
+ | Team invite emails (vendor relay, zero email setup) | | | ✓ |
183
210
 
184
211
  ---
185
212
 
@@ -190,6 +217,8 @@ and no card.
190
217
  | Write | `engraphis_remember` | Store a fact; deterministically resolved (add/reinforce/supersede) |
191
218
  | Write | `engraphis_record_event` | Append a lightweight episodic log entry |
192
219
  | Write | `engraphis_link` | Explicitly connect two related memories |
220
+ | Write | `engraphis_ingest` | Store raw text; Engraphis extracts the discrete facts worth keeping |
221
+ | Write | `engraphis_consolidate` | Run one sleep-time consolidation sweep: distill recurring episodes |
193
222
  | Read | `engraphis_recall` | Hybrid vector + lexical + graph recall |
194
223
  | Read | `engraphis_recall_grounded` | Cited answer from retrieved memories — or abstain |
195
224
  | Read | `engraphis_recall_proactive` | "What should I know right now" — no query needed |
@@ -205,6 +234,58 @@ and no card.
205
234
 
206
235
  ---
207
236
 
237
+ ## Cloud sync
238
+
239
+ Cloud sync keeps your memory store consistent across all your machines — and, on the Team
240
+ tier, across a group — without giving up local-first ownership. It ships two transports:
241
+
242
+ - **Folder transport** — any shared directory (Dropbox, iCloud, Syncthing, a git repo, a
243
+ mounted drive). Zero infrastructure.
244
+ - **Managed relay** — HTTPS against the Engraphis relay, authenticated by your license key.
245
+ One-click in the dashboard or `python -m scripts.sync --relay`.
246
+
247
+ Sync is a **state-based CRDT**: deterministic merge, no conflict copies, no data loss.
248
+ Every field resolves by a commutative, idempotent rule so `merge(A, B) == merge(B, A)`.
249
+ See [`docs/SYNC.md`](docs/SYNC.md) for architecture, security model, and CLI usage.
250
+
251
+ ---
252
+
253
+ ## Encryption at rest
254
+
255
+ Set `ENGRAPHIS_DB_KEY` (or `ENGRAPHIS_DB_KEY_FILE`) and install the extra:
256
+
257
+ ```bash
258
+ pip install "engraphis[encryption]"
259
+ ```
260
+
261
+ The entire database file is transparently encrypted with AES-256 via SQLCipher — full-text
262
+ search, the graph, and every query keep working unchanged. When a key is set, Engraphis
263
+ **fails loud** rather than silently falling back to plaintext. Generate a strong key:
264
+
265
+ ```bash
266
+ python -c "import secrets; print(secrets.token_hex(32))"
267
+ ```
268
+
269
+ > An existing plaintext database cannot be opened with a key — migrate it (dump → import
270
+ > into a fresh keyed DB). See `.env.example` for all encryption options.
271
+
272
+ ---
273
+
274
+ ## Import files & folders
275
+
276
+ Drag-and-drop or server-side import, both member-gated and bounded:
277
+
278
+ - **Dashboard upload** — the Workspaces tab's "Import files & folders" section accepts files
279
+ directly from the browser.
280
+ - **Server-side folder import** — `MemoryService.import_folder()` reads a directory on the
281
+ machine running Engraphis, one memory per file, with path-traversal guards.
282
+ - **MCP ingest** — `engraphis_ingest` accepts raw text and extracts discrete facts
283
+ (when `ENGRAPHIS_EXTRACTOR=llm` is configured; otherwise stores verbatim).
284
+
285
+ All imported memories are marked **untrusted** by default.
286
+
287
+ ---
288
+
208
289
  ## Configuration
209
290
 
210
291
  All via environment (or `.env`):
@@ -215,12 +296,24 @@ All via environment (or `.env`):
215
296
  | `ENGRAPHIS_HOST` | `127.0.0.1` | Server bind address |
216
297
  | `ENGRAPHIS_PORT` | `8700` | Dashboard port |
217
298
  | `ENGRAPHIS_API_TOKEN` | — | If set, REST API requires `Authorization: Bearer <token>` |
299
+ | `ENGRAPHIS_DB_KEY` | — | Encrypt the database at rest (SQLCipher). Or use `ENGRAPHIS_DB_KEY_FILE` |
218
300
  | `ENGRAPHIS_EMBED_MODEL` | `all-MiniLM-L6-v2` | sentence-transformers model |
219
- | `ENGRAPHIS_LLM_PROVIDER` | `openai` | `openai \| anthropic \| google \| openrouter` |
220
- | `ENGRAPHIS_LLM_API_KEY` | — | LLM API key (only for chat/synthesis) |
301
+ | `ENGRAPHIS_EXTRACTOR` | `none` | `none` = store verbatim; `llm` = extract facts via LLM before storing |
302
+ | `ENGRAPHIS_GRAPH_EXTRACTOR` | `regex` | `regex` = dependency-free NER (offline); `none` = disable graph population |
303
+ | `ENGRAPHIS_LLM_PROVIDER` | `openai` | `openai \| anthropic \| google \| openrouter \| custom` |
304
+ | `ENGRAPHIS_LLM_MODEL` | `gpt-4o-mini` | Model name (provider-specific) |
305
+ | `ENGRAPHIS_LLM_API_KEY` | — | LLM API key (only for chat/synthesis and `extractor=llm`) |
306
+ | `ENGRAPHIS_LLM_BASE_URL` | — | Base URL for openrouter / custom OpenAI-compatible endpoints |
221
307
  | `ENGRAPHIS_LICENSE_KEY` | — | Pro/Team key (or `~/.engraphis/license.key`) |
308
+ | `ENGRAPHIS_TEAM_MODE` | — | Set `1` to enable per-user logins + roles |
309
+ | `ENGRAPHIS_LOOP_INTERVAL` | `60` | Background consolidation loop interval in seconds (0 = disabled) |
310
+ | `ENGRAPHIS_DECAY_HALFLIFE_DAYS` | `7` | Ebbinghaus decay half-life (higher = memories persist longer) |
311
+ | `ENGRAPHIS_FORWARDED_ALLOW_IPS` | `127.0.0.1` | Trusted reverse-proxy IPs for TLS termination (`*` = trust all) |
312
+ | `ENGRAPHIS_RELAY_URL` | built-in | Managed sync relay URL (Pro/Team) |
313
+ | `ENGRAPHIS_AUTOSYNC_LOOP` | `1` | Kill switch for the in-process auto-sync loop (0 = off) |
222
314
 
223
- See `.env.example` for the full list.
315
+ See `.env.example` for the full list including commercial/vendor, email delivery, and
316
+ cloud-license enforcement options.
224
317
 
225
318
  ---
226
319
 
@@ -229,16 +322,22 @@ See `.env.example` for the full list.
229
322
  ```
230
323
  engraphis/
231
324
  ├── engraphis/
232
- │ ├── core/ # v2 engine — interfaces, store, recall, scoring, schema
233
- │ ├── backends/ # pluggable embedder / vector index / reranker / codegraph
325
+ │ ├── core/ # v2 engine — interfaces, store, recall, scoring, schema, sync
326
+ │ ├── backends/ # pluggable embedder / vector index / reranker / codegraph / sync transports / encryption
234
327
  │ ├── service.py # validated MemoryService facade
235
328
  │ ├── mcp_server.py # MCP server — 18 tools
236
329
  │ ├── dashboard_app.py # dashboard WebUI (FastAPI)
330
+ │ ├── autosync.py # background auto-sync loop (Pro/Team)
331
+ │ ├── licensing.py # license verification (offline + cloud)
332
+ │ ├── analytics.py # Pro analytics engine
333
+ │ ├── automation.py # scheduled maintenance policies (Pro)
334
+ │ ├── billing.py # Polar webhook fulfillment
237
335
  │ ├── config.py / app.py # env settings / REST server
238
336
  │ └── static/ # dashboard frontend
239
337
  ├── eval/ # offline retrieval eval harness + datasets
240
- ├── tests/ # pytest suite (offline, numpy-only core)
241
- ├── scripts/ # start_dashboard, inspector, cli, init, consolidate
338
+ ├── tests/ # pytest suite (300+ tests, offline numpy-only core)
339
+ ├── scripts/ # start_dashboard, inspector, cli, init, consolidate, sync
340
+ ├── docs/ # SYNC.md, KILO_CODE_INTEGRATION.md
242
341
  ├── Dockerfile / docker-compose.yml
243
342
  └── pyproject.toml
244
343
  ```
@@ -194,7 +194,8 @@ _INVITE_TIMEOUT = 10.0
194
194
 
195
195
 
196
196
  def send_team_invite(base_url: str, key: str, to: str, name: str, role: str,
197
- invited_by: str, *, timeout: float = _INVITE_TIMEOUT) -> Tuple[bool, str]:
197
+ invited_by: str, *, dashboard_url: str = "",
198
+ timeout: float = _INVITE_TIMEOUT) -> Tuple[bool, str]:
198
199
  """POST a team-invite request to the vendor relay's ``/license/v1/team-invite``.
199
200
 
200
201
  Used by a self-hosted Team dashboard (``routes.v2_team.add_user``) that has no
@@ -209,7 +210,8 @@ def send_team_invite(base_url: str, key: str, to: str, name: str, role: str,
209
210
  """
210
211
  url = base_url.rstrip("/") + "/license/v1/team-invite"
211
212
  data = json.dumps({"key": key, "to": to, "name": name, "role": role,
212
- "invited_by": invited_by}).encode("utf-8")
213
+ "invited_by": invited_by, "dashboard_url": dashboard_url}
214
+ ).encode("utf-8")
213
215
  req = urllib.request.Request(
214
216
  url, data=data, method="POST", headers={"Content-Type": "application/json"})
215
217
  try:
@@ -264,14 +264,50 @@ def _reject_nonfinite(token: str):
264
264
  raise ValueError("non-finite JSON constant: %s" % token)
265
265
 
266
266
 
267
+ _MAX_BUNDLE_DEPTH = 200 # generous; real bundles are shallow. Explicit DoS guard so
268
+ # deeply-nested input is rejected on every Python version (3.12+'s JSON scanner no
269
+ # longer raises RecursionError for ~1000-deep input, so we can't rely on that alone).
270
+
271
+
272
+ def _scan_depth(s: str) -> int:
273
+ """Cheap max-nesting-depth scan that skips JSON string literals; used to reject
274
+ pathologically deep bundles without relying on the JSON scanner's RecursionError."""
275
+ depth = 0
276
+ max_depth = 0
277
+ in_str = False
278
+ esc = False
279
+ for ch in s:
280
+ if in_str:
281
+ if esc:
282
+ esc = False
283
+ elif ch == "\\":
284
+ esc = True
285
+ elif ch == '"':
286
+ in_str = False
287
+ continue
288
+ if ch == '"':
289
+ in_str = True
290
+ elif ch in "[{":
291
+ depth += 1
292
+ if depth > max_depth:
293
+ max_depth = depth
294
+ elif ch in "]}":
295
+ if depth > 0:
296
+ depth -= 1
297
+ return max_depth
298
+
299
+
267
300
  def loads_strict(data: bytes):
268
301
  """Parse untrusted bundle bytes, rejecting the non-standard ``Infinity``/``NaN``
269
302
  tokens Python's ``json`` accepts by default (they later raise ``OverflowError`` in
270
303
  ``int()`` and would otherwise abort the whole sync run). Deeply-nested input that
271
304
  would raise ``RecursionError`` in the JSON scanner is normalized to ``ValueError``
272
305
  so a single hostile bundle can't crash the whole sync run (DoS)."""
306
+ text = data.decode("utf-8")
307
+ if _scan_depth(text) > _MAX_BUNDLE_DEPTH:
308
+ raise ValueError("bundle JSON is nested too deeply")
273
309
  try:
274
- return json.loads(data.decode("utf-8"), parse_constant=_reject_nonfinite)
310
+ return json.loads(text, parse_constant=_reject_nonfinite)
275
311
  except RecursionError:
276
312
  raise ValueError("bundle JSON is nested too deeply")
277
313
 
@@ -111,6 +111,13 @@ def create_app() -> FastAPI:
111
111
 
112
112
  @app.middleware("http")
113
113
  async def _auth_gate(request: Request, call_next):
114
+ from engraphis.service import set_current_user
115
+ # Clear any user bound to this context before we decide who (if anyone) is calling,
116
+ # so a personal-folder check can never inherit a stale identity from a prior request
117
+ # served on the same worker context. The team branch below rebinds the real user;
118
+ # public paths, the bearer bypass, and single-user mode all leave it cleared, which
119
+ # is exactly "no per-user restriction".
120
+ set_current_user(None)
114
121
  path = request.url.path
115
122
  if not path.startswith("/api/") or path in _PUBLIC or path.startswith("/api/docs") \
116
123
  or path.startswith("/api/openapi"):
@@ -132,6 +139,9 @@ def create_app() -> FastAPI:
132
139
  return JSONResponse({"error": "requires the %s role" % need},
133
140
  status_code=403)
134
141
  request.state.user = user
142
+ # Bind the identity the service reads to enforce personal-folder ownership on
143
+ # every workspace-scoped read/write (see MemoryService._authorize_workspace).
144
+ set_current_user(user)
135
145
  return await call_next(request)
136
146
  # Single-user modes: optional bearer token, exactly as before team mode existed.
137
147
  if settings.api_token and not _bearer_ok(request):
@@ -314,6 +314,7 @@ async def team_invite(request: Request):
314
314
  name = (body.get("name") or "").strip()[:120]
315
315
  role = (body.get("role") or "member").strip()[:32]
316
316
  invited_by = (body.get("invited_by") or "").strip().lower()
317
+ dashboard_url = (body.get("dashboard_url") or "").strip()[:2048]
317
318
 
318
319
  lic = reg.verify_for_feature(key, "team") # bad/expired/wrong-plan/revoked → 402
319
320
  if not _EMAIL_RE.match(to):
@@ -330,7 +331,12 @@ async def team_invite(request: Request):
330
331
 
331
332
  from engraphis.inspector.webhooks import send_team_invite_email
332
333
  try:
333
- await asyncio.to_thread(send_team_invite_email, to, name, role, invited_by=invited_by)
334
+ # Echo the just-verified Team key into the email so the relay-delivered invite
335
+ # also carries Pro activation (the key is this account's shared team key — the
336
+ # same one the caller already proved they hold). dashboard_url is forwarded from
337
+ # the admin (empty => "ask your admin"), never the relay host's own env value.
338
+ await asyncio.to_thread(send_team_invite_email, to, name, role,
339
+ invited_by=invited_by, key=key, dashboard_url=dashboard_url)
334
340
  except Exception as exc: # noqa: BLE001 — surface a safe message, don't leak internals
335
341
  return JSONResponse({"error": "delivery failed: %s" % exc}, status_code=502)
336
342
  return {"sent": True}