General-purpose permanent memory vault for durable personal and project knowledge.
The vault is built around one rule: markdown is the source of truth; SQLite is only a rebuildable index. Anything placed in raw/ is preserved forever, while recall uses compact memory layers so answers stay short and the system does not become a heavy QMD-style report generator.
This vault is a local-first memory ledger. It keeps raw source material, creates compact citable cards, indexes those cards, and recalls short relevant results.
It is not a raw sync dump. It is also not a vector database pretending to be memory. The permanent state is ordinary files you can inspect in Obsidian, edit carefully, back up, diff, and rebuild from.
raw/ append-only source intake; agents never edit files here
distilled/ compact source cards derived from raw files
memory/
cards/ concise recall cards with durable extraction sections
people/ future person/entity pages
projects/ future project pages
topics/ future topic pages
.memory/
index.sqlite disposable SQLite index rebuilt from markdown
logs/
ingest-log.md append-only operational history
scripts/
memory.py CLI for ingest, recall, lint, reindex, doctor, vacuum
docs/
superpowers/ design and implementation plans
assets/ README diagrams
benchmarks/
qmd/ fixture vault and query set for SQLite-vs-QMD comparison
The durable pipeline is:
raw/<source>.md
-> distilled/<source-id>.md
-> memory/cards/<source-id>.md
-> .memory/index.sqlite
raw/ is the source-of-record. The deterministic ingester extracts explicit durable memory from markdown and text sources without copying the whole raw body into recall cards. It looks for durable facts, preferences, decisions, tasks, people, projects, topics, dates, and links, then writes compact citable cards.
Recognized source patterns include:
Fact: SQLite is a rebuildable index.
Decision: Keep raw files immutable.
Preference: Keep recall answers short.
Task: Review the Alpha plan.
- [ ] Follow up with the project owner by 2026-05-12.
People: Example Owner, Casey Lee
Project: Second Brain
Topic: memory indexing
Date: 2026-05-06
Link: https://example.com/spec
The output cards contain sections such as:
## Decisions
## Preferences
## Tasks
## People
## Projects
## Topics
## Dates
## Links
During ingest, extracted people, projects, and topics also populate compact entity pages:
memory/people/example-owner.md
memory/projects/second-brain.md
memory/topics/memory-indexing.md
These pages are generated indexes over memory cards. They are useful for browsing, but the source truth remains raw/, distilled/, and memory/cards/.
.memory/index.sqlite stores derived metadata and FTS5 rows for distilled/ and memory/. If the database is stale, corrupt, or the schema changes, delete it and run:
python3 scripts/memory.py reindex --rebuildDefault recall searches compact layers only:
Recall does not search raw/ by default. This keeps answers short and prevents raw transcripts, pasted emails, or long notes from flooding agent context.
python3 scripts/memory.py recall "project alpha"Example output shape:
Recall: project alpha
1. Project Alpha Meeting
Path: memory/cards/project-alpha-meeting-a1b2c3d4e5f6.md
Snippet: Project Alpha Meeting ... source: distilled/project-alpha...
Start the web control surface:
./start.pyThe app opens a local Apple-like command center for dashboard status, ingest, triage, recall, index maintenance, and optional QMD benchmarking.
If the default port is already in use, start.py automatically chooses an open local port and prints the actual URL.
Initialize the folder structure and SQLite index:
python3 scripts/memory.py initAdd a source:
mkdir -p raw
$EDITOR raw/example.md
python3 scripts/memory.py ingestFor unstructured sources that do not use explicit labels, enable the optional LLM-assisted distillation adapter:
python3 scripts/memory.py ingest --llm-command "your-llm-json-command"The command receives a prompt on stdin and must write JSON to stdout:
{
"facts": [],
"preferences": [],
"decisions": [],
"tasks": [],
"people": [],
"projects": [],
"topics": [],
"dates": [],
"links": []
}You can also set the command once for CLI and web ingest:
export SECOND_BRAIN_LLM_COMMAND="your-llm-json-command"The adapter is only used when deterministic extraction finds no durable signal, so explicit labelled notes stay deterministic.
List memory cards that still need review:
python3 scripts/memory.py triageRebuild the disposable SQLite index from markdown:
python3 scripts/memory.py reindex --rebuildRecall concise memory:
python3 scripts/memory.py recall "what did I decide about sqlite"Run an optional QMD benchmark search over memory/ and distilled/:
python3 scripts/memory.py qmd-bench "sqlite disposable index"If QMD is not installed, this command exits successfully and reports that the optional benchmark was skipped.
Run the fixture comparison benchmark:
python3 scripts/memory.py qmd-fixture-benchThis loads benchmarks/qmd/fixtures/raw/ into an isolated derived vault at .memory/qmd-fixture-vault/, runs the fixture queries in benchmarks/qmd/queries.json, and reports whether SQLite FTS and QMD hit the expected memory paths. If QMD is not installed, SQLite still runs and QMD is marked SKIP.
Check for rot:
python3 scripts/memory.py lintCheck SQLite health:
python3 scripts/memory.py doctorCompact SQLite:
python3 scripts/memory.py vacuumRun tests:
python3 -m unittest tests/test_memory.py -vThe Makefile wraps common commands:
make test
make web-test
make start
make memory-init
make ingest
make triage
make reindex
make recall Q="project alpha"
make qmd-bench Q="sqlite disposable index"
make qmd-fixture-bench
make lint
make doctor
make vacuumraw/is append-only source memory.- Every raw file should have a matching distilled card.
- Every distilled card should cite an existing raw path and source hash.
- Recall should return compact paths and snippets, not long reports.
- SQLite rows must match current markdown file hashes.
- If SQLite is wrong, rebuild it from markdown.
- QMD, Qdrant, or embeddings may be added later as replaceable indexes, not as permanent memory.
QMD is useful as a local markdown search accelerator. Qdrant is useful later if the vault needs a stronger vector/hybrid retrieval engine. Neither should become the canonical memory store.
The canonical memory store is this:
raw/ + distilled/ + memory/ + logs/
Indexes are allowed to fail. Markdown memory should survive.
The optional QMD adapter uses these collections:
second-brain-memory -> memory/
second-brain-distilled -> distilled/
It intentionally excludes raw/.
The fixture benchmark lives in:
benchmarks/qmd/
fixtures/raw/ small labelled sources
queries.json query ids, text, and expected result path fragments
Use it when changing extraction, indexing, or search behavior:
make qmd-fixture-benchThe local web app is intentionally small:
./start.py
-> scripts/memory_web.py
-> web/index.html
-> web/styles.css
-> web/app.js
./start.py --host 127.0.0.1 --port 8765 can be used to request a specific bind address. The server still falls back to a free port when the requested port is busy.
The dashboard exposes the controls that matter for maintenance:
- Dashboard: raw, distilled, memory, triage, SQLite, QMD, and latest ingest.
- Ingest: run deterministic extraction after placing files in
raw/; ifSECOND_BRAIN_LLM_COMMANDis configured, unstructured sources can use LLM-assisted distillation. - Triage: list cards marked
needs-human-or-llm-distillation. - Recall: search compact layers without showing raw bodies.
- Indexes: reindex, lint, doctor, vacuum, and optional QMD benchmark.
- Settings: path and policy reminders.
The UI uses the local DESIGN.md Apple guidance installed with:
npx getdesign@latest add apple- It indexes markdown and text raw files only.
- The LLM adapter expects a local command that returns JSON; this repo does not bundle an LLM provider.
- Embeddings are intentionally not included in the first version.
- Add provider-specific examples for
SECOND_BRAIN_LLM_COMMAND. - Add richer merge rules for manually edited people, project, and topic pages.
- Expand QMD fixture queries as the vault grows.
- Add optional vector adapter only after the markdown/SQLite baseline is reliable.