Trust console · Architecture

One retrieval engine, inspectable end to end.

Ingestion chunks and embeds documents into Supabase pgvector; the API embeds your question, matches against the same index, and generates an answer it can cite. This page summarizes docs/architecture.md and docs/SCHEMA.md — the docs are the source of truth.

Ingestion side

scripts/ingest.ts and the examples/* lane runners share one pipeline: read markdown or PDF, normalize and chunk (per-lane sizes and overlaps), embed each chunk with text-embedding-3-small, and upsert keyed on the chunk's HMAC hash. Writes use the Supabase service role; re-running an ingest never duplicates chunks.

Serving side

POST /api/answer runs on the Node.js runtime with dynamic rendering. It reads with the anon key under row-level security — the serving path physically cannot write to the knowledge store.

Request lifecycle

What happens to a question, in order

The order matters: hashing happens before any external call, so no downstream failure can leak a raw query into logs.

  1. Zod validates the request body; the rate limiter (10 req/min/IP) and security headers are enforced in proxy.ts before the route runs.
  2. The prompt-injection guard screens the query (log or block mode).
  3. The query is hashed (salted HMAC-SHA256) — from here on, telemetry only ever sees q_hash and q_len.
  4. OpenAI embeds the query; the embedding is ephemeral and never stored.
  5. match_documents returns the top 5 chunks at ≥ 0.5 cosine similarity.
  6. gpt-4o-mini generates the answer from the retrieved context only.
  7. Citations are built from the matched chunks: source_url, snippet, similarity score.
  8. RagMetrics logs one structured record — latency, vector-search ms, chunk count, tokens, estimated cost — plus a scrubbed Sentry breadcrumb.

Inspect the schema

Inspect the schema

-- supabase/migrations/001_rag_schema.sql
CREATE INDEX idx_rag_docs_embedding
ON rag_docs USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);

SELECT * FROM match_documents('[…]'::vector(1536), 0.5, 5);

Source of truth: docs/architecture.md, docs/SCHEMA.md, supabase/migrations/001_rag_schema.sql · implementation: app/api/answer/route.ts, lib/supabase.ts, lib/openai.ts