Monorepo: server/ (FastAPI) + client/ (React). Upload, ingest, hybrid search, and agentic chat with team auth.
Requires: Docker, Python 3.12+, uv, Node 20+, pnpm, Ollama with embeddinggemma (+ gemma4 for local chat)
Database:
docker compose up -dIf the Postgres volume already existed before auth, apply migrations:
docker compose exec -T postgres psql -U rag_user -d rag_db < server/db/migrations/002_users_sessions.sql
docker compose exec -T postgres psql -U rag_user -d rag_db < server/db/migrations/003_chat_attribution.sql
docker compose exec -T postgres psql -U rag_user -d rag_db < server/db/migrations/004_p0_security.sqlCreate a user (no public registration):
cd server && uv run python -m app.cli create-user --username admin --display-name "Admin" --password 'your-password'Backend (terminal 1):
cd server && uv sync && uv run uvicorn app.main:app --reload --port 8000Frontend (terminal 2):
cd client && pnpm install && pnpm dev- App: http://localhost:5173 — sign in, upload PDF/md/txt, ask questions
- API docs: http://localhost:8000/docs
Preflight (DB + Ollama embeddings):
cd server && uv run python -m app.cli preflightRuns Postgres, Ollama, API, and nginx web UI on port 8080:
docker compose --profile full up -d --buildPull models inside the Ollama container:
docker compose exec ollama ollama pull embeddinggemma
docker compose exec ollama ollama pull gemma4Create the first user against the API container:
docker compose exec api uv run python -m app.cli create-user --username admin --display-name "Admin" --password 'your-password'| Variable | Purpose |
|---|---|
DATABASE_URL |
Postgres connection string |
OLLAMA_BASE_URL |
Local Ollama for embeddings + local chat |
OLLAMA_EMBED_MODEL |
Embedding model (default embeddinggemma:latest) |
OLLAMA_LLM_MODE |
Startup chat mode: local or cloud |
OLLAMA_LOCAL_CHAT_MODEL |
Local chat model (e.g. gemma4) |
OLLAMA_CLOUD_CHAT_MODEL |
Cloud chat model (e.g. gemma4:31b) |
OLLAMA_CLOUD_API_KEY |
Required for cloud chat |
SESSION_SECRET |
Session cookie signing (change in production) |
COOKIE_SECURE |
true when serving over HTTPS |
CORS_ORIGINS |
Comma-separated dev origins (default http://localhost:5173) |
MAX_UPLOAD_BYTES |
Max upload size in bytes (default 25 MB) |
MAX_PDF_PAGES |
Max PDF pages to parse (default 200) |
LOGIN_MAX_ATTEMPTS |
Failed logins before lockout (default 5) |
LOGIN_RATE_WINDOW_SECONDS |
Rate limit window in seconds (default 300) |
Chat LLM mode can be switched at runtime by any signed-in user via Ask → Chat model (team) — one setting per deployment.
Authorization model: Documents and team threads are shared library-wide. Private threads are visible only to their starter. Only the uploader may delete a document; only the thread starter may delete a conversation.
Run security regression tests:
cd server && uv run pytest tests/ -v- Sign in at http://localhost:5173
- Upload a PDF on Library — status should reach ready
- Ask a question on Ask — answers cite library passages
- Unauthenticated API calls return 401