4 November 2026·5 min read
Making a meeting archive answerable, without letting client calls leak into it
Transcripts are the one record of what was actually agreed, and nobody ever reads them. Retrieval fixes that — and immediately creates a confidentiality problem that a policy will not solve.

Requirements get explained in meetings and then quietly lost. The work is done weeks later from a compressed summary, the exact wording is gone, and the disagreement that follows is about what somebody meant rather than what they said. The transcript almost always has the answer in it. Nobody reads transcripts.
This is close to the ideal retrieval problem — a growing archive of unstructured text that people need answers out of rather than a reading list — so I built one: a small Node CLI that ingests Google Meet transcripts, parses them into speaker-and-timestamp turns, stores those turns with embeddings in SQLite, and answers a natural-language question with the exact quotes and the times they were said.
Index the turn, not the meeting
The unit of retrieval decides what you get back. Embed a whole meeting and the nearest match is an hour of conversation that is broadly about the right topic, which is no better than the summary you were trying to escape. Embed the speaker turn and the match is the sentence somebody actually said, with their name and the timestamp attached.
That attribution is most of the value. "The deadline moved" is a claim; "the client said the deadline moved, at 14:02 on the Tuesday call" is evidence, and only the second one ends an argument.
SQLite, not a vector database
An archive of meetings is thousands of turns, not millions of documents. At that size a dedicated vector service is a running cost, a network hop and an outage surface in exchange for a speed difference nobody can perceive. SQLite holds the turns, the embeddings and the metadata in one file you can copy, back up, or delete.
Pick the boring store until the data is big enough to argue otherwise. It rarely is.
The confidentiality problem is the actual engineering
The moment client calls and internal calls land in one searchable index, a general question can return a client’s words. That is not a feature with a bug in it — it is the predictable result of putting both in the same box, and no amount of "remember not to search for that" will hold.
So the separation is a property of the data rather than of anyone’s discipline. Each recording is classified at capture by two independent gates: who actually appears in the transcript, and who was on the invitation. Both have to agree it is internal before it is treated as internal.
- Two signals, because either one alone is wrong often enough to matter — someone invited who never speaks, someone speaking who was never invited.
- It fails closed. An unclear classification is treated as client material, which is the direction you want to be wrong in.
- It happens at capture, not at query time. A filter applied when searching is a filter somebody can forget to apply; a recording that never entered the general index cannot come out of it.
The general shape is worth taking away even if you never build this: when a system can leak, put the boundary in the data at the point of entry. Every boundary enforced at the point of use is one query away from being bypassed.
Why this is a tool and not a product
It is a command-line tool with no interface, which is a deliberate limit rather than an unfinished state. Retrieval over your own archive is useful to the person who owns the archive; turning it into something other people sign into means handling their recordings, their consent and their retention, and that is a different project with a different risk profile.
It also stays honest about what it is for. The answer is quotes and timestamps you can go and verify, not a confident paragraph with no provenance — which is the failure mode of every retrieval system that decides it would rather be a chatbot.
RAG or fine-tuning?Why this was retrieval and not training — facts change, and answers have to be attributable.↗The same boundary argument, in a browserEvery project and coding agent on its own isolated port, so parallel work cannot read across.↗


