← Back to Blog

Day 163: Pinchy 0.9.0

0.9.0 is out. Three weeks since 0.8.0, and the headline is the one thing people have asked for in almost every conversation since I started: let the agent read our documents.

What actually shipped

Knowledge Base agents. You mount document folders, grant specific folders to specific agents, and run an index. Pinchy splits the documents into passages, embeds them, and stores them in its own Postgres — which is why the database image had to change, since the stock postgres:17 doesn’t carry pgvector. Retrieval runs vector similarity and Postgres full-text search over the same passages and fuses the two rankings, so a question asked in different words and a question containing an exact part number both land. Answers cite the document path and page.

Two of the retrieval decisions came from real customer document trees rather than from theory, and they are the ones I would keep if I could keep only two. Archive folders (OLD, Archive, Archiv) are indexed but held out of normal search, because an archived quality binder answered a certification question with certificates that expired in 2013. And no single document may contribute more than three passages to an answer, because one 549-chunk compilation binder filled every slot and pushed a clean eight-page datasheet out of the results entirely.

No hosted embedding API, no managed vector store. Reading your documents needs a second model, and the obvious implementation reaches for a hosted embedding API or a managed vector database — at which point your documents leave the building and “self-hosted” quietly stops being true. In 0.9.0 the embedding runs on the Ollama you already run, with bge-m3, and the vectors go into the same Postgres as everything else. No key, no third party, nothing crossing your network boundary. The honest asterisk is that the model is a one-time ollama pull, so an air-gapped install has to carry it in rather than fetch it. The version with the embedder bundled in the image, where there is nothing to pull at all, is on main and ships in the next release. What did land here is that same bundling for agent memory recall, which on some installs meant memory started working at all.

IMAP. Email was Gmail and Microsoft 365, both of which mean registering an OAuth application before you can read a single message. Now any IMAP mailbox works: host, port, credentials, done.

Audit rows stopped naming people. An append-only log and a right to erasure pull against each other, and the resolution is to keep the personal data out of the immutable part. Rows now carry a random pseudonym; the mapping to the person lives in the mutable user record. The signature covers the pseudonym, so verification is unaffected.

The honest limits

The knowledge base indexes PDFs. Not markdown, not Office documents — those reach an agent the other way, as attachments in a conversation. That is a narrow first version on purpose: a PDF has stable pages, so a citation can point at page 4 and you can go check it.

The erasure story ships the mechanism, not the button. Removing a user today deactivates the account and revokes its sessions; the one-click irreversible erasure that destroys the pseudonym mapping is still ahead. And the substitution only works forward — rows written before this release still carry the raw id, because the log cannot be rewritten retroactively, which is rather the point of it.

The upgrade step you cannot skip

The db service moves from postgres:17 to pgvector/pgvector:pg17-trixie. If you keep your old compose file, the migration fails with extension "vector" is not available and Pinchy restart-loops. Nothing is lost — fetch the new compose file, start again, and the migration resumes where it stopped — but the app is down until you do.

I want to be plain that this is the failure mode I chose. Migrations run on boot, so a failed one takes the whole app down rather than leaving it up with a broken knowledge base and no indication why. Given what this month has been about — an instance that kept serving while its config had silently frozen mid-write — a loud stop beats a quiet half-state. You will know immediately, and the fix is two commands.

Day 163

Three weeks, and the shape of the work was not what I expected going in. I thought the hard part of a knowledge base would be retrieval quality. Retrieval quality was a week. The rest was everything around it: what happens when a folder has 549 chunks in one file, what a citation has to look like to be checkable by a human, whether the index can be built without phoning anyone, and how loudly to fail when the database can’t do the thing the feature needs. The feature is the easy half. Making it honest is the release.

← Day 162: The Connection Test Was a Port Scanner Day 164: Six Weeks of Screenshots That Never Changed →

Pinchy is open source and ready to deploy. Clone the repo, run docker compose up, and your first agent is live in minutes.