A self-hosted notes app with auto-linked knowledge graph and grounded chat over your notes (RAG). React + FastAPI + SQLite + sqlite-vec.
Status: working prototype. Auth is dev-mode only — see Security before exposing this to the internet.
- Markdown notes with autosave, search,
#tagfiltering, edit/preview toggle, word counts. - Auto-linking — every note shows related notes by embedding similarity. No manual
[[wikilinks]]. - Knowledge graph — interactive force-directed view with cluster detection.
- Multi-pane workspace — split into up to four panes (editor / graph / chat). Drag-and-drop notes into panes. Resizable.
- Chat (RAG) — ask questions about a team's notes. Streamed answers with inline numbered citations you can expand to see the grounding chunk.
- Save-as-note — turn a chat answer into a new note with provenance metadata (
source="llm",derived_from=[note_ids]). - Teams — team-scoped collections with viewer / editor / owner roles.
- Ingestion — paste text, upload files (
.txt/.md/.pdf), or just write notes. Re-ingest is idempotent. - 8 themes, keyboard shortcuts, mobile-responsive.
┌──────────────┐ ┌────────────────┐ ┌─────────────────┐
│ React + Vite │ ──► │ FastAPI server │ ──► │ SQLite + vec0 │
│ (Tailwind) │ SSE │ (uvicorn) │ │ (sqlite-vec) │
└──────────────┘ └────────────────┘ └─────────────────┘
│
▼
┌──────────────────┐
│ LLM + Embedder │
│ via LlamaIndex │
└──────────────────┘
- One SQLite file (
data/notes.db) holds notes, teams, members, tags, AND thechunks+chunk_embeddings(vec0) tables. - Pluggable LLM/embedder providers:
azure_openai/openai/anthropic/ollama/huggingface— configured inconfig.toml. - Default chunker is heading-aware (
backend/services/chunking.py); falls back to LlamaIndexSentenceSplittervia config flag. - Optional cross-encoder reranker (off by default; ~3s p50 on CPU).
See PLAN.md for the phased roadmap and ISSUES.md for the open punch list.
Requirements: Node 20+, Python 3.12+, an LLM API key.
# 1. Install deps (creates .venv and installs Python + npm packages)
python -m venv .venv
make install
# 2. Configure
cp .env.example .env
# Edit .env to add AZURE_OPENAI_API_KEY (or another provider's key)
# Edit config.toml if you want to change the LLM/embedder
# 3. Run
make dev # starts vite (5173) + uvicorn (3001) concurrentlyOpen http://localhost:5173. The Vite dev server proxies /api/* to the backend on port 3001.
For production: npm run build && npm run start — the FastAPI app serves the built SPA.
Single config file: config.toml. Sections:
[llm]— provider, model, deployment, base_url, api_version, temperature[embedding]— provider, model, dim (locked at first ingestion)[vector_store]—sqlite_veconly for now[chunking]—strategy(notesorsentence_splitter),chunk_size,chunk_overlap,short_note_token_threshold[retrieval]—top_k, optionalreranker_model,rerank_fetch_k
API keys are read from env (AZURE_OPENAI_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY).
Note: changing
[embedding].dimtriggers a one-time chunk wipe — thevec0table is dim-locked at create time.
make test # full suite (pytest + Playwright)
make test-py # python only
make test-e2e # browser e2e (needs `npx playwright install chromium` once)
make test-eval # run the RAG eval harness on the seed query setCoverage:
- pytest: chunker, retrieval, reranker, ingest API, chat API, sharing/permissions, write-back, eval components.
- Playwright: smoke test runs green; richer specs are noted as drift-prone in
ISSUES.md. - Eval harness (
backend/eval/):recall@k,mrr,ndcg@k,hit@k, plus LLM-as-judge for faithfulness / answer relevance / context relevance. Reports go tobackend/eval/reports/.
See backend/tests/README.md for what is and isn't covered.
- Authentication is dev-mode: the
X-User-Emailrequest header identifies the caller; missing →dev@local. There are no sessions, cookies, OAuth, CSRF protection, or rate limiting. - Sharing tests verify the per-collection vector-store filter (no cross-team leakage), but green tests here do not mean the auth surface is secure.
- Run it locally or behind your own auth proxy. Don't expose it to the open internet without bolting real auth onto
backend/services/users.py:resolve_current_user.
src/ React frontend (components, hooks, themes)
backend/ FastAPI backend
routers/ HTTP endpoints (notes, teams, members, chat, ingest, …)
services/ Domain logic (chat, retrieval, chunking, permissions, …)
models/ Pydantic schemas
vector_store/ sqlite-vec backend
llm/ LlamaIndex factory (LLM + embedder providers)
config/ TOML config loader
eval/ RAG eval harness (metrics, judge, runner, fixtures)
tests/ pytest suite
e2e/ Playwright specs
scripts/ eval CLI shim, synthetic-corpus generator
public/ PWA icons + manifest + service worker
Open an issue first for non-trivial changes. PRs that regress eval metrics need an explicit tradeoff note (see PLAN.md cross-cutting concerns).
MIT — see LICENSE.