All projects AI · Open source

NotebookLM, self-hosted

Most tutorials ship 200 lines that break in front of a real user.

1server, mounted by both a web deployment and a desktop app
18tables, including five embedding tables — one per dimension
13model providers, and your keys never sit in the database in plaintext
01Why it exists

Every “build a NotebookLM clone” tutorial ships a demo that falls over the moment a real document and a real user arrive. I wanted the shipped version — the one with migrations, retries, cancellation and a story for what happens when the model returns nonsense.

It runs two ways from one codebase: hosted, on Neon and S3; or entirely on your machine, with an embedded Postgres, local files and a local text-to-speech model, so it works with no account and no network.

02One server, two shells The only difference between the hosted app and the desktop app is a single adapter.
Next.js on VercelhostedElectron desktoplocal-firstOne Hono appsame routes, both shellsPlatformAdapterdb + storage + authNeon PostgreshostedPGliteembedded, offline

The whole server is one Hono app. Next.js mounts it under a catch-all route; Electron mounts it through the dev server, then as a bundle. Behaviour is identical because the only thing that differs is the adapter holding the database, the storage and the auth — Neon or an embedded Postgres, S3 or the local filesystem.

03How a question is answered Seven steps, and two of them can fail without taking the answer down.
1 · Expand

The question is rewritten into a small set of phrasings. If the model errors, the original question is used and nothing is lost.

2 · Embed and search

Cosine search against the embedding table matching the model’s dimension — five sibling tables exist so changing embedding model is not destructive.

3 · Keyword pass

A case-insensitive match over chunk text, merged with the vector hits. Vector results win collisions.

4 · Threshold and rerank

Weak matches are dropped, then the model scores the survivors and the top slice is kept. Both the expansion and the rerank degrade to the unprocessed list rather than failing.

5 · Answer with markers

Chunks go into the prompt carrying ids, and citations are streamed as the model echoes them, so a claim is linked to its source as it is written.

6 · Deep research

A planner splits the question, searches in parallel, scores and summarises the sources, then writes the report section by section.

7 · Critique, then verify

The agent grades its own report. If it finds gaps and rates itself below eight out of ten, it runs a second bounded round and appends what it found, then lists which claims it could actually support.

Silent fallback on a thrown error is a footgun.

agent/harness.ts, on why one error is deliberately not caught
04The parts that are not the demo

This is the half that tutorials leave out.

  • Keys are encrypted with the user bound inProvider keys are never environment variables and never plaintext in the database. They are encrypted with the user id mixed into the encryption itself, so decrypting one user’s key as another fails rather than succeeding quietly.
  • Changing embedding model is non-destructiveFive sibling tables, one per dimension, and retrieval only reads rows written by the model currently configured.
  • Closing the window does not cancel the workIf the modal closes mid-generation the job keeps running so the result still lands; cancelling is a separate, explicit path.
  • Two-host audio, locally if you wantThe script is generated as speaker turns, then voiced — hosted, or by a local model with no key and no network. Concurrency differs per backend for a written-down reason.
  • Results are cached on content, not countsThe cache key fingerprints the actual chunks, because chunk ids regenerate on re-ingest and a row count would miss a change.
05Honest state

Shipped, documented, and without a test suite.

  • No testsThere is typechecking, linting and formatting, and no test files. That is the largest gap in the project and it is not disguised here.
  • 29 releasesVersioned and changelogged from 0.1.9 to 0.3.0.
  • MITIncluding a 45-page write-up of how it was built.

The interesting work was in the failure paths.

Any of these steps can fail. The question that took the time was which ones are allowed to take the answer down with them, and which should quietly hand back what they had.