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.
- {engraphis-0.8.8/engraphis.egg-info → engraphis-0.9.2}/PKG-INFO +121 -22
- {engraphis-0.8.8 → engraphis-0.9.2}/README.md +120 -21
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/cloud_license.py +4 -2
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/sync.py +37 -1
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/dashboard_app.py +10 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/license_cloud.py +7 -1
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/webhooks.py +47 -20
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/licensing.py +15 -2
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/redirector.py +0 -1
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/routes/v2_api.py +17 -3
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/routes/v2_team.py +45 -15
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/service.py +200 -15
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/index.html +36 -16
- {engraphis-0.8.8 → engraphis-0.9.2/engraphis.egg-info}/PKG-INFO +121 -22
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis.egg-info/SOURCES.txt +2 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/pyproject.toml +1 -1
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_autosync.py +2 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_billing.py +68 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_cloud_license.py +103 -14
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_dashboard_v2.py +68 -2
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_online_only_enforcement.py +3 -3
- engraphis-0.9.2/tests/test_personal_folders.py +139 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_sync_dashboard.py +28 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_sync_relay.py +2 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_team_audit.py +7 -7
- engraphis-0.9.2/tests/test_v1_auto_migrate.py +126 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_webhook_e2e.py +4 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/LICENSE +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/NOTICE +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/__init__.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/analytics.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/app.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/automation.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/autosync.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/__init__.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/codegraph.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/embedder_api.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/embedder_deterministic.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/embedder_st.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/encrypted_db.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/extractor.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/graph_extractor.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/reranker.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/sync_folder.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/sync_relay.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/vector_numpy.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/backends/vector_sqlitevec.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/billing.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/config.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/__init__.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/consolidate.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/engine.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/graphrank.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/grounded.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/ids.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/interfaces.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/recall.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/resolve.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/schema.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/scoring.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/store.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/core/textutil.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/__init__.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/embedder.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/ingest.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/intelligence.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/recall.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/reweight.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/engines/thoughts.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/graphdata.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/__init__.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/app.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/auth.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/cloud_mount.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/license_registry.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/inspector/sync_relay.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/llm/__init__.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/llm/client.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/logging_setup.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/mcp_server.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/models.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/routes/__init__.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/routes/memory.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/routes/vault.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/__init__.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/engraphis-icon.png +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/engraphis.ico +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/favicon-32.png +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/vendor/d3.min.js +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/vendor/force-graph.LICENSE +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/vendor/force-graph.min.js +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/vendor/marked.min.js +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/static/vendor/purify.min.js +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/stores/__init__.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/stores/graph.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/stores/ledger.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/stores/vaults.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis/stores/vectors.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis.egg-info/dependency_links.txt +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis.egg-info/entry_points.txt +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis.egg-info/requires.txt +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/engraphis.egg-info/top_level.txt +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/__init__.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/auto_maintain.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/backfill_graph.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/cli.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/consolidate.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/init.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/inspector.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/install_shortcuts.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/license_admin.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/mcp_server_http.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/migrate_to_v2.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/sdk_compat.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/seed_from_obsidian.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/smoke_cloud.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/start_dashboard.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/start_server.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/sync.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/test_routes.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/scripts/update.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/setup.cfg +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_analytics.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_app_auth.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_backends_factories.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_cloud_endpoints_mounted.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_codegraph.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_config.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_consolidate.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_core_ids.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_core_store.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_encrypted_store.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_engine.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_eval_external.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_eval_harness.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_extractor.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_graph_extractor.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_graphdata.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_graphrank.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_grounded.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_ingest_entities.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_init.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_inspector.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_inspector_pro.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_licensing.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_logging_setup.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_mcp_server.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_merge.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_migration.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_password_reset.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_provenance_flags.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_ready.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_recall.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_resolve.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_scoring.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_service.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_service_graph.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_service_isolation.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_session_idempotent.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_sync.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_sync_cli.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_v1_hardening.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_v1_licensing.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_vector_numpy.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_webhooks.py +0 -0
- {engraphis-0.8.8 → engraphis-0.9.2}/tests/test_workspace_isolation.py +0 -0
- {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.
|
|
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
|
+
[](https://github.com/Coding-Dev-Tools/engraphis)
|
|
72
|
+
[](https://github.com/Coding-Dev-Tools/engraphis/blob/main/LICENSE)
|
|
72
73
|
[](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** |
|
|
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]"
|
|
183
|
-
pip install "engraphis[server]"
|
|
184
|
-
pip install "engraphis[mcp]"
|
|
185
|
-
pip install engraphis
|
|
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
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
and
|
|
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
|
-
| `
|
|
288
|
-
| `
|
|
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 (
|
|
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
|
+
[](https://github.com/Coding-Dev-Tools/engraphis)
|
|
4
|
+
[](https://github.com/Coding-Dev-Tools/engraphis/blob/main/LICENSE)
|
|
4
5
|
[](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** |
|
|
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]"
|
|
115
|
-
pip install "engraphis[server]"
|
|
116
|
-
pip install "engraphis[mcp]"
|
|
117
|
-
pip install engraphis
|
|
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
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
and
|
|
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
|
-
| `
|
|
220
|
-
| `
|
|
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 (
|
|
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, *,
|
|
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
|
|
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(
|
|
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
|
-
|
|
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}
|