Moosa Memon
← Index of work

Multi-tenant RAG where the tenant boundary lives in SQL

PerimeterRAG: a retrieval-augmented Q&A backend for multi-tenant SaaS. Fast, source-cited answers over each customer's documents, with access scoping enforced in the database rather than hoped for in application code, and an explicit 'not in your documents' instead of a confident guess.

Type
RAG
Stack
FastAPI / Postgres / pgvector / Docker
Status
Complete; Dockerized with a deployment story and a small frontend
Measured
Cited answer in 663 ms end to end on the demo corpus (embedding 41 ms, retrieval 8 ms, generation 612 ms), with page, section and chunk in every citation

Problem

The usual “chat with your documents” integration works in a demo and starts failing at scale: retrieval misses the relevant chunk, answers drift into the wrong document, latency climbs. And nothing stops one customer’s query from reaching another’s data if a filter is ever forgotten in a code path someone adds later.

System

Every query arrives with a bearer token that resolves to a tenant, and the tenant filter is part of the retrieval query itself, not a parameter a caller could omit. Retrieval returns chunks with document, page and section metadata; generation is constrained to cite them; a found: false path returns “not in your documents” when nothing scores well enough. Responses carry per-stage timings and a cache-hit flag so performance is visible per request, not guessed at in aggregate.

Worth knowing

The tenant boundary being in SQL is the whole design. An application-layer filter is one refactor away from being missing; a database-level scope means a bug can produce a wrong answer but not a leak. That’s the property a SaaS buyer actually needs before they’ll put customer documents into it.

Have a workflow that looks like this?

Most of these start as a messy, manual process someone got tired of. Twenty minutes is usually enough to sketch how I'd approach yours.