May 31, 2026
DOCSAI: how I structured a private PDF RAG app with citations
DOCSAI is a document chat app designed around a simple rule: the assistant should answer from retrieved document context, not from general model memory.
The core flow is intentionally small and auditable:
- Authenticated users upload PDF files.
- The app validates and stores files privately.
- A background worker parses pages with LiteParse.
- A custom chunker prepares citation-ready fragments.
- OpenAI or OpenRouter generates 1536-dimensional embeddings.
- PostgreSQL with pgvector retrieves relevant chunks.
- The chat answers only from retrieved context and cites sources like
[S1].
Why I kept the stack boring
The project does not need LangChain, Redis, S3, or extra microservices for the first version. A focused stack makes it easier to debug ingestion, retrieval, and answer grounding.
The stack is:
- Next.js, TypeScript, Tailwind CSS, and shadcn-style components.
- Vercel AI SDK for streaming chat and provider integration.
- LiteParse for PDF parsing.
- Drizzle ORM with PostgreSQL and pgvector.
- Graphile Worker for background ingestion.
- Better Auth for email/password login.
Security decisions
DOCSAI is not a public anonymous chat. Uploads are private, signup can be closed, and local defaults bind services to 127.0.0.1. For a portfolio demo, the correct approach is a limited demo account with sample documents only.
What this proves
RAG quality is not only about the LLM. The boring parts matter: validation, chunking, background jobs, retrieval shape, citations, and auth boundaries.