Cortex
The filesystem is the database, so there is nothing to import and nothing to lose.
Sole developer ยท Apr 2026 โ Present
Every personal dashboard I had tried wanted me to import my notes into it, and then the notes lived in two places. Cortex never owns the data โ it parses the markdown that is already there and writes back into the original files, under an allowlist of eight paths it is permitted to touch. That constraint is the reason the feature count got as far as it did: once the blast radius was written down, everything after it was cheap to add.
The filesystem is the database
A scanner walks the projects directory looking for a state file in a fixed priority order, starting with agent_state.md and falling back to README.md, and pulls status, last-modified date and next steps out of whichever it finds first. Anything untouched for 14 or 30 days gets flagged as stale. A second pass reads every markdown file across the projects tree and the notes vault for unchecked boxes and TODO, FIXME and HACK markers, and groups what it finds by project. Nothing is imported. There is no schema, no sync step and no migration, because the parse happens on read and the files stay where they were. Which is also why the surface count got large: 23 Express route modules sit on one substrate, and adding a feature means writing a parser and a route rather than a table and a backfill. The frontend is 28 React components and 16 hooks against that API, with Recharts for the time-series views and D3 for the link graph between notes.
Deciding what it may write
Ticking a checkbox in the UI edits the original file in place rather than a copy, and that is the part worth being careful about. A parser bug in a read-only tool shows you wrong data on a screen. The same bug in a tool with write access rewrites files that have no other copy. So the app is read-only by default and the writes are enumerated: eight paths, written into the README before most of the features that needed them existed. Append-only to the capture file and the reading log, review-log updates, weekly-review generation, add-node-only on Obsidian canvas files, in-place checkbox toggling, deadline add and remove, and OAuth token storage. Everything outside that list is read-only. A later hardening pass added path containment on top of the allowlist, so a filename that tries to walk out of an allowed root is rejected before any I/O, and it sanitizes URLs coming out of the AI layer rather than trusting model output to be a safe href.
Local AI that stays local
The AI features are optional and off unless configured. Per-project summaries and question answering over the notes run through Ollama on the same machine against an embedding index. Running offline is the only reason I was willing to index everything instead of a curated subset, and the tradeoffs below are all downstream of that. The index is better-sqlite3 with embeddings stored as JSON arrays and cosine similarity computed in plain JavaScript. sqlite-vss would be faster and does not build cleanly on Windows, and a vector extension that fails to compile on the only machine this runs on is worth less than a slower loop that always works. Text is chunked at roughly 500 characters on paragraph boundaries with overlap, and each chunk is keyed by a SHA-256 of its own content against a separate embeddings table, so editing one paragraph of a file reuses the embeddings for every paragraph it did not touch. A chokidar watcher with per-file debouncing re-indexes on change instead of on a timer. The Claude API and Google Calendar are the two things that can send anything outward, and both stay dark until an explicit key is supplied.
The MCP server and the desktop shell
The same aggregated data is exposed over MCP on a stdio transport: 11 tools covering TODOs, deadlines, projects, note search, capture, daily context, wiki search, wiki lint, the reading log, weekly-review generation and per-project detail. That means Claude Desktop calls a scanner that has already parsed the tree, instead of walking the tree again for every question. The scanners are the same modules the Express routes call, so the two surfaces cannot drift apart. The Electron shell is optional and thin. esbuild compiles the main and preload entrypoints, and the window it opens loads the same Vite build a browser would get. What the shell adds is what a browser tab cannot do: a tray icon and a global shortcut that drops a capture overlay over whatever is on screen, dismissed again on blur. electron-builder is configured for NSIS, dmg and AppImage targets.
The scale, and the caveat
80 commits between April and August 2026, sole author. 286 tracked files on the active branch, 66 of them Vitest test files against 44 modules in the server library, plus two Playwright specs for smoke and visual coverage. The caveat is the audience. There is no deployment, no accounts and nobody else using it, and the feature count outran the one user a while ago. Two feature branches are still unmerged, so the default branch does not show the current state either. It is a tool I keep, not a product.