- Node.js 18+
- A Neon Postgres database with the
pgvectorextension enabled - An OpenAI-compatible API key (OpenAI, OpenRouter, etc.)
- A Microsoft Entra ID (Azure AD) app registration
Register an app in the Azure portal:
- Go to Microsoft Entra ID > App registrations > New registration
- Set Redirect URI to
http://localhost:5173(Single-page application) - Under API permissions, add delegated permissions:
Sites.Read.AllFiles.Read.All
- Under Certificates & secrets, create a client secret
- Note the Application (client) ID and Directory (tenant) ID
- Create a project at neon.tech.
- In the SQL editor, enable pgvector:
CREATE EXTENSION IF NOT EXISTS vector;
- Copy the pooled connection string (with
?sslmode=require) — this is yourDATABASE_URL.
The schema uses
halfvec(2048)with an HNSW cosine index. If your embedding model produces a different dimension, updateEMBEDDING_DIMinsrc/db/schema.tsand regenerate migrations.
cd backend
cp .env.example .env
# Edit .env with your DATABASE_URL, API key, and Azure credentials
npm install
npm run db:migrate # apply Drizzle migrations
npm run dev| Command | Description |
|---|---|
npm run db:generate |
Generate a new migration from schema changes |
npm run db:migrate |
Apply pending migrations to DATABASE_URL |
npm run db:studio |
Open Drizzle Studio to browse the database |
cd frontend
cp .env.example .env
# Edit .env with your Azure client ID and tenant ID
npm install
npm run devOpen http://localhost:5173 — the Vite dev server proxies /api requests to the backend.
| Variable | Description | Default |
|---|---|---|
OPENAI_API_BASE |
LLM / embedding API base URL | https://openrouter.ai/api/v1 |
OPENAI_API_KEY |
API key for the AI provider | (required) |
CHAT_MODEL |
Chat completion model name | deepseek/deepseek-v4-flash:free |
EMBEDDING_MODEL |
Embedding model name | nvidia/llama-nemotron-embed-vl-1b-v2:free |
CHUNK_SIZE |
Text splitter chunk size (chars) | 1000 |
CHUNK_OVERLAP |
Text splitter overlap (chars) | 200 |
DATABASE_URL |
Neon Postgres connection string | (required) |
AZURE_CLIENT_ID |
Azure app client ID | (required) |
AZURE_TENANT_ID |
Azure tenant ID | (required) |
AZURE_CLIENT_SECRET |
Azure app client secret | (required) |
AZURE_REDIRECT_URI |
OAuth redirect URI | http://localhost:5173 |
| Variable | Description | Default |
|---|---|---|
VITE_AZURE_CLIENT_ID |
Azure app client ID | (required) |
VITE_AZURE_TENANT_ID |
Azure tenant ID | (required) |
VITE_AZURE_REDIRECT_URI |
OAuth redirect URI | http://localhost:5173 |
Documents imported from SharePoint are parsed using these libraries:
| Extension | Parser Used |
|---|---|
.pdf |
pdf-parse |
.txt |
Native fs |
.md |
Native fs |
.docx |
mammoth |
.csv |
csv-parse |
.xlsx |
xlsx |
cd backend
npm run dev # Dev server with hot reload (tsx watch)
npm run build # Compile TypeScript
npm start # Run compiled outputcd frontend
npm run dev # Dev server on :5173
npm run build # Production build → dist/
npm run preview # Preview production build
npm run lint # ESLintVite is configured to proxy /api/* requests to http://localhost:8000:
// vite.config.ts
server: {
proxy: {
"/api": {
target: "http://localhost:8000",
changeOrigin: true,
},
},
}All document metadata, chunk text, and vectors live in Neon Postgres:
| Table | Contents |
|---|---|
resources |
One row per imported document (id, filename, file type, source) |
embeddings |
One row per chunk (content, halfvec(2048) embedding, metadata) |
Foreign key embeddings.resource_id → resources.id uses ON DELETE CASCADE, so deleting a resource removes its chunks. Retrieval uses the HNSW index on embeddings.embedding with halfvec_cosine_ops.
To reset all indexed data:
TRUNCATE TABLE embeddings, resources;Or drop and re-run migrations:
npm run db:migrate- CORS — Currently hardcoded to
http://localhost:5173; update for your domain - Reverse proxy — Use Nginx or similar to serve the frontend and proxy
/apito the backend - Database — Use a production Neon branch (or any Postgres with pgvector ≥ 0.7 for
halfvec); use the pooled connection string for serverless workloads - Rate limiting — Add request rate limiting to prevent abuse
- Logging — Add structured logging for observability
- Azure permissions — Ensure the app registration has admin consent for the required Graph API scopes