Platform  ·  2026

StudyVault

A local-first study OS for CFA, LSAT, quant and Excel — retrieval practice, FSRS scheduling, and answers grounded in your own curriculum.

4 Exam domains
2994 Tests
3 Sidecars
264 LSAT API routes

4 Exam domains

Platform

2026

Key decisions

  • The default interaction is retrieval practice, producing an answer from memory before anything is revealed, because the act of retrieving is what consolidates.
  • Scheduling runs FSRS-4.5 through ts-fsrs, and an optimizer refits the weights on the user's own review history, because the interval should be a consequence of measured retention rather than a fixed calendar.
  • Progress reports what you can still retrieve after a delay, so the number goes down when your retention goes down.
  • Everything runs on the user's machine — sidecars bind 127.0.0.1 and a build-time gate fails on non-loopback egress — because the ingested curriculum is licensed for candidate use only.

Built against a real exam

The CFA curriculum is large, hierarchical and unforgiving about the gap between having read something and being able to produce it under time pressure. That makes it a good forcing function for a study tool: anything that only creates a feeling of progress gets caught by the exam.

The same forcing function now applies across four exam domains in one window — CFA with authored content packs per level (src/domains/cfa), quant, Excel, and LSAT, a full second prep app with its own FastAPI + SQLite backend serving the question bank, timed sections, drills and blind review. Each domain keeps its own router and design system; what they share is the study engine underneath.

Retrieval, not review

Re-reading produces fluency, and fluency feels exactly like knowledge while you are looking at the page. StudyVault is built on retrieval practice — the default interaction produces an answer from memory before revealing anything, because the act of retrieving is what consolidates.

Scheduling is spaced and driven by performance. src/lib/scheduler.ts runs FSRS-4.5 via the ts-fsrs reference implementation, mapping a confidence-graded answer onto FSRS ratings — a miss becomes Again, correct-with-high-confidence becomes Easy — and src/lib/fsrsOptimizer.ts refits a principled subset of the weights by coordinate descent against the user’s own questionResults history, persisting the personalised parameters so the interval is a consequence of measured retention, not a fixed calendar. The LSAT sidecar shares the same scheduling model over /api/srs/params, so a review in either domain schedules the same way.

On top of the queue sits the Study Director (src/lib/studyDirector.ts): a pure rankStudyActions over due reviews, weak-topic mastery and the 14-day forecast, wrapped by buildStudyPlan, which also interleaves a fraction of slots with non-weakest topics — the desirable-difficulty move that keeps one bad topic from becoming the whole plan.

Progress that is not a lie

The metric a study app chooses determines the behaviour it produces. Percentage of material covered rewards moving fast and forgetting; StudyVault reports what you can still retrieve after a delay, so the number goes down when your retention goes down. Less pleasant, considerably more useful in the last month before a sitting. The analytics route plots the 14-day review-load forecast and 30-day mastery-over-time straight from the review log — both are derived from what you actually produced, not what you opened.

Generated practice is held to the same bar as authored content. Mock exams are built per level from a declared topic blueprint, and every generated or imported question is routed through the LSAT backend’s /api/gen/generation-quality rubric — trap metadata, distractor quality, single defensible answer — so one quality gate spans both domains.

The machine underneath

One Vite bundle carries both apps: the host at / and LSAT at /lsat, split into lazy chunks so neither domain pays for the other’s code, with soft navigation between them. The Electron 43 shell runs a sandboxed renderer behind a typed preload bridge and an app://studyvault protocol, and its main-process supervisor launches three PyInstaller-packaged sidecars — SurrealDB on :8000, the open-notebook API and worker on :5055 for notebook RAG, and the LSAT backend on :8100 — with provenance checks, bounded-backoff restarts and an owned-child watchdog that reaps them if the app disappears.

Host data lives in Dexie / IndexedDB behind a storage abstraction whose SurrealDB driver is the cutover target. Grounded answers go through src/lib/localRag.ts — BM25 plus cosine over local chunks, the union of curriculum and notebook sources, degrading to host-only when the notebook sidecar is down — and come back with numbered citation chips. All model inference is local, against LM Studio or Ollama over the OpenAI-compatible API.

The no-cloud invariant is enforced, not promised: scripts/check-no-egress.mjs statically scans the shipped renderer, Electron host and LSAT backend for non-loopback network egress outside a documented allowlist and fails the build on a hit — because the ingested curriculum PDFs are licensed for candidate use only, and “works on a plane” is a property of the build, not of the marketing.