Heirloom sealHeirloomBeta
Heirloom seal

Transparency

A contract you can verify, not a promise to believe.

Every question the archive answers, refuses, or scrubs is logged with its diagnostics. The five checks below run between a question and an answer, and any of them can fail the whole reply.

The contract

Five checks. Any one of them can stop the answer.

  1. Retrieval before model.

    The question is matched against the archive's index first. The language model isn't invoked until retrieval has found something to point at. If the archive is empty on a topic, the model is never asked.

  2. A floor, not a hope.

    An answer grounds on either a strong vector match or a literal overlap with the question. Either alone misses too much.

  3. Every citation, checked.

    Each claim in a streamed answer must point at a capture that retrieval actually returned. A citation outside that set fails the entire answer. There is no partial salvage.

  4. No first person.

    Any answer that says I or my outside a quote fails. The system describes what the creator said. It does not become the person who said it.

  5. One refusal sentence.

    When the contract fails, the answer collapses to one line, the same one every time: "I don't have that in the archive. Try asking another way?" No graceful filler. No suggested alternatives the system cannot back up.

How a question becomes an answer

From a sentence to a citation.

  1. 01

    A question arrives

    A nominee asks something on the Reflect surface. The session is scoped so everything downstream can only see what the creator already released to them. Row-level security in the database enforces it.

  2. 02

    Embed locally

    The question is turned into a 768-dim vector by EmbeddingGemma running on the same device through Ollama. Nothing about the question leaves the machine.

  3. 03

    Retrieve

    A vector index in pgvector (or sqlite-vec inside the .app) returns the closest captures — released ones for a nominee, everything for the creator.

  4. 04

    Gate

    The result must clear a similarity floor or share a substantive word with the question. If neither holds, the refusal line is served and the model is never called.

  5. 05

    Synthesise

    A grounded variant of Gemma 4, built from an open Modelfile that strips it of fabrication-friendly defaults, streams an answer that cites the retrieved captures.

  6. 06

    Validate

    Every citation is checked against the retrieved set. Any first-person token outside quotes fails the whole answer. The diagnostics are logged inside the archive.

The whole system, on one page

What runs, where, and why.

Heirloom system architecture: client surfaces on the left, Next.js route handlers in the middle with the /api/reflect endpoint in wax red, sidecars on the right (Ollama with gemma4:e4b and embeddinggemma, whisper-cpp, opt-in LuxTTS, Postgres with pgvector and a SQLite + sqlite-vec mirror), and the five-step grounding contract along the bottom.

What the model never sees

The model gets the words. Never the people.

The retrieval and synthesis prompts include only the capture's text and the date it was captured. No names. No nominee list. No life events. No anchor dates. No biography. The model has the words, never the social graph around them.

Face recognition runs entirely in the browser. The descriptors never leave the device. When a face match later changes, every photo's caption is quietly re-rendered without the wrong name.

Audit it yourself

The whole thing is open under Apache 2.0.

You don't have to take our word for any of this. The contract is short, the code is public, and the design choices behind it are written out plainly.