lorekeep 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lorekeep-0.1.0/.github/workflows/release.yml +32 -0
- lorekeep-0.1.0/.gitignore +31 -0
- lorekeep-0.1.0/.lorekeep/config.yaml.example +31 -0
- lorekeep-0.1.0/LICENSE +21 -0
- lorekeep-0.1.0/PKG-INFO +246 -0
- lorekeep-0.1.0/README.md +224 -0
- lorekeep-0.1.0/SECURITY.md +76 -0
- lorekeep-0.1.0/docs/compile.md +69 -0
- lorekeep-0.1.0/docs/serve.md +65 -0
- lorekeep-0.1.0/docs/superpowers/plans/2026-06-14-lorekeep-plan-a-compile-pipeline.md +2393 -0
- lorekeep-0.1.0/docs/superpowers/plans/2026-06-14-lorekeep-plan-b-serve-mcp.md +2049 -0
- lorekeep-0.1.0/docs/superpowers/plans/2026-06-15-lorekeep-plan-c-data-home-dev-mode.md +618 -0
- lorekeep-0.1.0/docs/superpowers/specs/2026-06-14-lorekeep-temporal-kg-mcp-design.md +399 -0
- lorekeep-0.1.0/graph/schema.json +26 -0
- lorekeep-0.1.0/pyproject.toml +41 -0
- lorekeep-0.1.0/src/lorekeep/__init__.py +3 -0
- lorekeep-0.1.0/src/lorekeep/cli.py +229 -0
- lorekeep-0.1.0/src/lorekeep/compile/__init__.py +0 -0
- lorekeep-0.1.0/src/lorekeep/compile/extract.py +150 -0
- lorekeep-0.1.0/src/lorekeep/compile/ingest.py +55 -0
- lorekeep-0.1.0/src/lorekeep/compile/providers.py +49 -0
- lorekeep-0.1.0/src/lorekeep/compile/resolve.py +111 -0
- lorekeep-0.1.0/src/lorekeep/compile/writer.py +63 -0
- lorekeep-0.1.0/src/lorekeep/config.py +39 -0
- lorekeep-0.1.0/src/lorekeep/defaults.py +44 -0
- lorekeep-0.1.0/src/lorekeep/eval/__init__.py +0 -0
- lorekeep-0.1.0/src/lorekeep/eval/construction.py +97 -0
- lorekeep-0.1.0/src/lorekeep/eval/gold.py +31 -0
- lorekeep-0.1.0/src/lorekeep/eval/retrieval.py +46 -0
- lorekeep-0.1.0/src/lorekeep/facts_io.py +22 -0
- lorekeep-0.1.0/src/lorekeep/integrations/__init__.py +0 -0
- lorekeep-0.1.0/src/lorekeep/integrations/claude_code.py +19 -0
- lorekeep-0.1.0/src/lorekeep/integrations/codex.py +56 -0
- lorekeep-0.1.0/src/lorekeep/integrations/common.py +23 -0
- lorekeep-0.1.0/src/lorekeep/integrations/cursor.py +21 -0
- lorekeep-0.1.0/src/lorekeep/mcp_server.py +120 -0
- lorekeep-0.1.0/src/lorekeep/models.py +130 -0
- lorekeep-0.1.0/src/lorekeep/paths.py +58 -0
- lorekeep-0.1.0/src/lorekeep/perm/__init__.py +0 -0
- lorekeep-0.1.0/src/lorekeep/perm/ns.py +113 -0
- lorekeep-0.1.0/src/lorekeep/pipeline.py +67 -0
- lorekeep-0.1.0/src/lorekeep/schema_io.py +12 -0
- lorekeep-0.1.0/src/lorekeep/store/__init__.py +0 -0
- lorekeep-0.1.0/src/lorekeep/store/fts.py +54 -0
- lorekeep-0.1.0/src/lorekeep/store/graph.py +137 -0
- lorekeep-0.1.0/tests/conftest.py +7 -0
- lorekeep-0.1.0/tests/fixtures/gold/payments.facts.jsonl +6 -0
- lorekeep-0.1.0/tests/fixtures/raw/backend/payments.md +11 -0
- lorekeep-0.1.0/tests/fixtures/retrieval/questions.json +24 -0
- lorekeep-0.1.0/tests/fixtures/schema.json +13 -0
- lorekeep-0.1.0/tests/test_compile_cli.py +23 -0
- lorekeep-0.1.0/tests/test_config.py +24 -0
- lorekeep-0.1.0/tests/test_construction_eval.py +105 -0
- lorekeep-0.1.0/tests/test_defaults.py +21 -0
- lorekeep-0.1.0/tests/test_determinism.py +53 -0
- lorekeep-0.1.0/tests/test_doctor_cli.py +25 -0
- lorekeep-0.1.0/tests/test_eval_cli.py +35 -0
- lorekeep-0.1.0/tests/test_extract.py +125 -0
- lorekeep-0.1.0/tests/test_fts.py +31 -0
- lorekeep-0.1.0/tests/test_graph_store.py +103 -0
- lorekeep-0.1.0/tests/test_ingest.py +47 -0
- lorekeep-0.1.0/tests/test_init_cli.py +31 -0
- lorekeep-0.1.0/tests/test_integrations.py +68 -0
- lorekeep-0.1.0/tests/test_mcp_add_cli.py +28 -0
- lorekeep-0.1.0/tests/test_mcp_reload.py +32 -0
- lorekeep-0.1.0/tests/test_mcp_server.py +74 -0
- lorekeep-0.1.0/tests/test_models.py +46 -0
- lorekeep-0.1.0/tests/test_paths.py +57 -0
- lorekeep-0.1.0/tests/test_perm.py +122 -0
- lorekeep-0.1.0/tests/test_pipeline.py +45 -0
- lorekeep-0.1.0/tests/test_providers.py +22 -0
- lorekeep-0.1.0/tests/test_resolve.py +66 -0
- lorekeep-0.1.0/tests/test_retrieval_eval.py +17 -0
- lorekeep-0.1.0/tests/test_schema_io.py +12 -0
- lorekeep-0.1.0/tests/test_serve_cli.py +28 -0
- lorekeep-0.1.0/tests/test_smoke.py +10 -0
- lorekeep-0.1.0/tests/test_writer.py +54 -0
- lorekeep-0.1.0/uv.lock +2377 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Publish to PyPI via OIDC trusted publishing (no API token).
|
|
4
|
+
# Triggers on a published GitHub Release. Requires a "pending trusted publisher"
|
|
5
|
+
# configured on PyPI (project = lorekeep, repo = manhhailua/lorekeep,
|
|
6
|
+
# workflow = release.yml, environment = blank) before the first release.
|
|
7
|
+
on:
|
|
8
|
+
release:
|
|
9
|
+
types: [published]
|
|
10
|
+
workflow_dispatch: {}
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
id-token: write # required for PyPI trusted publishing
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build-and-publish:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
environment: pypi
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Set up uv
|
|
24
|
+
uses: astral-sh/setup-uv@v5
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
|
|
28
|
+
- name: Build sdist + wheel
|
|
29
|
+
run: uv build
|
|
30
|
+
|
|
31
|
+
- name: Publish to PyPI
|
|
32
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Lorekeep local-only artifacts (never committed) — except the config template
|
|
2
|
+
.lorekeep/*
|
|
3
|
+
!.lorekeep/config.yaml.example
|
|
4
|
+
*.sqlite
|
|
5
|
+
*.sqlite-*
|
|
6
|
+
|
|
7
|
+
# Lorekeep compiled graph (reproducible via `lorekeep compile`; keep schema.json)
|
|
8
|
+
graph/facts.jsonl
|
|
9
|
+
graph/manifest.json
|
|
10
|
+
|
|
11
|
+
# Personal trial data (local-only; do not commit — contains personal/work memory)
|
|
12
|
+
raw/*-memory/
|
|
13
|
+
raw/personal/
|
|
14
|
+
.mcp.json
|
|
15
|
+
|
|
16
|
+
# Python
|
|
17
|
+
__pycache__/
|
|
18
|
+
*.py[cod]
|
|
19
|
+
.venv/
|
|
20
|
+
venv/
|
|
21
|
+
dist/
|
|
22
|
+
build/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.ruff_cache/
|
|
25
|
+
.mypy_cache/
|
|
26
|
+
.pytest_cache/
|
|
27
|
+
|
|
28
|
+
# OS / editors
|
|
29
|
+
.DS_Store
|
|
30
|
+
.idea/
|
|
31
|
+
.vscode/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Copy to .lorekeep/config.yaml (gitignored). Provider used at compile time only.
|
|
2
|
+
# API keys NEVER go in this file - set them as env vars (api_key_env names which).
|
|
3
|
+
#
|
|
4
|
+
# Option A: Alibaba ModelStudio / DashScope (international, OpenAI-compatible)
|
|
5
|
+
provider:
|
|
6
|
+
backend: openai
|
|
7
|
+
model: openai/qwen-plus # qwen-max | qwen-plus | qwen-turbo
|
|
8
|
+
api_base: https://dashscope-intl.aliyuncs.com/compatible-mode/v1
|
|
9
|
+
api_key_env: DASHSCOPE_API_KEY # export DASHSCOPE_API_KEY=...
|
|
10
|
+
temperature: 0.0
|
|
11
|
+
#
|
|
12
|
+
# Option B: OpenAI
|
|
13
|
+
# provider:
|
|
14
|
+
# backend: openai
|
|
15
|
+
# model: openai/gpt-4o-mini
|
|
16
|
+
# api_base: null
|
|
17
|
+
# api_key_env: OPENAI_API_KEY
|
|
18
|
+
# temperature: 0.0
|
|
19
|
+
#
|
|
20
|
+
# Option C: Ollama (local, strict privacy)
|
|
21
|
+
# provider:
|
|
22
|
+
# backend: ollama
|
|
23
|
+
# model: ollama/llama3
|
|
24
|
+
# api_base: http://localhost:11434
|
|
25
|
+
# api_key_env: null
|
|
26
|
+
# temperature: 0.0
|
|
27
|
+
compile:
|
|
28
|
+
chunk_lines: 60
|
|
29
|
+
ns:
|
|
30
|
+
default: [public]
|
|
31
|
+
install_source: pypi # pypi (portable .mcp.json) | local | git+URL | path
|
lorekeep-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Manh Pham
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
lorekeep-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lorekeep
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Temporal knowledge graph for AI agents via MCP
|
|
5
|
+
Project-URL: Homepage, https://github.com/manhhailua/lorekeep
|
|
6
|
+
Project-URL: Repository, https://github.com/manhhailua/lorekeep
|
|
7
|
+
Project-URL: Security, https://github.com/manhhailua/lorekeep/security/policy
|
|
8
|
+
Author: Manh Pham
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Requires-Dist: litellm>=1.40
|
|
13
|
+
Requires-Dist: mcp>=1.0
|
|
14
|
+
Requires-Dist: mistune>=3.0
|
|
15
|
+
Requires-Dist: networkx>=3.2
|
|
16
|
+
Requires-Dist: platformdirs>=4.0
|
|
17
|
+
Requires-Dist: pydantic>=2.6
|
|
18
|
+
Requires-Dist: pyyaml>=6.0
|
|
19
|
+
Requires-Dist: rich>=13.7
|
|
20
|
+
Requires-Dist: typer>=0.12
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# Lorekeep
|
|
24
|
+
|
|
25
|
+
**A temporal knowledge graph for AI agents, served read-only over MCP.**
|
|
26
|
+
|
|
27
|
+
[](LICENSE)
|
|
28
|
+
|
|
29
|
+
Lorekeep compiles a team's raw documentation into a versioned, time-aware
|
|
30
|
+
knowledge graph (`facts.jsonl`) and exposes it to coding agents (Claude Code,
|
|
31
|
+
Cursor, Codex) through the Model Context Protocol — with per-namespace
|
|
32
|
+
permission and zero servers to run.
|
|
33
|
+
|
|
34
|
+
It applies Andrej Karpathy's "LLM Knowledge Base" idea: raw docs are the
|
|
35
|
+
**source code**, the compiled graph is the **executable**. Knowledge is
|
|
36
|
+
processed once at compile time, not re-RAG'd on every query.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Why
|
|
41
|
+
|
|
42
|
+
Existing tools each miss part of what a team needs:
|
|
43
|
+
|
|
44
|
+
| | file-based | temporal KG | compile step | team permission | MCP |
|
|
45
|
+
|---|---|---|---|---|---|
|
|
46
|
+
| Obsidian + MCP | ✅ | ❌ | ❌ | ❌ | ✅ |
|
|
47
|
+
| mcp-knowledge-graph | ✅ | ❌ | ❌ | ❌ (local) | ✅ |
|
|
48
|
+
| mem0 / cognee | ❌ (DB) | partial | ❌ | partial (DB) | ✅ |
|
|
49
|
+
|
|
50
|
+
Lorekeep targets the gap: **strictly file-based + temporal graph + compile-once +
|
|
51
|
+
namespace-scoped permission + MCP** — for team-level (not just single-user)
|
|
52
|
+
knowledge.
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
- **Compile-only** — a curator (human + LLM) builds the graph; agents only read.
|
|
57
|
+
No write path, no concurrency hell, deterministic output.
|
|
58
|
+
- **File-sovereign** — `facts.jsonl` (one fact per line, sorted) is the single
|
|
59
|
+
source of truth and the sync unit (git or S3). No binary store committed.
|
|
60
|
+
- **Temporal** — every fact carries `valid_from`/`valid_to` (half-open
|
|
61
|
+
`[from, to)`); query "what was true at *T*", history, diffs.
|
|
62
|
+
- **Namespace permission** — facts are tagged `ns` from the directory tree
|
|
63
|
+
(`raw/<ns>/`); agents scoped to namespaces; cross-namespace edges
|
|
64
|
+
hidden unless both endpoints are visible. Deny-by-default.
|
|
65
|
+
- **MCP, stdio-first** — `lorekeep serve` exposes 8 read-only tools; `lorekeep mcp add`
|
|
66
|
+
wires Claude Code / Cursor / Codex. No server process to babysit.
|
|
67
|
+
- **Lazy-reload** — `lorekeep compile` updates the graph; the MCP server
|
|
68
|
+
auto-refreshes on the next query. Connect once, use forever.
|
|
69
|
+
- **Provider-pluggable extraction** — litellm (OpenAI / Anthropic /
|
|
70
|
+
DashScope/Qwen / Ollama). Strict-privacy → Ollama, fully local.
|
|
71
|
+
- **Tier-1 eval** — extraction P/R/F1 vs a gold corpus, entity-resolution F1,
|
|
72
|
+
graph-structure metrics, determinism property tests.
|
|
73
|
+
|
|
74
|
+
## Install
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# from PyPI:
|
|
78
|
+
uvx lorekeep init # try it without installing
|
|
79
|
+
|
|
80
|
+
# or from a clone:
|
|
81
|
+
git clone https://github.com/manhhailua/lorekeep && cd lorekeep
|
|
82
|
+
uv tool install . # installs the `lorekeep` command
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Quickstart
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# 1. bootstrap a data home (~/.config/lorekeep + ~/.local/share/lorekeep)
|
|
89
|
+
uvx lorekeep init
|
|
90
|
+
|
|
91
|
+
# 2. add docs under the data home's raw/<namespace>/
|
|
92
|
+
mkdir -p ~/.local/share/lorekeep/raw/backend
|
|
93
|
+
cp your-docs.md ~/.local/share/lorekeep/raw/backend/
|
|
94
|
+
|
|
95
|
+
# 3. set a provider (edit ~/.config/lorekeep/config.yaml), then compile
|
|
96
|
+
uvx lorekeep compile # raw/*.md -> graph/facts.jsonl
|
|
97
|
+
|
|
98
|
+
# 4. wire a coding agent (writes a portable .mcp.json)
|
|
99
|
+
uvx lorekeep mcp add --agent claude --ns backend
|
|
100
|
+
|
|
101
|
+
# 5. verify
|
|
102
|
+
uvx lorekeep doctor
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Restart Claude Code → the 8 Lorekeep tools are available, scoped to your namespace.
|
|
106
|
+
|
|
107
|
+
## How it works
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
COMPILE (offline, curator) SYNC
|
|
111
|
+
raw/<ns>/*.md ──► ingest ──► extract(LLM) ──► resolve ──► writer ──► facts.jsonl
|
|
112
|
+
│
|
|
113
|
+
┌─────────────────────────────────┘
|
|
114
|
+
▼ (git pull / aws s3 sync)
|
|
115
|
+
SERVE + QUERY (runtime, per device)
|
|
116
|
+
facts.jsonl ──load──► GraphStore (networkx, temporal) ──► ScopedGraph (ns) ──► MCP ──► agent
|
|
117
|
+
▲ │
|
|
118
|
+
└── lazy-reload on mtime change ◄──┘
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Pipeline** (`ingest → extract → resolve → writer`): markdown is chunked with
|
|
122
|
+
provenance; an LLM extracts schema-constrained nodes/edges with temporal +
|
|
123
|
+
namespace tags; aliases collapse to canonical entities; a deterministic writer
|
|
124
|
+
emits sorted, byte-stable `facts.jsonl` + a `manifest.json` (provenance +
|
|
125
|
+
errors + quarantine). Re-compiling unchanged input is byte-identical (per-chunk
|
|
126
|
+
hash cache), so git diffs stay clean.
|
|
127
|
+
|
|
128
|
+
**Serve**: `GraphStore` loads `facts.jsonl` into a networkx graph with temporal
|
|
129
|
+
queries. `ScopedGraph` is the single permission chokepoint — every query is
|
|
130
|
+
filtered through strict visibility rules. The FastMCP server is a thin layer of
|
|
131
|
+
read-only tools over `ScopedGraph`. It lazy-reloads when `facts.jsonl` changes,
|
|
132
|
+
so `compile` is instantly visible without reconnecting.
|
|
133
|
+
|
|
134
|
+
## Concepts
|
|
135
|
+
|
|
136
|
+
**fact** — one line of `facts.jsonl`, a `node` or `edge`:
|
|
137
|
+
```jsonl
|
|
138
|
+
{"kind":"node","id":"svc:payments","type":"service","ns":["backend"],"valid_from":"2024-01-15","valid_to":null,"props":{"lang":"go"},"src":["raw/backend/payments.md:12"]}
|
|
139
|
+
{"kind":"edge","id":"e_depends_on_0001","type":"depends_on","from":"svc:payments","to":"svc:auth","ns":["backend"],"valid_from":"2024-01-15","valid_to":"2025-03-01","props":{},"src":["...:20"]}
|
|
140
|
+
```
|
|
141
|
+
- `ns` — namespace set; `["public"]` is globally visible.
|
|
142
|
+
- `valid_to: null` ⇒ current. History = multiple edges, same endpoints, different windows.
|
|
143
|
+
- `src` — provenance to raw doc line (audit, incremental re-compile, agent citations).
|
|
144
|
+
|
|
145
|
+
**Permission** — effective_ns = allowed ∪ {public}. Node visible iff
|
|
146
|
+
`ns ∩ effective_ns ≠ ∅`. Edge visible iff **both** endpoints visible **and**
|
|
147
|
+
`edge.ns ∩ effective_ns ≠ ∅`. Deny-by-default; an edge never reveals a
|
|
148
|
+
neighbor the caller can't see.
|
|
149
|
+
|
|
150
|
+
**Temporal queries** — `at_time(T)` (snapshot of facts valid at T, half-open
|
|
151
|
+
`[from,to)`), `history(id)` (versions of an entity), `changes(t1,t2)` (edges
|
|
152
|
+
that began/ended in the window).
|
|
153
|
+
|
|
154
|
+
## MCP tools (read-only, scoped)
|
|
155
|
+
|
|
156
|
+
`search` · `get_node` · `neighbors` · `at_time` · `history` · `changes` ·
|
|
157
|
+
`list_namespaces` · `schema`. Every result is filtered to the caller's
|
|
158
|
+
namespace.
|
|
159
|
+
|
|
160
|
+
## Configuration
|
|
161
|
+
|
|
162
|
+
`config.yaml` (resolved by precedence: explicit `LOREKEEP_*` env > `LOREKEEP_HOME` >
|
|
163
|
+
dev marker > XDG):
|
|
164
|
+
```yaml
|
|
165
|
+
provider:
|
|
166
|
+
model: openai/qwen-plus # litellm model string
|
|
167
|
+
api_base: https://dashscope-intl.aliyuncs.com/compatible-mode/v1
|
|
168
|
+
api_key_env: DASHSCOPE_API_KEY # env var name (preferred)
|
|
169
|
+
api_key: null # or inline (gitignored config only)
|
|
170
|
+
ns:
|
|
171
|
+
default: [public]
|
|
172
|
+
install_source: pypi # pypi = portable .mcp.json
|
|
173
|
+
```
|
|
174
|
+
API keys never live in committed files — use `api_key_env` (env) or inline
|
|
175
|
+
`api_key` in the gitignored config only. Examples (DashScope / OpenAI / Ollama)
|
|
176
|
+
in [`.lorekeep/config.yaml.example`](.lorekeep/config.yaml.example).
|
|
177
|
+
|
|
178
|
+
## Data home & dev mode
|
|
179
|
+
|
|
180
|
+
Path resolution (high → low): explicit `LOREKEEP_*` env → `LOREKEEP_HOME` →
|
|
181
|
+
**dev mode** (`.lorekeep/` or `raw/` in CWD; auto-detected in a source checkout)
|
|
182
|
+
→ XDG (`~/.config/lorekeep`, `~/.local/share/lorekeep`). So:
|
|
183
|
+
|
|
184
|
+
- **Installed**: `uvx lorekeep init` bootstraps the XDG home.
|
|
185
|
+
- **Local dev**: from the repo, `uv run lorekeep compile` uses the repo's
|
|
186
|
+
`raw/` + `graph/` (zero migration).
|
|
187
|
+
- **Custom KB**: `LOREKEEP_HOME=~/kb-work uvx lorekeep …`.
|
|
188
|
+
|
|
189
|
+
See [`docs/compile.md`](docs/compile.md) and [`docs/serve.md`](docs/serve.md).
|
|
190
|
+
|
|
191
|
+
## Evaluation
|
|
192
|
+
|
|
193
|
+
Tier-1 (CI): extraction P/R/F1 vs a gold corpus, entity-resolution pairwise F1,
|
|
194
|
+
graph-structure metrics, determinism. Run: `uvx lorekeep eval`. The north star is
|
|
195
|
+
*systematic thinking with complete information* — memory-recall benchmarks
|
|
196
|
+
(LoCoMo, LongMemEval) are parity checks, not the optimization target. See the
|
|
197
|
+
[design spec](docs/superpowers/specs/2026-06-14-lorekeep-temporal-kg-mcp-design.md) §16.
|
|
198
|
+
|
|
199
|
+
## Project layout
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
src/lorekeep/
|
|
203
|
+
models.py shared contract (Node/Edge/Schema/Manifest)
|
|
204
|
+
facts_io.py facts.jsonl loader (store + eval)
|
|
205
|
+
paths.py 4-tier path resolution (env/home/dev/XDG)
|
|
206
|
+
defaults.py default schema + config (for `init`)
|
|
207
|
+
config.py, schema_io.py
|
|
208
|
+
compile/{ingest,extract,resolve,writer}.py the compile pipeline
|
|
209
|
+
compile/providers.py LLMProvider (Fake/LiteLLM)
|
|
210
|
+
store/{graph,fts}.py GraphStore + optional FTS cache
|
|
211
|
+
perm/ns.py ScopedGraph permission chokepoint
|
|
212
|
+
mcp_server.py FastMCP + 8 read tools (lazy-reload)
|
|
213
|
+
integrations/{claude_code,cursor,codex,common}.py
|
|
214
|
+
pipeline.py, cli.py
|
|
215
|
+
eval/{gold,construction,retrieval}.py
|
|
216
|
+
tests/ ~106 tests
|
|
217
|
+
docs/ compile.md, serve.md, specs/, plans/
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Status
|
|
221
|
+
|
|
222
|
+
**v1** — compile pipeline + serve (store/permission/MCP/integrations) + data-home
|
|
223
|
+
+ dev mode + lazy-reload, all merged to `main`, 114 tests green. Published to
|
|
224
|
+
PyPI as `lorekeep`.
|
|
225
|
+
|
|
226
|
+
Roadmap (phase 2+): streamable-HTTP team server, OIDC/SSO,
|
|
227
|
+
embeddings/hybrid search, `wiki.md` views, full Tier-2 benchmark datasets
|
|
228
|
+
(HotpotQA/CronQuestions) and the bespoke Tier-3 Lorekeep-Reason eval.
|
|
229
|
+
|
|
230
|
+
## Documentation
|
|
231
|
+
|
|
232
|
+
- [Compile quickstart](docs/compile.md)
|
|
233
|
+
- [Serve to coding agents](docs/serve.md)
|
|
234
|
+
- [Design spec (architecture, permission, temporal, eval)](docs/superpowers/specs/2026-06-14-lorekeep-temporal-kg-mcp-design.md)
|
|
235
|
+
- Implementation plans: [A compile](docs/superpowers/plans/2026-06-14-lorekeep-plan-a-compile-pipeline.md),
|
|
236
|
+
[B serve](docs/superpowers/plans/2026-06-14-lorekeep-plan-b-serve-mcp.md),
|
|
237
|
+
[C data-home](docs/superpowers/plans/2026-06-15-lorekeep-plan-c-data-home-dev-mode.md)
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
Lorekeep is released under the **MIT License** — see [`LICENSE`](LICENSE).
|
|
242
|
+
|
|
243
|
+
Copyright © 2026 Manh Pham. You're free to use, copy, modify, merge, publish,
|
|
244
|
+
distribute, sublicense, and/or sell copies of the software, provided the
|
|
245
|
+
copyright and permission notice are included in all copies. The software is
|
|
246
|
+
provided "as is", without warranty of any kind.
|
lorekeep-0.1.0/README.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Lorekeep
|
|
2
|
+
|
|
3
|
+
**A temporal knowledge graph for AI agents, served read-only over MCP.**
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Lorekeep compiles a team's raw documentation into a versioned, time-aware
|
|
8
|
+
knowledge graph (`facts.jsonl`) and exposes it to coding agents (Claude Code,
|
|
9
|
+
Cursor, Codex) through the Model Context Protocol — with per-namespace
|
|
10
|
+
permission and zero servers to run.
|
|
11
|
+
|
|
12
|
+
It applies Andrej Karpathy's "LLM Knowledge Base" idea: raw docs are the
|
|
13
|
+
**source code**, the compiled graph is the **executable**. Knowledge is
|
|
14
|
+
processed once at compile time, not re-RAG'd on every query.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Why
|
|
19
|
+
|
|
20
|
+
Existing tools each miss part of what a team needs:
|
|
21
|
+
|
|
22
|
+
| | file-based | temporal KG | compile step | team permission | MCP |
|
|
23
|
+
|---|---|---|---|---|---|
|
|
24
|
+
| Obsidian + MCP | ✅ | ❌ | ❌ | ❌ | ✅ |
|
|
25
|
+
| mcp-knowledge-graph | ✅ | ❌ | ❌ | ❌ (local) | ✅ |
|
|
26
|
+
| mem0 / cognee | ❌ (DB) | partial | ❌ | partial (DB) | ✅ |
|
|
27
|
+
|
|
28
|
+
Lorekeep targets the gap: **strictly file-based + temporal graph + compile-once +
|
|
29
|
+
namespace-scoped permission + MCP** — for team-level (not just single-user)
|
|
30
|
+
knowledge.
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- **Compile-only** — a curator (human + LLM) builds the graph; agents only read.
|
|
35
|
+
No write path, no concurrency hell, deterministic output.
|
|
36
|
+
- **File-sovereign** — `facts.jsonl` (one fact per line, sorted) is the single
|
|
37
|
+
source of truth and the sync unit (git or S3). No binary store committed.
|
|
38
|
+
- **Temporal** — every fact carries `valid_from`/`valid_to` (half-open
|
|
39
|
+
`[from, to)`); query "what was true at *T*", history, diffs.
|
|
40
|
+
- **Namespace permission** — facts are tagged `ns` from the directory tree
|
|
41
|
+
(`raw/<ns>/`); agents scoped to namespaces; cross-namespace edges
|
|
42
|
+
hidden unless both endpoints are visible. Deny-by-default.
|
|
43
|
+
- **MCP, stdio-first** — `lorekeep serve` exposes 8 read-only tools; `lorekeep mcp add`
|
|
44
|
+
wires Claude Code / Cursor / Codex. No server process to babysit.
|
|
45
|
+
- **Lazy-reload** — `lorekeep compile` updates the graph; the MCP server
|
|
46
|
+
auto-refreshes on the next query. Connect once, use forever.
|
|
47
|
+
- **Provider-pluggable extraction** — litellm (OpenAI / Anthropic /
|
|
48
|
+
DashScope/Qwen / Ollama). Strict-privacy → Ollama, fully local.
|
|
49
|
+
- **Tier-1 eval** — extraction P/R/F1 vs a gold corpus, entity-resolution F1,
|
|
50
|
+
graph-structure metrics, determinism property tests.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# from PyPI:
|
|
56
|
+
uvx lorekeep init # try it without installing
|
|
57
|
+
|
|
58
|
+
# or from a clone:
|
|
59
|
+
git clone https://github.com/manhhailua/lorekeep && cd lorekeep
|
|
60
|
+
uv tool install . # installs the `lorekeep` command
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quickstart
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# 1. bootstrap a data home (~/.config/lorekeep + ~/.local/share/lorekeep)
|
|
67
|
+
uvx lorekeep init
|
|
68
|
+
|
|
69
|
+
# 2. add docs under the data home's raw/<namespace>/
|
|
70
|
+
mkdir -p ~/.local/share/lorekeep/raw/backend
|
|
71
|
+
cp your-docs.md ~/.local/share/lorekeep/raw/backend/
|
|
72
|
+
|
|
73
|
+
# 3. set a provider (edit ~/.config/lorekeep/config.yaml), then compile
|
|
74
|
+
uvx lorekeep compile # raw/*.md -> graph/facts.jsonl
|
|
75
|
+
|
|
76
|
+
# 4. wire a coding agent (writes a portable .mcp.json)
|
|
77
|
+
uvx lorekeep mcp add --agent claude --ns backend
|
|
78
|
+
|
|
79
|
+
# 5. verify
|
|
80
|
+
uvx lorekeep doctor
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Restart Claude Code → the 8 Lorekeep tools are available, scoped to your namespace.
|
|
84
|
+
|
|
85
|
+
## How it works
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
COMPILE (offline, curator) SYNC
|
|
89
|
+
raw/<ns>/*.md ──► ingest ──► extract(LLM) ──► resolve ──► writer ──► facts.jsonl
|
|
90
|
+
│
|
|
91
|
+
┌─────────────────────────────────┘
|
|
92
|
+
▼ (git pull / aws s3 sync)
|
|
93
|
+
SERVE + QUERY (runtime, per device)
|
|
94
|
+
facts.jsonl ──load──► GraphStore (networkx, temporal) ──► ScopedGraph (ns) ──► MCP ──► agent
|
|
95
|
+
▲ │
|
|
96
|
+
└── lazy-reload on mtime change ◄──┘
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Pipeline** (`ingest → extract → resolve → writer`): markdown is chunked with
|
|
100
|
+
provenance; an LLM extracts schema-constrained nodes/edges with temporal +
|
|
101
|
+
namespace tags; aliases collapse to canonical entities; a deterministic writer
|
|
102
|
+
emits sorted, byte-stable `facts.jsonl` + a `manifest.json` (provenance +
|
|
103
|
+
errors + quarantine). Re-compiling unchanged input is byte-identical (per-chunk
|
|
104
|
+
hash cache), so git diffs stay clean.
|
|
105
|
+
|
|
106
|
+
**Serve**: `GraphStore` loads `facts.jsonl` into a networkx graph with temporal
|
|
107
|
+
queries. `ScopedGraph` is the single permission chokepoint — every query is
|
|
108
|
+
filtered through strict visibility rules. The FastMCP server is a thin layer of
|
|
109
|
+
read-only tools over `ScopedGraph`. It lazy-reloads when `facts.jsonl` changes,
|
|
110
|
+
so `compile` is instantly visible without reconnecting.
|
|
111
|
+
|
|
112
|
+
## Concepts
|
|
113
|
+
|
|
114
|
+
**fact** — one line of `facts.jsonl`, a `node` or `edge`:
|
|
115
|
+
```jsonl
|
|
116
|
+
{"kind":"node","id":"svc:payments","type":"service","ns":["backend"],"valid_from":"2024-01-15","valid_to":null,"props":{"lang":"go"},"src":["raw/backend/payments.md:12"]}
|
|
117
|
+
{"kind":"edge","id":"e_depends_on_0001","type":"depends_on","from":"svc:payments","to":"svc:auth","ns":["backend"],"valid_from":"2024-01-15","valid_to":"2025-03-01","props":{},"src":["...:20"]}
|
|
118
|
+
```
|
|
119
|
+
- `ns` — namespace set; `["public"]` is globally visible.
|
|
120
|
+
- `valid_to: null` ⇒ current. History = multiple edges, same endpoints, different windows.
|
|
121
|
+
- `src` — provenance to raw doc line (audit, incremental re-compile, agent citations).
|
|
122
|
+
|
|
123
|
+
**Permission** — effective_ns = allowed ∪ {public}. Node visible iff
|
|
124
|
+
`ns ∩ effective_ns ≠ ∅`. Edge visible iff **both** endpoints visible **and**
|
|
125
|
+
`edge.ns ∩ effective_ns ≠ ∅`. Deny-by-default; an edge never reveals a
|
|
126
|
+
neighbor the caller can't see.
|
|
127
|
+
|
|
128
|
+
**Temporal queries** — `at_time(T)` (snapshot of facts valid at T, half-open
|
|
129
|
+
`[from,to)`), `history(id)` (versions of an entity), `changes(t1,t2)` (edges
|
|
130
|
+
that began/ended in the window).
|
|
131
|
+
|
|
132
|
+
## MCP tools (read-only, scoped)
|
|
133
|
+
|
|
134
|
+
`search` · `get_node` · `neighbors` · `at_time` · `history` · `changes` ·
|
|
135
|
+
`list_namespaces` · `schema`. Every result is filtered to the caller's
|
|
136
|
+
namespace.
|
|
137
|
+
|
|
138
|
+
## Configuration
|
|
139
|
+
|
|
140
|
+
`config.yaml` (resolved by precedence: explicit `LOREKEEP_*` env > `LOREKEEP_HOME` >
|
|
141
|
+
dev marker > XDG):
|
|
142
|
+
```yaml
|
|
143
|
+
provider:
|
|
144
|
+
model: openai/qwen-plus # litellm model string
|
|
145
|
+
api_base: https://dashscope-intl.aliyuncs.com/compatible-mode/v1
|
|
146
|
+
api_key_env: DASHSCOPE_API_KEY # env var name (preferred)
|
|
147
|
+
api_key: null # or inline (gitignored config only)
|
|
148
|
+
ns:
|
|
149
|
+
default: [public]
|
|
150
|
+
install_source: pypi # pypi = portable .mcp.json
|
|
151
|
+
```
|
|
152
|
+
API keys never live in committed files — use `api_key_env` (env) or inline
|
|
153
|
+
`api_key` in the gitignored config only. Examples (DashScope / OpenAI / Ollama)
|
|
154
|
+
in [`.lorekeep/config.yaml.example`](.lorekeep/config.yaml.example).
|
|
155
|
+
|
|
156
|
+
## Data home & dev mode
|
|
157
|
+
|
|
158
|
+
Path resolution (high → low): explicit `LOREKEEP_*` env → `LOREKEEP_HOME` →
|
|
159
|
+
**dev mode** (`.lorekeep/` or `raw/` in CWD; auto-detected in a source checkout)
|
|
160
|
+
→ XDG (`~/.config/lorekeep`, `~/.local/share/lorekeep`). So:
|
|
161
|
+
|
|
162
|
+
- **Installed**: `uvx lorekeep init` bootstraps the XDG home.
|
|
163
|
+
- **Local dev**: from the repo, `uv run lorekeep compile` uses the repo's
|
|
164
|
+
`raw/` + `graph/` (zero migration).
|
|
165
|
+
- **Custom KB**: `LOREKEEP_HOME=~/kb-work uvx lorekeep …`.
|
|
166
|
+
|
|
167
|
+
See [`docs/compile.md`](docs/compile.md) and [`docs/serve.md`](docs/serve.md).
|
|
168
|
+
|
|
169
|
+
## Evaluation
|
|
170
|
+
|
|
171
|
+
Tier-1 (CI): extraction P/R/F1 vs a gold corpus, entity-resolution pairwise F1,
|
|
172
|
+
graph-structure metrics, determinism. Run: `uvx lorekeep eval`. The north star is
|
|
173
|
+
*systematic thinking with complete information* — memory-recall benchmarks
|
|
174
|
+
(LoCoMo, LongMemEval) are parity checks, not the optimization target. See the
|
|
175
|
+
[design spec](docs/superpowers/specs/2026-06-14-lorekeep-temporal-kg-mcp-design.md) §16.
|
|
176
|
+
|
|
177
|
+
## Project layout
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
src/lorekeep/
|
|
181
|
+
models.py shared contract (Node/Edge/Schema/Manifest)
|
|
182
|
+
facts_io.py facts.jsonl loader (store + eval)
|
|
183
|
+
paths.py 4-tier path resolution (env/home/dev/XDG)
|
|
184
|
+
defaults.py default schema + config (for `init`)
|
|
185
|
+
config.py, schema_io.py
|
|
186
|
+
compile/{ingest,extract,resolve,writer}.py the compile pipeline
|
|
187
|
+
compile/providers.py LLMProvider (Fake/LiteLLM)
|
|
188
|
+
store/{graph,fts}.py GraphStore + optional FTS cache
|
|
189
|
+
perm/ns.py ScopedGraph permission chokepoint
|
|
190
|
+
mcp_server.py FastMCP + 8 read tools (lazy-reload)
|
|
191
|
+
integrations/{claude_code,cursor,codex,common}.py
|
|
192
|
+
pipeline.py, cli.py
|
|
193
|
+
eval/{gold,construction,retrieval}.py
|
|
194
|
+
tests/ ~106 tests
|
|
195
|
+
docs/ compile.md, serve.md, specs/, plans/
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Status
|
|
199
|
+
|
|
200
|
+
**v1** — compile pipeline + serve (store/permission/MCP/integrations) + data-home
|
|
201
|
+
+ dev mode + lazy-reload, all merged to `main`, 114 tests green. Published to
|
|
202
|
+
PyPI as `lorekeep`.
|
|
203
|
+
|
|
204
|
+
Roadmap (phase 2+): streamable-HTTP team server, OIDC/SSO,
|
|
205
|
+
embeddings/hybrid search, `wiki.md` views, full Tier-2 benchmark datasets
|
|
206
|
+
(HotpotQA/CronQuestions) and the bespoke Tier-3 Lorekeep-Reason eval.
|
|
207
|
+
|
|
208
|
+
## Documentation
|
|
209
|
+
|
|
210
|
+
- [Compile quickstart](docs/compile.md)
|
|
211
|
+
- [Serve to coding agents](docs/serve.md)
|
|
212
|
+
- [Design spec (architecture, permission, temporal, eval)](docs/superpowers/specs/2026-06-14-lorekeep-temporal-kg-mcp-design.md)
|
|
213
|
+
- Implementation plans: [A compile](docs/superpowers/plans/2026-06-14-lorekeep-plan-a-compile-pipeline.md),
|
|
214
|
+
[B serve](docs/superpowers/plans/2026-06-14-lorekeep-plan-b-serve-mcp.md),
|
|
215
|
+
[C data-home](docs/superpowers/plans/2026-06-15-lorekeep-plan-c-data-home-dev-mode.md)
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
Lorekeep is released under the **MIT License** — see [`LICENSE`](LICENSE).
|
|
220
|
+
|
|
221
|
+
Copyright © 2026 Manh Pham. You're free to use, copy, modify, merge, publish,
|
|
222
|
+
distribute, sublicense, and/or sell copies of the software, provided the
|
|
223
|
+
copyright and permission notice are included in all copies. The software is
|
|
224
|
+
provided "as is", without warranty of any kind.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Security policy — Lorekeep
|
|
2
|
+
|
|
3
|
+
Lorekeep compiles team documents into a temporal knowledge graph and serves it
|
|
4
|
+
read-only to AI coding agents over MCP. This document describes the threat
|
|
5
|
+
model and the configuration decisions that keep a deployment safe.
|
|
6
|
+
|
|
7
|
+
## Trust model
|
|
8
|
+
|
|
9
|
+
- **Compile-only, single writer.** The graph (`graph/facts.jsonl`) is produced by
|
|
10
|
+
`lorekeep compile` and never mutated by the server. Agents read via MCP; there is
|
|
11
|
+
no write API. No concurrency control is needed because readers are read-only.
|
|
12
|
+
- **Per-process namespace scope.** An MCP server's visible data is fixed at startup
|
|
13
|
+
by `LOREKEEP_NS` (comma-separated namespaces). Visibility is enforced by a single
|
|
14
|
+
chokepoint, `ScopedGraph` (`src/lorekeep/perm/ns.py`), applied to **every** query.
|
|
15
|
+
- **Deny-by-default.** `effective_ns = allowed ∪ {public}`. A node is visible iff
|
|
16
|
+
`node.ns ∩ effective_ns ≠ ∅`; an edge is visible iff **both** endpoints are
|
|
17
|
+
visible **and** `edge.ns ∩ effective_ns ≠ ∅`.
|
|
18
|
+
- **No information oracle.** `get_node` returns the same `"not found or out of
|
|
19
|
+
scope"` whether a node is absent or merely outside scope. `list_namespaces`
|
|
20
|
+
returns only the caller's own `effective_ns` — it does **not** enumerate
|
|
21
|
+
namespace names that exist but are hidden.
|
|
22
|
+
|
|
23
|
+
## Compile-time data egress
|
|
24
|
+
|
|
25
|
+
At compile, **every file under `raw/` is sent to the configured LLM provider**
|
|
26
|
+
for extraction. This is by design (the documents *are* the knowledge graph's
|
|
27
|
+
source), but it has two consequences:
|
|
28
|
+
|
|
29
|
+
1. Treat `raw/` as trusted content. Do not point `LOREKEEP_RAW` at a directory
|
|
30
|
+
that holds secrets.
|
|
31
|
+
2. **Symlink guard.** `compile/ingest.py` skips any file whose resolved target
|
|
32
|
+
escapes `raw_root` and warns on stderr. This prevents a planted symlink
|
|
33
|
+
(`raw/x/leak.md -> ~/.ssh/id_rsa`) from exfiltrating files outside `raw/` to
|
|
34
|
+
the provider. Keep this guard; do not disable it.
|
|
35
|
+
|
|
36
|
+
For team/shared `raw/` directories (a stated target), the trust boundary is
|
|
37
|
+
"anyone who can write to `raw/`". Isolate raw/ per team and compile per team.
|
|
38
|
+
|
|
39
|
+
## API keys
|
|
40
|
+
|
|
41
|
+
- Prefer `api_key_env` (the name of an environment variable) over an inline
|
|
42
|
+
`api_key`. The provider resolves the env var first and only falls back to the
|
|
43
|
+
inline value if the env var is unset.
|
|
44
|
+
- `config.yaml` is gitignored by default (`.lorekeep/*` except the `.example`
|
|
45
|
+
template). **Never commit a real `config.yaml`.** If an inline key is used,
|
|
46
|
+
`lorekeep compile` prints a warning recommending `api_key_env`.
|
|
47
|
+
- Keys are passed only to `litellm.completion` at compile; the server never reads
|
|
48
|
+
or transmits a key.
|
|
49
|
+
|
|
50
|
+
## No remote-code surface
|
|
51
|
+
|
|
52
|
+
- No `subprocess`, shell, `eval`, `exec`, `pickle`, or `marshal`. Config uses
|
|
53
|
+
`yaml.safe_load`; all user-supplied data is parsed as JSON. SQLite FTS uses
|
|
54
|
+
parameterized queries.
|
|
55
|
+
- Filesystem writes are confined to the data home (`raw/`, `graph/`, `.lorekeep/`)
|
|
56
|
+
and, for `lorekeep mcp add`, the agent config file (`.mcp.json` /
|
|
57
|
+
`.cursor/mcp.json` / `config.toml`), which is merged, not clobbered.
|
|
58
|
+
- `facts.jsonl` and `manifest.json` are written atomically (temp file +
|
|
59
|
+
`os.replace`), so a concurrent read during compile never sees a partial file.
|
|
60
|
+
|
|
61
|
+
## Reporting a vulnerability
|
|
62
|
+
|
|
63
|
+
Please open a private security advisory on
|
|
64
|
+
[github.com/manhhailua/lorekeep](https://github.com/manhhailua/lorekeep/security/advisories/new)
|
|
65
|
+
rather than a public issue. Include the affected version, a reproduction, and
|
|
66
|
+
impact. Reports are acknowledged within 7 days. A fix and disclosure are
|
|
67
|
+
coordinated with the reporter.
|
|
68
|
+
|
|
69
|
+
## Residual accepted risks
|
|
70
|
+
|
|
71
|
+
- An inline `api_key` may live in a gitignored `config.yaml`. Owners are
|
|
72
|
+
responsible for keeping that file local.
|
|
73
|
+
- Shared `raw/` directories trust everyone with write access (see egress above).
|
|
74
|
+
- The MCP client (the coding agent) is trusted within its `LOREKEEP_NS` scope: it
|
|
75
|
+
can read everything in scope, which is the intended behavior. Scope assignment
|
|
76
|
+
is an operational responsibility, not enforced by the server.
|