# Technology Stack

**Analysis Date:** 2026-06-02

## Languages

**Primary:**
- Python 3.12 — backend API, services, Celery tasks, storage backends
- JavaScript (ES Modules, ES2022+) — Vue 3 frontend SPA

**Secondary:**
- SQL — PostgreSQL schema via Alembic migrations (`backend/migrations/`)
- HTML/CSS — Vue SFC templates, Tailwind utility classes

## Runtime

**Backend:**
- CPython 3.12 (pinned: `FROM python:3.12-slim` in `backend/Dockerfile`)
- ASGI server: Uvicorn `>=0.29` with `[standard]` extras
- Entry point: `backend/main.py` — `uvicorn main:app`

**Frontend:**
- Node.js 20 (pinned: `FROM node:20-alpine` in `frontend/Dockerfile`)
- Dev server: Vite 5 on port 5173, proxies `/api` → `http://backend:8000`
- Entry point: `frontend/index.html` → `frontend/src/main.js`

**Package Manager:**
- Backend: `pip` — `backend/requirements.txt`; no lockfile (floating `>=` ranges used throughout — see CONCERNS.md)
- Frontend: `npm` — lockfile: `frontend/package-lock.json`

## Frameworks

### Backend Core

| Package | Version | Purpose |
|---|---|---|
| `fastapi` | `>=0.111` | Async REST API framework — `backend/main.py` |
| `uvicorn[standard]` | `>=0.29` | ASGI server |
| `pydantic` | `>=2.0` with `[email]` | Request/response validation |
| `pydantic-settings` | `>=2.2` | Environment-based config — `backend/config.py` |
| `python-multipart` | `>=0.0.27` | Multipart file upload parsing |

### ORM / Database

| Package | Version | Purpose |
|---|---|---|
| `sqlalchemy[asyncio]` | `>=2.0.49` | Async ORM — `backend/db/session.py`, `backend/db/models.py` |
| `psycopg[binary]` | `>=3.3.4` | psycopg v3 async PostgreSQL driver |
| `alembic` | `>=1.18.4` | Schema migrations — `backend/migrations/` |
| `aiosqlite` | `>=0.20.0` | SQLite async driver (test isolation only) |

### Background Tasks

| Package | Version | Purpose |
|---|---|---|
| `celery[redis]` | `>=5.5.0` | Async task queue — `backend/celery_app.py` |
| `redis` | `>=4.6.0` | Redis async client; Celery broker + result backend + JTI token store |

### Auth / Security

| Package | Version | Purpose |
|---|---|---|
| `PyJWT` | `>=2.8.0` | JWT access token creation and verification — `backend/services/auth.py` |
| `pwdlib[argon2]` | `>=0.2.1` | Argon2id password hashing |
| `pyotp` | `>=2.9.0` | TOTP provisioning and verification (2FA) |
| `cryptography` | `>=41.0.0` | HKDF per-user key derivation; Fernet encryption for cloud credentials |
| `slowapi` | `>=0.1.9` | Rate limiting middleware on auth endpoints |
| `httpx` | `>=0.27` | Async HTTP client (HIBP k-anonymity checks, OneDrive Graph API) |

### Document Processing

| Package | Version | Purpose |
|---|---|---|
| `PyMuPDF` | `>=1.26.7` | PDF text extraction — `backend/services/extractor.py` |
| `python-docx` | `>=1.1` | DOCX text extraction — `backend/services/extractor.py` |
| `pytesseract` | `>=0.3` | OCR for image files — `backend/services/extractor.py` |
| `Pillow` | `>=10.3` | Image loading for OCR pipeline |
| `aiofiles` | `>=23.2` | Async file I/O |

### AI Classification

| Package | Version | Purpose |
|---|---|---|
| `anthropic` | `>=0.26` | Anthropic Claude SDK — `backend/ai/anthropic_provider.py` |
| `openai` | `>=1.30` | OpenAI SDK; also used as shim for Ollama and LM Studio — `backend/ai/openai_provider.py` |

### Cloud Storage SDKs

| Package | Version | Purpose |
|---|---|---|
| `minio` | `>=7.2.20` | MinIO/S3 object storage SDK — `backend/storage/minio_backend.py` |
| `google-auth-oauthlib` | `>=1.3.1` | Google OAuth2 flow — `backend/storage/google_drive_backend.py` |
| `google-api-python-client` | `>=2.196.0` | Google Drive v3 API — `backend/storage/google_drive_backend.py` |
| `msal` | `>=1.36.0` | Microsoft Auth Library for OneDrive — `backend/storage/onedrive_backend.py` |
| `webdavclient3` | `>=3.14.7` | Generic WebDAV + Nextcloud — `backend/storage/webdav_backend.py` |
| `cachetools` | `>=5.3.0` | Cloud connection caching — `backend/services/cloud_cache.py` |

### Frontend

| Package | Version | Purpose |
|---|---|---|
| `vue` | `^3.4.0` | UI framework (Options API) — `frontend/src/` |
| `vue-router` | `^4.3.0` | Client-side routing — `frontend/src/router/` |
| `pinia` | `^2.1.0` | State management (JWT access token stored in memory only) — `frontend/src/stores/` |
| `qrcode` | `^1.5.4` | TOTP QR code generation for 2FA enrollment UI |
| `tailwindcss` | `^3.4.0` | Utility-first CSS — `frontend/tailwind.config.js` |

### Frontend Dev / Build

| Tool | Version | Purpose |
|---|---|---|
| `vite` | `^5.2.0` | Dev server and bundler — `frontend/vite.config.js` |
| `@vitejs/plugin-vue` | `^5.0.0` | Vue SFC compilation |
| `postcss` | `^8.4.0` | CSS processing — `frontend/postcss.config.js` |
| `autoprefixer` | `^10.4.0` | CSS vendor prefixing |

### Testing

| Tool | Version | Purpose |
|---|---|---|
| `pytest` | `>=8.2` | Backend test runner — `backend/pytest.ini` |
| `pytest-asyncio` | `>=1.3.0` | Async test support (`asyncio_mode = auto`) |
| `vitest` | `^4.1.7` | Frontend test runner — `frontend/vitest.config.js` |
| `@vue/test-utils` | `^2.4.10` | Vue component test utilities |
| `happy-dom` | `^20.9.0` | DOM environment for Vitest |

## Infrastructure

### Docker Compose Services (`docker-compose.yml`)

| Service | Image | Port(s) | Notes |
|---|---|---|---|
| `postgres` | `postgres:17-alpine` | internal | Persistent `postgres_data` volume |
| `minio` | `minio/minio:latest` | `9000`, `9001` | S3-compatible object store; persistent `minio_data` volume |
| `redis` | `redis:7-alpine` | internal | Password-protected; Celery broker + JTI revocation store |
| `backend` | Built from `./backend` | `8000` | Hot-reload via volume mount; depends on postgres, minio, redis |
| `celery-worker` | Built from `./backend` | — | Processes `documents` queue |
| `celery-beat` | Built from `./backend` | — | Periodic task scheduler |
| `frontend` | Built from `./frontend` | `5173` | Vite dev server; proxies `/api` → `backend:8000` |

### Database Role Separation

- `docuvault_app` — DML only (SELECT/INSERT/UPDATE/DELETE); used by FastAPI app
- `docuvault_migrate` — DDL; used by Alembic migrations only
- Init script: `docker/postgres/initdb.d/01-init-users.sql`

### System Dependencies (backend Docker image)

Installed via `apt-get` in `backend/Dockerfile`:
- `tesseract-ocr` — OCR binary for `pytesseract`
- `libgl1`, `libglib2.0-0` — shared libraries required by PyMuPDF

## Configuration

**Environment variables** are the single source of truth, read by `pydantic-settings` in `backend/config.py`.

Required for core operation:
- `DATABASE_URL` — psycopg v3 async DSN for app user
- `DATABASE_MIGRATE_URL` — psycopg v3 DSN for migrate user
- `MINIO_ENDPOINT`, `MINIO_ACCESS_KEY`, `MINIO_SECRET_KEY`, `MINIO_BUCKET`
- `REDIS_URL` — used by both FastAPI (JTI store) and Celery
- `SECRET_KEY` — JWT signing secret
- `CLOUD_CREDS_KEY` — 32-byte master key for HKDF cloud credential encryption

Optional:
- `SMTP_HOST/PORT/USER/PASSWORD/FROM` — transactional email
- `GOOGLE_CLIENT_ID/SECRET`, `ONEDRIVE_CLIENT_ID/SECRET` — OAuth cloud storage
- `ADMIN_EMAIL`, `ADMIN_PASSWORD` — bootstrap admin account
- `SYSTEM_PROMPT`, `DEFAULT_AI_PROVIDER`, `DEFAULT_AI_MODEL` — AI defaults
- `CORS_ORIGINS`, `FRONTEND_URL`, `BACKEND_URL`

## Platform Requirements

**Development:**
- Docker + Docker Compose (preferred), or
- Python 3.12, Node.js 20 plus running PostgreSQL 17, MinIO, Redis instances locally

**Production:**
- Containerised via Docker Compose; no cloud-native manifests or reverse-proxy config detected in repo

---

*Stack analysis: 2026-06-02*
