# The index `CACHE_DIR/index/projects.mdb`, LMDB, entirely optional. ## Honest sizing At twelve projects this saves **50 to 150 ms** per load. It is not a speed fix today and is not presented as one. What it buys: - **`--since`** — an agent asking what changed is a range read, not a diff of two full scans. - **recency as a range read**, for when the tree is 500 projects. The key is `[MAX - mtimeMs, id]`, so ascending *is* newest-first. - **decision counts without running the reducer** — the cost that grows fastest, being the only part of a project read that touches megabytes of cue files. ## The rule > **If a value exists only in the index, that is a bug.** It is in the code, because it is what keeps `lib/browse.ts`'s "No database. The filesystem is the model." true. **FS first, index after, best effort**, in a swallowed try/catch. A crash between the two leaves a signature that no longer matches, which the next read repairs. A stale index self-heals and the user sees nothing but latency. Index-**first** could claim something the filesystem does not say. That is the one failure this refuses. ## Freshness signs INPUTS `sha1(schema + kind signature + mtimes + sizes)`. Never the produced record, which would be circular; never bytes, which the export build already learned about. The schema folds into every signature so a bump invalidates everything — deliberately **not** a generation counter, which would invalidate every project whenever any one of them changed. Every read verifies. A record whose signature no longer matches is discarded, not migrated. ## Degradation A missing or unopenable store returns a **no-op** whose `get()` is `null` and whose `put()` does nothing — copied in posture from `common/lib/channelSignature.ts`. A fresh checkout, a deleted cache and a machine without the native module all take that path, and everything still works. ## Observing it It is deliberately almost invisible, so its health has to be surfaced on purpose: - the footer note on `/browse` — `index: 9/11 fresh` - the `x-index` header on `/api/browse/projects` - `umtool index` — records, schema, when it was built - `umtool index --rebuild` / `--prune` / `--since ` Three specs hold it to its contract: the header reports what it served, deleting the `.mdb` produces byte-identical page data, and a manifest edited behind its back is re-read rather than served stale. ## Discovered by getting it wrong once **`lmdb` has to be a direct dependency of umtool.** The plan said to import it through `common`, which already depends on it — but under pnpm's strict resolution it does not resolve from umtool at all, and the CLI (plain node, no bundler) cannot import a bridge written in TypeScript. pnpm dedupes it to the same store entry anyway. **`lmdb` has to be in `serverExternalPackages`.** Bundled, Turbopack tries to resolve its `moduleRequire('cbor-x')` — an *optional* dependency it only reaches for an encoding nothing here uses — and fails the whole module graph. Every page importing `lib/projects` then 500s naming a package that is not involved. Caught by e2e, not by `tsc` and not by a build run before the wiring.