diff --git a/docs/01-User-Manual.md b/docs/01-User-Manual.md
new file mode 100644
index 0000000..5c6e6df
--- /dev/null
+++ b/docs/01-User-Manual.md
@@ -0,0 +1,492 @@
+---
+pdf_options:
+ format: A4
+ margin: 25mm 20mm
+ headerTemplate: '
Oliver DeckForge — User Manual
'
+ footerTemplate: ' /
'
+ displayHeaderFooter: true
+stylesheet:
+ - https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.5.1/github-markdown.min.css
+body_class: markdown-body
+css: |-
+ body { font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; }
+ .markdown-body { max-width: 100%; }
+ h1 { color: #5146E5; border-bottom: 3px solid #5146E5; padding-bottom: 8px; }
+ h2 { color: #3b3494; border-bottom: 1px solid #E9E8F8; padding-bottom: 6px; margin-top: 2em; }
+ h3 { color: #4a4a4a; }
+ table { width: 100%; }
+ th { background: #5146E5; color: white; }
+ td, th { padding: 8px 12px; }
+ code { background: #f0eff8; color: #5146E5; padding: 2px 6px; border-radius: 4px; }
+ pre code { background: #1e1e2e; color: #cdd6f4; display: block; padding: 16px; border-radius: 8px; }
+ blockquote { border-left: 4px solid #5146E5; background: #f8f7ff; padding: 12px 16px; margin: 16px 0; }
+ .tip { border-left: 4px solid #22c55e; background: #f0fdf4; padding: 12px 16px; margin: 16px 0; border-radius: 0 8px 8px 0; }
+ .warning { border-left: 4px solid #f59e0b; background: #fffbeb; padding: 12px 16px; margin: 16px 0; border-radius: 0 8px 8px 0; }
+ img { max-width: 100%; max-height: 280px; object-fit: contain; display: block; margin: 16px auto; }
+---
+
+# Oliver DeckForge User Manual
+
+**Version 1.0** | Enterprise AI Presentation Generator
+
+---
+
+## Table of Contents
+
+1. [Getting Started](#1-getting-started)
+2. [Dashboard](#2-dashboard)
+3. [Creating a Presentation](#3-creating-a-presentation)
+4. [Working with the Outline](#4-working-with-the-outline)
+5. [Selecting a Template](#5-selecting-a-template)
+6. [Generation Progress](#6-generation-progress)
+7. [Editing Slides](#7-editing-slides)
+8. [Review Workflow](#8-review-workflow)
+9. [Exporting Presentations](#9-exporting-presentations)
+10. [Keyboard Shortcuts & Tips](#10-keyboard-shortcuts--tips)
+
+---
+
+## 1. Getting Started
+
+### 1.1 Logging In
+
+Oliver DeckForge supports two authentication methods:
+
+```mermaid
+flowchart LR
+ A[Open App] --> B{Auth Mode?}
+ B -->|Production| C[Azure AD SSO]
+ B -->|Development| D[Email + Password]
+ C --> E[Microsoft Login]
+ E --> F[Dashboard]
+ D --> F
+```
+
+**Production Mode (Azure AD SSO):**
+Click **"Sign in with Microsoft"** and authenticate with your organization's Microsoft 365 credentials.
+
+**Development Mode:**
+When Azure AD is not configured, a development login form appears. Enter:
+- **Email:** Your email address (e.g., `admin@deckforge.dev`)
+- **Password:** The development password (default: `devpass123`)
+
+> After successful authentication, you are redirected to the Dashboard.
+
+### 1.2 User Interface Overview
+
+The application has two main areas:
+
+| Area | Access | Purpose |
+|------|--------|---------|
+| **Presentation Generator** | All users | Create, edit, and export presentations |
+| **Admin Panel** | Admins only | Manage users, clients, templates, settings |
+
+---
+
+## 2. Dashboard
+
+The dashboard is your home screen after logging in. What you see depends on your account configuration.
+
+### 2.1 Client Grid View
+
+If your account is associated with one or more clients, you see a grid of **client cards**:
+
+- Each card displays the client's **name** and **logo**
+- Click a card to view that client's templates and presentations
+- Click **"New Presentation"** to start creating
+
+### 2.2 Client Detail View
+
+After selecting a client, two tabs appear:
+
+**Templates Tab**
+- Shows all available master deck templates for this client
+- Each template card shows a **thumbnail**, **name**, **layout count**, and **status**
+- Click a template to start a new presentation with that template pre-selected
+
+**Presentations Tab**
+- Lists all presentations created under this client
+- Each card shows:
+ - Presentation **title**
+ - **Status badge**: Draft (yellow), In Review (blue), Approved (green)
+ - **Last updated** date
+- Click any card to open it in the editor
+
+### 2.3 Quick Actions
+
+| Action | How |
+|--------|-----|
+| Create new presentation | Click **"New Presentation"** button (top-right) |
+| Open existing presentation | Click on any presentation card |
+| Switch client | Click breadcrumb "All Clients" to go back to client grid |
+
+---
+
+## 3. Creating a Presentation
+
+### 3.1 Workflow Overview
+
+```mermaid
+flowchart LR
+ A[Upload Page] --> B{Input}
+ B -->|Topic| C[Text]
+ B -->|File| D[DOCX/PDF/PPTX]
+ B -->|URL| E[Web]
+ C --> F[Configure]
+ D --> F
+ E --> F
+ F --> G[Outline] --> H[Template] --> I[Generate] --> J[Edit] --> K[Export]
+```
+
+### 3.2 The Upload Page
+
+Navigate to **Create Presentation** to see the upload page. You have three ways to provide content:
+
+#### Option A: Topic or Text Prompt
+Type your presentation topic or detailed content in the large text area:
+
+> **Example:** "Quarterly business review for Q3 2026, focusing on revenue growth, customer acquisition, and product roadmap milestones"
+
+#### Option B: Upload Documents
+Drag and drop files into the upload zone, or click **"Choose Files"**:
+
+| Format | Notes |
+|--------|-------|
+| `.docx` | Word documents — structured content with tables extracted |
+| `.pptx` | PowerPoint files — existing slides used as reference |
+| `.pdf` | PDF documents — text and layout extracted (max 1 PDF) |
+| `.txt` | Plain text files — raw content |
+
+Multiple files can be uploaded simultaneously (except PDFs — only one allowed).
+
+#### Option C: Paste a URL
+Enter a web URL to extract content from a web page. The system fetches and processes the page content automatically.
+
+### 3.3 Configuration Options
+
+Before generating, configure these settings:
+
+**Slide Count**
+- Choose from presets: 5, 8, 10, 12, 15, or 20 slides
+- Or enter a custom number
+
+**Language**
+- Select the output language from a searchable dropdown
+- Default: English
+
+**Advanced Settings** (click the sliders icon):
+
+| Setting | Options | Default |
+|---------|---------|---------|
+| **Tone** | Default, Professional, Casual, Funny, Academic | Default |
+| **Verbosity** | Concise, Standard, Detailed | Standard |
+| **Table of Contents** | On/Off | Off |
+| **Title Slide** | On/Off | On |
+| **Web Search** | On/Off — allows AI to search the web for fresh data | Off |
+| **Custom Instructions** | Free text to guide the AI | — |
+
+> **Tip:** Custom instructions are powerful. Use them to specify audience, emphasize particular topics, or set formatting preferences.
+
+### 3.4 Document Preview
+
+If you uploaded documents, a **Document Preview** page appears next:
+
+- **Left sidebar** lists all uploaded files
+- Click a file to preview its extracted content in the main area
+- Content is rendered as formatted Markdown
+- Click **"Next"** to proceed to the outline
+
+---
+
+## 4. Working with the Outline
+
+The outline page shows the AI-generated structure for your presentation.
+
+### 4.1 Outline View
+
+Each slide appears as an editable card with:
+
+- **Slide number** badge (left side)
+- **Content editor** — editable Markdown content
+- **Attached files** — badges showing which uploaded documents are linked to this slide
+- **Drag handle** — reorder slides by dragging
+- **Delete button** — remove a slide (trash icon, right side)
+
+### 4.2 Editing the Outline
+
+| Action | How |
+|--------|-----|
+| **Edit slide content** | Click into the text area and modify |
+| **Reorder slides** | Drag the handle (dots icon) on the left |
+| **Delete a slide** | Click the trash icon on the right |
+| **Add a new slide** | Click **"+ Add Slide"** at the bottom |
+
+### 4.3 Streaming Indicator
+
+While the AI is generating the outline:
+- A **"Thinking"** spinner appears
+- Content streams in real-time with animation
+- The active slide auto-scrolls into view
+- Editing is disabled until streaming completes
+
+> **Note:** Wait for streaming to finish before making edits to avoid conflicts.
+
+---
+
+## 5. Selecting a Template
+
+Switch to the **"Select Template"** tab on the outline page.
+
+### 5.1 Built-in Templates
+
+Pre-configured templates ship with the system. Each card shows:
+- Template **name** and **description**
+- Preview images of 4 sample layouts
+- A **"Selected"** badge on the active choice
+
+### 5.2 Custom AI Templates
+
+If your organization has uploaded master decks (PPTX files parsed by AI), they appear under **"Custom AI Templates"**:
+- Shows the master deck **thumbnail** and **name**
+- Contains layouts specifically designed for your brand
+
+### 5.3 Generating the Presentation
+
+1. Select a template (border turns blue)
+2. Click **"Generate Presentation"** at the bottom
+3. You are redirected to the progress page
+
+> **Important:** You must select a template before the Generate button becomes active.
+
+---
+
+## 6. Generation Progress
+
+### 6.1 Progress Screen
+
+```mermaid
+stateDiagram-v2
+ [*] --> Processing
+ Processing --> Completed: All slides generated
+ Processing --> Failed: Error occurred
+ Completed --> Editor: Auto-redirect (1.5s)
+ Failed --> Outline: User clicks Retry
+```
+
+The progress page shows:
+- **Status icon**: Animated hamster wheel (processing), green check (done), or red X (failed)
+- **Progress bar** with percentage
+- **Status message** describing the current step
+
+### 6.2 Actions During Generation
+
+| Status | Available Actions |
+|--------|-------------------|
+| **Processing** | **Cancel** — stops the job, returns to outline |
+| **Completed** | **Open Presentation** — auto-opens after 1.5 seconds |
+| **Failed** | **Retry** — returns to outline to try again |
+
+### 6.3 Connection Method
+
+The progress page uses **Server-Sent Events (SSE)** for real-time updates. If SSE fails, it falls back to **polling** every 3 seconds.
+
+---
+
+## 7. Editing Slides
+
+### 7.1 Editor Layout
+
+```mermaid
+flowchart LR
+ subgraph Editor
+ A[Left Sidebar
Slide Thumbnails] --- B[Main Content
Slide Preview]
+ end
+ subgraph Header
+ C[Logo] --- D[Actions
Undo/Redo/Export]
+ end
+```
+
+The presentation editor has:
+- **Header bar** (purple) with actions
+- **Left sidebar** with slide thumbnails
+- **Main area** with full-size slide previews
+
+### 7.2 Sidebar Navigation
+
+Toggle between two views:
+- **Grid view** — thumbnail previews of all slides
+- **List view** — slide numbers and titles
+
+Click any slide to navigate to it. Drag to reorder.
+
+### 7.3 Slide Actions
+
+Hover over any slide to reveal action overlays:
+
+| Position | Action | Description |
+|----------|--------|-------------|
+| **Top-left** | Update slide | Opens prompt input — describe changes and AI regenerates |
+| **Top-right** | Delete slide | Removes the slide |
+| **Top-right** | Speaker notes | View/edit speaker notes for this slide |
+| **Bottom-center** | Add slide | Insert a new slide below this one |
+
+### 7.4 AI-Powered Slide Updates
+
+1. Hover over a slide
+2. Click the **magic wand** icon (top-left)
+3. Enter your update prompt (e.g., "Add a chart showing quarterly revenue")
+4. Click **"Update"**
+5. The AI regenerates the slide content based on your instructions
+
+> **Note:** Updates may take 30–120 seconds depending on content complexity and image generation.
+
+### 7.5 Auto-Save
+
+The editor automatically saves your changes. A spinning icon in the header indicates a save is in progress.
+
+---
+
+## 8. Review Workflow
+
+### 8.1 Status Flow
+
+```mermaid
+stateDiagram-v2
+ direction LR
+ Draft --> InReview: Submit for Review
+ InReview --> Approved: Approve
+ InReview --> Draft: Return to Draft
+ Approved --> Draft: Reopen
+```
+
+Every presentation has a review status:
+
+| Status | Badge Color | Meaning |
+|--------|-------------|---------|
+| **Draft** | Yellow | Work in progress — not reviewed |
+| **In Review** | Blue | Submitted for review — awaiting approval |
+| **Approved** | Green | Reviewed and approved — ready for use |
+
+### 8.2 Changing Status
+
+1. Click the **status badge** in the editor header
+2. A popover appears with:
+ - Current status display
+ - Last review comment (if any)
+ - Available status transition buttons
+ - Comment text area
+
+### 8.3 Adding Review Comments
+
+1. Open the status popover
+2. Type a comment in the text area
+3. Click **"Add Comment"**
+
+Comments are saved and visible to all users who access the presentation.
+
+### 8.4 Status Transitions
+
+| Current Status | Available Actions |
+|----------------|-------------------|
+| **Draft** | Submit to **In Review** |
+| **In Review** | **Approve** or return to **Draft** |
+| **Approved** | Return to **Draft** |
+
+---
+
+## 9. Exporting Presentations
+
+### 9.1 Export Options
+
+Click **"Export"** in the editor header to see options:
+
+| Format | Description |
+|--------|-------------|
+| **PDF** | High-quality PDF document, one slide per page |
+| **PPTX** | PowerPoint file, editable in Microsoft PowerPoint |
+
+### 9.2 Export Process
+
+```mermaid
+sequenceDiagram
+ participant U as User
+ participant E as Editor
+ participant S as Server
+ participant P as Puppeteer
+
+ U->>E: Click Export
+ E->>E: Auto-save presentation
+ E->>S: Request export
+ S->>P: Render slides in headless browser
+ P->>S: Return rendered output
+ S->>E: File ready
+ E->>U: Download starts
+```
+
+1. Click **"Export as PDF"** or **"Export as PPTX"**
+2. The system auto-saves your latest changes
+3. A loading overlay appears: **"Exporting presentation..."**
+4. The rendered file downloads automatically
+
+> **Note:** Export may take 15–60 seconds depending on slide count and image complexity.
+
+---
+
+## 10. Keyboard Shortcuts & Tips
+
+### 10.1 Tips for Better Presentations
+
+
+
+**Be specific in your prompt.** Instead of "Make a sales presentation," try: "Create a 12-slide sales pitch for enterprise CRM software targeting Fortune 500 IT directors, emphasizing ROI and integration capabilities."
+
+
+
+
+
+**Use Custom Instructions wisely.** Add rules like: "Use bullet points, not paragraphs. Keep each slide to 3-4 key points. Include data visualizations where possible."
+
+
+
+
+
+**Upload reference documents.** The AI produces significantly better content when given source material like reports, briefs, or existing presentations to draw from.
+
+
+
+
+
+**Image generation may fail silently.** If a slide shows a placeholder instead of a generated image, check the slide content for an image error indicator. Try updating the slide with a more specific image description.
+
+
+
+### 10.2 Troubleshooting
+
+| Problem | Solution |
+|---------|----------|
+| Export fails | Refresh the page and try again. Ensure the presentation has been saved. |
+| Slide update hangs | Wait up to 2 minutes. If still loading, refresh and retry. |
+| Outline won't generate | Check that you've entered content or uploaded at least one document. |
+| Template not showing | Ask your admin to verify the master deck has been fully parsed. |
+| Images missing | Image generation may be disabled or the provider API key may be invalid. Contact your admin. |
+| Cannot log in | Verify your credentials. In dev mode, check the `DEV_AUTH_PASSWORD` setting. |
+
+### 10.3 Browser Requirements
+
+Oliver DeckForge works best with:
+- **Google Chrome** 90+
+- **Microsoft Edge** 90+
+- **Firefox** 90+
+- **Safari** 15+
+
+> JavaScript must be enabled. A stable internet connection is required for AI generation.
+
+---
+
+
+
+**Oliver DeckForge** | Enterprise AI Presentation Generator
+
+Version 1.0 | © 2026 All Rights Reserved
+
+
diff --git a/docs/01-User-Manual.pdf b/docs/01-User-Manual.pdf
new file mode 100644
index 0000000..6964dab
Binary files /dev/null and b/docs/01-User-Manual.pdf differ
diff --git a/docs/02-System-Administration-Guide.md b/docs/02-System-Administration-Guide.md
new file mode 100644
index 0000000..6998b11
--- /dev/null
+++ b/docs/02-System-Administration-Guide.md
@@ -0,0 +1,1333 @@
+---
+pdf_options:
+ format: A4
+ margin: 25mm 20mm
+ headerTemplate: 'Oliver DeckForge — System Administration Guide
'
+ footerTemplate: ' /
'
+ displayHeaderFooter: true
+stylesheet:
+ - https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.5.1/github-markdown.min.css
+body_class: markdown-body
+css: |-
+ body { font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; }
+ .markdown-body { max-width: 100%; }
+ h1 { color: #5146E5; border-bottom: 3px solid #5146E5; padding-bottom: 8px; }
+ h2 { color: #3b3494; border-bottom: 1px solid #E9E8F8; padding-bottom: 6px; margin-top: 2em; }
+ h3 { color: #4a4a4a; }
+ h4 { color: #555; }
+ table { width: 100%; }
+ th { background: #5146E5; color: white; }
+ td, th { padding: 8px 12px; }
+ code { background: #f0eff8; color: #5146E5; padding: 2px 6px; border-radius: 4px; }
+ pre code { background: #1e1e2e; color: #cdd6f4; display: block; padding: 16px; border-radius: 8px; }
+ blockquote { border-left: 4px solid #5146E5; background: #f8f7ff; padding: 12px 16px; margin: 16px 0; }
+ .tip { border-left: 4px solid #22c55e; background: #f0fdf4; padding: 12px 16px; margin: 16px 0; border-radius: 0 8px 8px 0; }
+ .warning { border-left: 4px solid #f59e0b; background: #fffbeb; padding: 12px 16px; margin: 16px 0; border-radius: 0 8px 8px 0; }
+ .danger { border-left: 4px solid #ef4444; background: #fef2f2; padding: 12px 16px; margin: 16px 0; border-radius: 0 8px 8px 0; }
+ img { max-width: 100%; max-height: 280px; object-fit: contain; display: block; margin: 16px auto; }
+---
+
+# Oliver DeckForge — System Administration Guide
+
+**Version 1.0** | Complete Setup, Configuration & Operations Reference
+
+---
+
+## Table of Contents
+
+1. [Architecture Overview](#1-architecture-overview)
+2. [Installation & Deployment](#2-installation--deployment)
+3. [Environment Variables Reference](#3-environment-variables-reference)
+4. [Authentication Configuration](#4-authentication-configuration)
+5. [Role-Based Access Control](#5-role-based-access-control)
+6. [Admin Panel Operations](#6-admin-panel-operations)
+7. [Template Pipeline](#7-template-pipeline)
+8. [AI Provider Configuration](#8-ai-provider-configuration)
+9. [Database Administration](#9-database-administration)
+10. [Background Jobs & Workers](#10-background-jobs--workers)
+11. [Nginx & Networking](#11-nginx--networking)
+12. [Storage & File Management](#12-storage--file-management)
+13. [Monitoring & Logging](#13-monitoring--logging)
+14. [Backup & Recovery](#14-backup--recovery)
+15. [Security Hardening](#15-security-hardening)
+16. [Scaling & Performance](#16-scaling--performance)
+17. [Troubleshooting](#17-troubleshooting)
+18. [API Reference](#18-api-reference)
+
+---
+
+
+## 1. Architecture Overview
+
+### 1.1 System Architecture
+
+```mermaid
+graph LR
+ Browser([Browser]) --> NG[nginx :80]
+ NG --> NX[Next.js :3000]
+ NG --> FA[FastAPI :8000]
+ FA --> PG[(PostgreSQL)]
+ FA --> RD[(Redis)]
+ FA --> VOL[/app_data/]
+ ARQ[ARQ Worker] --> PG
+ ARQ --> RD
+ ARQ --> VOL
+ FA --> AI{AI APIs}
+ ARQ --> AI
+```
+
+### 1.2 Service Overview
+
+| Service | Technology | Port | Purpose |
+|---------|-----------|------|---------|
+| **nginx** | nginx:alpine | 80 | Reverse proxy, static file serving, SSL termination |
+| **web** | Next.js 14 | 3000 | Frontend SPA, Puppeteer-based export |
+| **api** | FastAPI + SQLModel | 8000 | REST API, authentication, RBAC |
+| **worker** | ARQ (Python) | — | Background AI generation, parsing, retention |
+| **postgres** | PostgreSQL 16 | 5432 | Primary relational database |
+| **redis** | Redis 7 | 6379 | Job queue, caching |
+
+### 1.3 Request Flow
+
+```mermaid
+sequenceDiagram
+ participant B as Browser
+ participant N as nginx
+ participant W as Next.js
+ participant A as FastAPI
+ participant M as Middleware
+ participant DB as PostgreSQL
+
+ B->>N: HTTP Request
+ alt /api/v1/*
+ N->>A: Proxy to backend
+ A->>M: Auth + RBAC check
+ M->>DB: Validate session
+ DB-->>M: User context
+ M-->>A: request.state.user
+ A-->>N: JSON response
+ else /* (all other)
+ N->>W: Proxy to frontend
+ W-->>N: HTML/JS/CSS
+ end
+ N-->>B: Response
+```
+
+### 1.4 Data Flow: Presentation Generation
+
+```mermaid
+flowchart LR
+ A[Submit] --> B[Decompose] --> C[Outline] --> D[Template]
+ D --> E[Job Queue] --> F[Worker]
+ F --> G[AI Content] --> H[Images] --> I[Save DB]
+ I --> J[SSE Notify] --> K[Editor]
+```
+
+---
+
+
+## 2. Installation & Deployment
+
+### 2.1 Prerequisites
+
+| Requirement | Minimum | Recommended |
+|-------------|---------|-------------|
+| Docker | 20.10+ | 24.0+ |
+| Docker Compose | v2.0+ | v2.20+ |
+| RAM | 4 GB | 8 GB+ |
+| Disk | 10 GB | 50 GB+ (for generated assets) |
+| CPU | 2 cores | 4+ cores |
+
+### 2.2 Quick Start
+
+```bash
+# 1. Clone repository
+git clone
+cd ppt-tool
+
+# 2. Configure environment
+cp .env.example .env
+# Edit .env — set ANTHROPIC_API_KEY at minimum
+
+# 3. Build and start
+make dev
+
+# 4. Run migrations
+make migrate
+
+# 5. Seed default data
+make seed
+```
+
+The application is available at:
+- **http://localhost** — Full application (via nginx)
+- **http://localhost:3000** — Frontend directly
+- **http://localhost:8000** — API directly
+- **http://localhost/docs** — Swagger API documentation
+
+### 2.3 Makefile Commands
+
+| Command | Description |
+|---------|-------------|
+| `make dev` | Build and start all services with logs |
+| `make build` | Build Docker images only |
+| `make up` | Start services in background (detached) |
+| `make down` | Stop and remove all containers |
+| `make migrate` | Run Alembic database migrations |
+| `make seed` | Seed default admin user and team |
+| `make test` | Run backend pytest suite |
+| `make test-e2e` | Run Cypress E2E tests |
+| `make test-all` | Run all tests |
+| `make logs` | Follow all container logs |
+| `make shell-api` | Open bash shell in API container |
+| `make shell-db` | Open psql shell in PostgreSQL |
+
+### 2.4 Local Development (Without Docker)
+
+#### Backend
+
+```bash
+cd backend
+python -m venv venv && source venv/bin/activate
+pip install -r requirements.txt
+
+export DATABASE_URL="postgresql+asyncpg://deckforge:deckforge@localhost:5432/deckforge"
+export REDIS_URL="redis://localhost:6379/0"
+export APP_DATA_DIRECTORY="./data"
+export ANTHROPIC_API_KEY="sk-ant-..."
+
+# Start API
+uvicorn api.main:app --reload --port 8000
+
+# Start worker (separate terminal)
+python -m arq workers.main.WorkerSettings
+```
+
+#### Frontend
+
+```bash
+cd frontend
+npm install
+npm run dev
+```
+
+The Next.js dev server proxies `/api/v1/` requests to `http://localhost:8000` via rewrites in `next.config.mjs`.
+
+### 2.5 Docker Image Details
+
+**Backend Dockerfile** (multi-stage):
+- **Builder stage**: `python:3.11-slim-bookworm` + `uv` package manager
+- **Runtime stage**: Includes LibreOffice (PDF conversion), Chromium (browser automation), fontconfig, ONNX models
+- Exposes port 8000
+
+**Frontend Dockerfile**:
+- **Base**: `node:20-alpine`
+- Includes Chromium for server-side Puppeteer PDF/PPTX export
+- Environment: `PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser`
+- Exposes port 3000
+
+---
+
+
+## 3. Environment Variables Reference
+
+### 3.1 Database & Infrastructure
+
+| Variable | Required | Default | Description |
+|----------|----------|---------|-------------|
+| `POSTGRES_PASSWORD` | No | `deckforge` | PostgreSQL password |
+| `DATABASE_URL` | Auto | Set by docker-compose | Full async connection string |
+| `REDIS_URL` | No | `redis://redis:6379/0` | Redis connection string |
+| `APP_DATA_DIRECTORY` | No | `/app_data` | Path for images, exports, uploads |
+| `TEMP_DIRECTORY` | No | `/tmp/deckforge` | Temporary file storage |
+
+### 3.2 Authentication
+
+| Variable | Required | Default | Description |
+|----------|----------|---------|-------------|
+| `JWT_SECRET_KEY` | **Yes** | `change-me-...` | Secret for JWT signing (256-bit+) |
+| `AZURE_AD_TENANT_ID` | No | — | Azure AD tenant ID (blank = dev mode) |
+| `AZURE_AD_CLIENT_ID` | No | — | Azure AD app client ID |
+| `AZURE_AD_CLIENT_SECRET` | No | — | Azure AD app secret |
+| `AZURE_AD_REDIRECT_URI` | No | `http://localhost/api/v1/auth/callback` | OAuth callback URL |
+| `DEV_AUTH_PASSWORD` | No | `devpass123` | Dev bypass login password |
+
+### 3.3 AI Providers
+
+| Variable | Required | Default | Description |
+|----------|----------|---------|-------------|
+| `LLM` | No | `anthropic` | Primary LLM provider |
+| `ANTHROPIC_API_KEY` | **Yes*** | — | Claude API key |
+| `ANTHROPIC_MODEL` | No | `claude-sonnet-4-6` | Claude model ID |
+| `OPENAI_API_KEY` | No | — | OpenAI API key |
+| `OPENAI_MODEL` | No | — | OpenAI model ID |
+| `GOOGLE_API_KEY` | No | — | Google Gemini API key |
+| `GOOGLE_MODEL` | No | — | Google model ID |
+| `OLLAMA_URL` | No | — | Ollama server URL |
+| `OLLAMA_MODEL` | No | — | Ollama model name |
+| `IMAGE_PROVIDER` | No | `nanobanana_pro` | Image generation provider |
+| `DISABLE_IMAGE_GENERATION` | No | — | Set to disable image gen |
+
+*Required if using Anthropic (default provider)
+
+### 3.4 Application Settings
+
+| Variable | Required | Default | Description |
+|----------|----------|---------|-------------|
+| `CAN_CHANGE_KEYS` | No | `false` | Allow runtime API key changes |
+| `DISABLE_ANONYMOUS_TRACKING` | No | `true` | Disable analytics tracking |
+| `SETTINGS_ENCRYPTION_KEY` | No | — | Fernet key for encrypting stored API keys |
+| `EXTENDED_REASONING` | No | — | Enable LLM extended thinking |
+| `TOOL_CALLS` | No | — | Enable LLM tool use |
+| `WEB_GROUNDING` | No | — | Enable web search in generation |
+| `NEXT_INTERNAL_URL` | No | `http://web:3000` | Backend → frontend URL (Docker) |
+
+### 3.5 Supported AI Providers
+
+**LLM Providers:**
+
+| Provider | Value | Models |
+|----------|-------|--------|
+| Anthropic | `anthropic` | claude-opus-4-6, claude-sonnet-4-6, claude-sonnet-4-5, claude-haiku-4-5 |
+| OpenAI | `openai` | gpt-4.1, gpt-4.1-mini, gpt-4o, o3, o4-mini |
+| Google | `google` | gemini-2.5-flash, gemini-2.5-pro, gemini-2.0-flash |
+| Ollama | `ollama` | Any locally installed model |
+| Custom | `custom` | Any OpenAI-compatible endpoint |
+
+**Image Providers:**
+
+| Provider | Value | Requirements |
+|----------|-------|-------------|
+| NanoBanana Pro | `nanobanana_pro` | Google API key |
+| Gemini Flash | `gemini_flash` | Google API key |
+| DALL-E 3 | `dall-e-3` | OpenAI API key |
+| GPT Image 1.5 | `gpt-image-1.5` | OpenAI API key |
+| Pexels | `pexels` | Pexels API key |
+| Pixabay | `pixabay` | Pixabay API key |
+| ComfyUI | `comfyui` | Local ComfyUI instance |
+
+---
+
+
+## 4. Authentication Configuration
+
+### 4.1 Authentication Flow
+
+```mermaid
+flowchart LR
+ A[Login Page] --> B{Azure AD?}
+ B -->|Yes| C[SSO] --> D[Callback] --> E{User exists?}
+ B -->|No| F[Dev Login] --> E
+ E -->|Yes| G[Update login]
+ E -->|No| H[Create user]
+ G --> I[JWT Cookie]
+ H --> I
+```
+
+### 4.2 Azure AD Setup (Production)
+
+1. **Register an application** in Azure Portal > Azure AD > App Registrations
+2. Set the **Redirect URI** to: `https://your-domain.com/api/v1/auth/callback`
+3. Create a **client secret** under Certificates & Secrets
+4. Configure environment variables:
+
+```env
+AZURE_AD_TENANT_ID=your-tenant-id
+AZURE_AD_CLIENT_ID=your-client-id
+AZURE_AD_CLIENT_SECRET=your-client-secret
+AZURE_AD_REDIRECT_URI=https://your-domain.com/api/v1/auth/callback
+```
+
+5. Grant API permissions: `User.Read` (delegated)
+
+### 4.3 Development Mode
+
+When `AZURE_AD_TENANT_ID` is empty or not set, the system enables development authentication:
+
+- Login form with email + password fields
+- Password validated against `DEV_AUTH_PASSWORD` environment variable
+- Users are auto-created on first login
+- Default role: `user`
+
+
+
+**Security Warning:** Development mode should never be used in production. Always configure Azure AD or another SSO provider for production deployments.
+
+
+
+### 4.4 JWT Configuration
+
+| Parameter | Value |
+|-----------|-------|
+| Algorithm | HS256 |
+| Expiry | 24 hours |
+| Storage | `session_token` HTTP cookie |
+| Payload | `sub` (user UUID), `email`, `role`, `exp`, `iat` |
+
+
+
+**Critical:** Change `JWT_SECRET_KEY` from the default value in production. Use a cryptographically random 256-bit key. All active sessions are invalidated when this key changes.
+
+
+
+---
+
+
+## 5. Role-Based Access Control
+
+### 5.1 Role Hierarchy
+
+```mermaid
+graph LR
+ SA[Super Admin] --> CA[Client Admin] --> U[User]
+ SA -.->|Full access| ALL[All Clients]
+ CA -.->|Scoped| CLIENT[Assigned Clients]
+ U -.->|Basic| OWN[Own Data]
+```
+
+### 5.2 Permission Matrix
+
+| Feature | Super Admin | Client Admin | User |
+|---------|:-----------:|:------------:|:----:|
+| **Presentations** | All | Client-scoped | Own only |
+| **Admin Panel** | Full | Limited | None |
+| **User Management** | CRUD all | View team members | — |
+| **Client Management** | CRUD all | View/edit assigned | — |
+| **Team Management** | All teams | Assigned client teams | — |
+| **Master Decks** | All | Client-scoped | — |
+| **Storage** | All clients | Client-scoped | — |
+| **Analytics** | Global + per-client | Client-scoped | — |
+| **Audit Logs** | All | Client-scoped | — |
+| **System Settings** | Full access | — | — |
+| **Brand Config** | All clients | Client-scoped | — |
+
+### 5.3 Multi-Tenant Data Isolation
+
+```mermaid
+flowchart LR
+ A[Request] --> B[Auth] --> C[RBAC] --> D{Role}
+ D -->|super_admin| E[No filter]
+ D -->|client_admin| F[Team lookup → client_ids]
+ D -->|user| G[Own data + team clients]
+```
+
+The `_resolve_client_filter()` pattern is used throughout:
+- **Super Admin with no client_id param** → Returns `None` (no filter, see all)
+- **Super Admin with client_id param** → Filters to specific client
+- **Client Admin** → Auto-scoped to accessible clients via `TeamMembershipModel`
+- **User** → Scoped to own data within accessible clients
+
+### 5.4 Admin Panel Navigation
+
+The sidebar dynamically shows menu items based on role:
+
+| Menu Item | Super Admin | Client Admin |
+|-----------|:-----------:|:------------:|
+| Users | Yes | — |
+| Clients | Yes | Yes |
+| Storage | Yes | Yes |
+| Audit Log | Yes | Yes |
+| Analytics | Yes | Yes |
+| Settings | Yes | — |
+
+---
+
+
+## 6. Admin Panel Operations
+
+### 6.1 User Management
+
+**Path:** Admin > Users (Super Admin only)
+
+#### Listing Users
+- Table with columns: Name, Email, Role, Status, Last Login
+- Filterable by active status and role
+
+#### Changing User Roles
+1. Find the user in the list
+2. Click the role dropdown
+3. Select new role: `super_admin`, `client_admin`, or `user`
+
+
+
+You cannot change your own role. This prevents accidental lockout.
+
+
+
+#### Deactivating Users
+1. Click **Deactivate** on the user row
+2. Confirm the action
+3. User's `is_active` is set to false — they can no longer log in
+4. Their presentations remain in the system
+
+#### Transferring Ownership
+Before deactivating a user (e.g., for GDPR compliance):
+1. Use the transfer ownership endpoint to move all presentations to another user
+2. Then deactivate the original user
+
+### 6.2 Client Management
+
+**Path:** Admin > Clients
+
+#### Creating a Client
+1. Click **"+ New Client"**
+2. Enter the client name
+3. A URL-safe slug is auto-generated
+4. A default team is auto-created for the client
+
+#### Client Settings
+Each client has configurable:
+
+| Setting | Description |
+|---------|-------------|
+| **Name** | Display name |
+| **Slug** | URL-safe identifier (unique) |
+| **Review Policy** | `self_approve` or `require_reviewer` |
+| **Retention Days** | Auto-delete presentations after N days (optional) |
+
+### 6.3 Team Management
+
+**Path:** Admin > Clients > [Client] > Teams
+
+Teams group users within a client:
+- Each client has a **default team** (cannot be deleted)
+- Users can belong to multiple teams
+- Team membership determines client access for non-admin users
+
+#### Adding Team Members
+1. Navigate to the client's team page
+2. Click **"+ Add Member"**
+3. Search and select a user from the dropdown
+4. The user now has access to this client's data
+
+### 6.4 Brand Configuration
+
+**Path:** Admin > Clients > [Client] > Brand Config
+
+Configure branding per client:
+
+| Setting | Description |
+|---------|-------------|
+| **Primary Colors** | Color picker, add/remove multiple colors |
+| **Secondary Colors** | Color picker, add/remove multiple colors |
+| **Fonts** | Heading, Body, and Accent font names |
+| **Logos** | Upload multiple logo images |
+| **Voice Rules** | Text guidelines for AI tone and style |
+| **Voice Examples** | Good/bad example pairs for AI training |
+| **Brand Guideline** | Upload a PDF/DOCX brand guide |
+
+Brand configuration is injected into AI prompts during presentation generation to ensure brand consistency.
+
+### 6.5 Storage Management
+
+**Path:** Admin > Storage
+
+#### Summary Dashboard
+Four cards show:
+- **Presentations** count
+- **Export Files** count
+- **Master Decks** count
+- **Total Size** (formatted)
+
+#### Client Selector (Super Admin)
+Dropdown to filter by specific client or view all clients combined.
+
+#### Presentation Table
+- Columns: Title, Status, Created, Files, Size
+- Checkboxes for bulk selection
+- Per-row actions: Download PPTX, Delete
+
+#### Bulk Operations
+- Select multiple presentations via checkboxes
+- Click **"Delete Selected"** for bulk soft-delete
+
+#### Purge Files (Super Admin Only)
+- Amber banner shows count of soft-deleted presentations
+- **"Purge Files"** permanently removes files from disk
+- Returns statistics: files purged, bytes freed
+
+### 6.6 Analytics Dashboard
+
+**Path:** Admin > Analytics
+
+#### Overview Metrics
+- Total Presentations (all-time)
+- This Month / This Week (30/7-day counts)
+- Active Users (distinct users, last 30 days)
+- Approval Rate (% approved or in_review)
+
+#### Usage Metrics
+- **Presentations per Day** — 14-day bar chart
+- **Top 10 Users** — ranked by presentation count
+
+#### Quality Metrics
+- **Status Distribution** — draft/in_review/approved breakdown
+- **Presentations with Comments** — count
+
+#### Performance Metrics
+- **Average Generation Time** — job completion duration
+- **Total Jobs** — all-time count
+- **Error Rate** — % failed jobs
+
+#### AI Usage (if tracking enabled)
+- Total AI Calls, Input/Output Tokens
+- Usage by Provider (bar chart)
+- Usage by Model (top 10)
+- Daily Usage Trend
+
+### 6.7 Audit Logs
+
+**Path:** Admin > Audit Log
+
+All mutating API requests are logged automatically.
+
+#### Query Filters
+- **Action** — text search (e.g., "admin_delete")
+- **User ID** — filter by specific user
+- **Resource Type** — e.g., "presentation", "storage"
+- **Client ID** — scope to a client
+- **Date Range** — from/to date pickers
+
+#### Export
+- Click **"Export Audit Log"**
+- Choose format: CSV or JSON
+- Downloads up to 10,000 entries
+
+#### Logged Actions
+| Action | Trigger |
+|--------|---------|
+| `admin_delete` | Single presentation soft-delete |
+| `admin_bulk_delete` | Bulk presentation delete |
+| `admin_purge` | Hard-delete purged files |
+| Role changes, team membership updates, etc. | Various admin operations |
+
+---
+
+
+## 7. Template Pipeline
+
+### 7.1 Master Deck → Template Flow
+
+```mermaid
+flowchart LR
+ A[Upload PPTX] --> B[Enqueue Job]
+ B --> C{Parse Mode}
+ C -->|layouts| D[slideLayouts XML]
+ C -->|slides| E[slides XML]
+ D --> F[PDF → Screenshots]
+ E --> F
+ F --> G[LLM Vision: Screenshot+XML → TSX]
+ G --> H[Store Layouts] --> I[Register Template]
+```
+
+### 7.2 Upload & Parse
+
+1. Navigate to **Admin > Clients > [Client] > Master Decks**
+2. Click **"Upload PPTX"** and select a `.pptx` file
+3. The deck enters **"pending"** status, then **"processing"**
+4. Auto-polling every 5 seconds shows current status
+5. On completion, status changes to **"completed"** and layouts appear
+
+### 7.3 Parse Modes
+
+| Mode | Source | Best For |
+|------|--------|----------|
+| **slides** (default) | Actual slides (`ppt/slides/`) | Decks with unique slide designs; 1:1 screenshot match |
+| **layouts** | Slide layouts (`ppt/slideLayouts/`) | Decks with reusable layout templates; may produce more layouts |
+
+### 7.4 Layout Management
+
+After parsing, manage layouts in the expanded deck view:
+
+**Filtering & Search:**
+- Text search by layout name
+- Type filter dropdown (auto-detected from layout types)
+- Code filter: All / Has Code / Missing Code
+
+**Individual Actions:**
+- **Edit** — modify name, type, or React TSX code
+- **Delete** — remove with confirmation dialog
+
+**Bulk Actions:**
+- Toggle **Select Mode** to show checkboxes
+- **Select All** / **Deselect All**
+- **Delete Selected** — bulk remove
+
+
+
+After deleting layouts, the system automatically re-registers the template by recreating `PresentationLayoutCodeModel` records for the remaining layouts.
+
+
+
+### 7.5 Reparsing
+
+If layouts need to be regenerated (e.g., after an LLM model upgrade):
+1. Click the reparse dropdown on the deck card
+2. Choose **"Reparse (slides)"** or **"Reparse (layouts)"**
+3. All existing layouts are replaced with freshly parsed versions
+
+---
+
+
+## 8. AI Provider Configuration
+
+### 8.1 Settings Page
+
+**Path:** Admin > Settings (Super Admin only)
+
+```mermaid
+flowchart LR
+ A[Select LLM Provider] --> B[Fetch Available Models]
+ B --> C[Select Model]
+ C --> D[Enter API Key]
+ D --> E[Test Connection]
+ E -->|OK| F[Save Settings]
+ E -->|Fail| G[Check Key / Network]
+```
+
+### 8.2 Configuring LLM Provider
+
+1. Open **Admin > Settings**
+2. Select the **LLM Provider** from the dropdown
+3. The **Model** dropdown auto-populates with available models
+4. Enter the **API Key** if not already set (shown as "Set" badge if configured)
+5. Click **"Test"** to verify connectivity
+6. Click **"Save Changes"**
+
+### 8.3 Configuring Image Provider
+
+1. Select the **Image Provider** from the dropdown
+2. Ensure the required API key is set (e.g., Google API key for NanoBanana Pro)
+3. Save changes
+
+### 8.4 Connection Testing
+
+The **"Test"** button performs a lightweight API call to validate the key:
+
+| Result | Display |
+|--------|---------|
+| Success | Green check + latency in ms |
+| Failure | Red X + error message |
+
+### 8.5 Settings Persistence
+
+Settings are persisted to the database via `KeyValueSqlModel`:
+- Survive container restarts
+- API keys optionally encrypted at rest (if `SETTINGS_ENCRYPTION_KEY` is set)
+- Environment variables serve as defaults — database values override them
+
+---
+
+
+## 9. Database Administration
+
+### 9.1 Schema Overview
+
+```mermaid
+erDiagram
+ UserModel ||--o{ TeamMembershipModel : "belongs to"
+ TeamModel ||--o{ TeamMembershipModel : "has"
+ ClientModel ||--o{ TeamModel : "owns"
+ ClientModel ||--o{ BrandConfigModel : "has"
+ ClientModel ||--o{ MasterDeckModel : "has"
+
+ UserModel ||--o{ PresentationModel : "creates"
+ ClientModel ||--o{ PresentationModel : "scopes"
+ PresentationModel ||--o{ SlideModel : "contains"
+ PresentationModel ||--o{ JobModel : "tracks"
+
+ UserModel ||--o{ AuditLogModel : "generates"
+ UserModel ||--o{ AIUsageModel : "tracks"
+
+ MasterDeckModel ||--o{ TemplateModel : "registers as"
+ TemplateModel ||--o{ PresentationLayoutCodeModel : "has layouts"
+```
+
+### 9.2 Core Tables
+
+| Table | Purpose | Key Fields |
+|-------|---------|------------|
+| `usermodel` | User accounts | id, email, role, azure_oid, is_active |
+| `clientmodel` | Tenant organizations | id, name, slug, retention_days, review_policy |
+| `teammodel` | Team groupings | id, name, client_id, is_default |
+| `teammembershipmodel` | User↔Team links | user_id, team_id, assigned_by |
+| `presentationmodel` | Presentations | id, title, owner_id, client_id, status, content |
+| `slidemodel` | Individual slides | id, presentation, index, content, layout |
+| `jobmodel` | Background jobs | id, job_type, status, progress, error_message |
+| `masterdeck` | Master PPTX decks | id, client_id, layouts (JSON), parse_status |
+| `templatemodel` | Registered templates | id, name, description |
+| `presentationlayoutcodemodel` | Layout TSX code | presentation, layout_name, layout_code |
+| `brandconfigmodel` | Brand settings | client_id, colors, fonts, logos, voice_rules |
+| `auditlogmodel` | Audit trail | user_id, action, resource_type, ip_address |
+| `aiusagemodel` | AI usage metrics | provider, model, tokens, duration_ms |
+| `keyvaluesqlmodel` | KV settings store | key, value (JSON) |
+| `imageasset` | Generated images | id, path, is_uploaded |
+
+### 9.3 Alembic Migrations
+
+```bash
+# View migration history
+docker compose exec api alembic history
+
+# Apply all pending migrations
+make migrate
+# or: docker compose exec api alembic upgrade head
+
+# Generate new migration after model changes
+docker compose exec api alembic revision --autogenerate -m "description"
+
+# Rollback last migration
+docker compose exec api alembic downgrade -1
+
+# View current revision
+docker compose exec api alembic current
+```
+
+
+
+Always review auto-generated migrations before applying. SQLAlchemy may miss rename operations (interpreting them as drop + create) or produce incorrect defaults.
+
+
+
+### 9.4 Direct Database Access
+
+```bash
+# Interactive psql
+make shell-db
+
+# Common queries
+SELECT COUNT(*) FROM usermodel;
+SELECT COUNT(*) FROM presentationmodel WHERE deleted_at IS NULL;
+SELECT status, COUNT(*) FROM jobmodel GROUP BY status;
+SELECT provider, SUM(total_tokens) FROM aiusagemodel GROUP BY provider;
+```
+
+---
+
+
+## 10. Background Jobs & Workers
+
+### 10.1 ARQ Worker Configuration
+
+```mermaid
+flowchart LR
+ RD[(Redis)] --> W[ARQ Worker]
+ W --> A[generate_presentation]
+ W --> B[parse_master_deck]
+ W --> C[Cron: cleanup 2AM / purge Mon 3AM]
+```
+
+| Setting | Value | Description |
+|---------|-------|-------------|
+| `max_jobs` | 5 | Maximum concurrent background jobs |
+| `job_timeout` | 1800s (30 min) | Per-job timeout |
+| `max_tries` | 3 | Retry attempts on failure |
+| `health_check_interval` | 30s | Health check frequency |
+
+### 10.2 Job Types
+
+#### Presentation Generation (`generate_presentation_task`)
+1. Load request from PresentationModel
+2. Fetch brand context (colors, fonts, voice rules)
+3. Generate outlines via LLM
+4. Generate per-slide structure and content
+5. Run image generation for each slide
+6. Save results to database
+7. Update JobModel progress (0–100%)
+
+#### Master Deck Parsing (`parse_master_deck_task`)
+1. Extract XML layouts/slides from PPTX
+2. Convert PPTX to PDF via LibreOffice
+3. Split PDF into per-page screenshots
+4. Send each screenshot + XML to LLM vision
+5. Store generated React TSX code
+6. Register as template
+
+#### Retention Cleanup (Cron — Daily)
+- Soft-deletes presentations exceeding client's `retention_days`
+- Runs at 2:00 AM UTC
+
+#### Retention Purge (Cron — Weekly)
+- Permanently deletes files for presentations soft-deleted 30+ days ago
+- Runs Monday 3:00 AM UTC
+
+### 10.3 Monitoring Jobs
+
+```bash
+# View worker logs
+docker compose logs -f worker
+
+# Check job status in database
+make shell-db
+# Then: SELECT id, job_type, status, progress, error_message FROM jobmodel ORDER BY created_at DESC LIMIT 20;
+```
+
+### 10.4 Common Job Issues
+
+| Issue | Cause | Solution |
+|-------|-------|----------|
+| Job stuck at 0% | Worker crashed or no workers running | Restart worker: `docker compose restart worker` |
+| Job times out | LLM response too slow | Increase `job_timeout` in WorkerSettings |
+| Job fails repeatedly | Invalid API key or model | Check Settings page, test connection |
+| Queue backed up | Too many concurrent requests | Scale workers horizontally |
+
+---
+
+
+## 11. Nginx & Networking
+
+### 11.1 Routing Rules
+
+```mermaid
+flowchart LR
+ A[Request :80] --> B{Path}
+ B -->|/api/v1/*| C[FastAPI :8000]
+ B -->|/app_data/*| D[Static files]
+ B -->|/static/*| D
+ B -->|/* catch-all| E[Next.js :3000]
+```
+
+### 11.2 Key Configuration
+
+| Setting | Value | Purpose |
+|---------|-------|---------|
+| `client_max_body_size` | 100M | Allow large PPTX uploads |
+| `proxy_read_timeout` | 30m | Long-running LLM operations |
+| `proxy_connect_timeout` | 30m | Connection establishment |
+| `proxy_buffering` | off | SSE streaming support |
+| `chunked_transfer_encoding` | off | SSE streaming support |
+
+### 11.3 SSL/TLS (Production)
+
+The default `nginx.conf` serves HTTP only. For production, add SSL:
+
+```nginx
+server {
+ listen 443 ssl http2;
+ ssl_certificate /etc/ssl/certs/your-cert.pem;
+ ssl_certificate_key /etc/ssl/private/your-key.pem;
+ # ... existing location blocks ...
+}
+
+server {
+ listen 80;
+ return 301 https://$host$request_uri;
+}
+```
+
+### 11.4 Inter-Service Communication
+
+```mermaid
+flowchart LR
+ API["api :8000"] -->|DATABASE_URL| PG["postgres :5432"]
+ API -->|REDIS_URL| RD["redis :6379"]
+ API -->|NEXT_INTERNAL_URL| WEB["web :3000"]
+ WORKER["worker"] -->|DATABASE_URL| PG
+ WORKER -->|REDIS_URL| RD
+ WEB -->|API_INTERNAL_URL| API
+ NG["nginx :80"] --> API
+ NG --> WEB
+```
+
+All services communicate via Docker Compose's internal network. No ports need to be exposed to the host except:
+- **80** (nginx) — user access
+- **5432** (postgres) — optional, for direct DB access
+- **6379** (redis) — optional, for debugging
+
+---
+
+
+## 12. Storage & File Management
+
+### 12.1 Directory Structure
+
+```
+/app_data/
+├── images/ # AI-generated images (UUID-named PNG files)
+├── exports/ # Generated PPTX/PDF export files
+├── uploads/ # User-uploaded documents (DOCX, PDF, TXT)
+├── fonts/ # Custom font files
+└── master_decks/ # Master deck PPTX files and screenshots
+ └── {deck_id}/
+ ├── original.pptx
+ ├── screenshots/
+ │ ├── page_1.png
+ │ ├── page_2.png
+ │ └── ...
+ └── pdf/
+ └── deck.pdf
+```
+
+### 12.2 File Serving
+
+| Context | Serving Method |
+|---------|---------------|
+| **Docker (production)** | nginx serves `/app_data/` directly from volume |
+| **Local development** | FastAPI `StaticFiles` mount on `/app_data` |
+| **Frontend access** | Next.js rewrites `/app_data/*` to backend |
+
+### 12.3 Retention Policy
+
+```mermaid
+flowchart LR
+ A[Presentation Created] -->|retention_days exceeded| B[Soft Delete
deleted_at = now]
+ B -->|30 days later| C[Hard Purge
Files removed from disk]
+```
+
+- **Retention days** configured per client in `ClientModel.retention_days`
+- **Soft delete** runs daily at 2:00 AM UTC (sets `deleted_at` timestamp)
+- **Hard purge** runs weekly Monday at 3:00 AM UTC (removes files for items soft-deleted 30+ days ago)
+- **Manual purge** available via Admin > Storage > "Purge Files" button
+
+### 12.4 Disk Space Monitoring
+
+Monitor the `app_data` volume:
+
+```bash
+# Check volume usage
+docker compose exec api du -sh /app_data/*
+
+# Check available disk space
+docker compose exec api df -h /app_data
+```
+
+---
+
+
+## 13. Monitoring & Logging
+
+### 13.1 Log Access
+
+```bash
+# All services
+make logs
+
+# Specific service
+docker compose logs -f api
+docker compose logs -f worker
+docker compose logs -f postgres
+docker compose logs -f web
+
+# Last N lines
+docker compose logs --tail=100 api
+```
+
+### 13.2 Audit Trail
+
+The `AuditMiddleware` automatically logs all mutating API requests:
+
+| Field | Content |
+|-------|---------|
+| `user_id` | Authenticated user's UUID |
+| `action` | Operation name (e.g., "admin_delete") |
+| `resource_type` | Entity type (e.g., "presentation") |
+| `resource_id` | Entity UUID |
+| `client_id` | Tenant context |
+| `details` | JSON with request/response metadata |
+| `ip_address` | Client IP address |
+| `created_at` | Timestamp (indexed for fast queries) |
+
+Audit logging is fire-and-forget (non-blocking) via `asyncio.create_task()`.
+
+### 13.3 AI Usage Tracking
+
+The `AIUsageModel` tracks all LLM API calls:
+
+| Metric | Description |
+|--------|-------------|
+| Provider | anthropic, openai, google, ollama |
+| Model | Specific model ID |
+| Call Type | outline, content, vision, etc. |
+| Input Tokens | Tokens sent to the model |
+| Output Tokens | Tokens received |
+| Duration (ms) | Call latency |
+| Error Details | Error message if failed |
+
+View aggregated metrics at **Admin > Analytics > AI Usage**.
+
+### 13.4 Health Checks
+
+| Service | Method | Interval | Timeout |
+|---------|--------|----------|---------|
+| PostgreSQL | `pg_isready` | 5s | 5s, 5 retries |
+| Redis | `redis-cli ping` | 5s | 5s, 5 retries |
+| API | Lifespan startup | At boot | — |
+| Worker | ARQ health check | 30s | — |
+
+---
+
+## 14. Backup & Recovery
+
+### 14.1 Backup Components
+
+| Component | Location | Strategy |
+|-----------|----------|----------|
+| Database | `postgres_data` volume | `pg_dump` to file |
+| Redis | `redis_data` volume | Optional (transient queue data) |
+| Files | `app_data` volume | File-level backup or snapshot |
+| Settings | Database (KeyValueSqlModel) | Included in pg_dump |
+| Migrations | `backend/migrations/` | In git repository |
+
+### 14.2 Database Backup
+
+```bash
+# Full database dump
+docker compose exec postgres pg_dump -U deckforge deckforge > backup_$(date +%Y%m%d).sql
+
+# Compressed backup
+docker compose exec postgres pg_dump -U deckforge deckforge | gzip > backup_$(date +%Y%m%d).sql.gz
+
+# Restore from backup
+docker compose exec -T postgres psql -U deckforge deckforge < backup.sql
+```
+
+### 14.3 File Backup
+
+```bash
+# Backup app_data volume
+docker run --rm -v ppt-tool_app_data:/data -v $(pwd):/backup alpine \
+ tar czf /backup/app_data_$(date +%Y%m%d).tar.gz -C /data .
+
+# Restore app_data
+docker run --rm -v ppt-tool_app_data:/data -v $(pwd):/backup alpine \
+ tar xzf /backup/app_data_YYYYMMDD.tar.gz -C /data
+```
+
+### 14.4 Disaster Recovery
+
+```mermaid
+flowchart LR
+ A[Deploy Stack] --> B[Restore DB] --> C[alembic upgrade] --> D[Restore app_data] --> E[Start Services] --> F[Verify]
+```
+
+1. Deploy fresh Docker Compose stack
+2. Start PostgreSQL and Redis first
+3. Restore database from latest `pg_dump`
+4. Run `alembic upgrade head` to ensure schema is current
+5. Restore `app_data` volume from backup
+6. Start remaining services
+7. Verify data integrity via Admin Panel
+
+---
+
+
+## 15. Security Hardening
+
+### 15.1 Production Checklist
+
+
+
+Before deploying to production, complete every item:
+
+
+
+| Item | Action | Priority |
+|------|--------|----------|
+| JWT Secret | Change `JWT_SECRET_KEY` to a random 256-bit key | Critical |
+| Dev Auth | Set `AZURE_AD_TENANT_ID` to disable dev bypass | Critical |
+| CORS | Restrict `allow_origins` from `*` to your domain | High |
+| SSL/TLS | Configure nginx with SSL certificates | High |
+| Database Password | Use a strong `POSTGRES_PASSWORD` | High |
+| API Keys | Set `SETTINGS_ENCRYPTION_KEY` for at-rest encryption | High |
+| Port Exposure | Remove host port mappings for postgres/redis | Medium |
+| Secrets | Move `.env` to Docker secrets or vault | Medium |
+| Rate Limiting | Add nginx rate limiting rules | Medium |
+| Monitoring | Set up external monitoring and alerting | Medium |
+
+### 15.2 CORS Configuration
+
+The default CORS configuration allows all origins. For production, modify `backend/api/main.py`:
+
+```python
+origins = [
+ "https://your-domain.com",
+ "https://app.your-domain.com",
+]
+```
+
+### 15.3 API Key Encryption
+
+Enable at-rest encryption for stored API keys:
+
+```bash
+# Generate a Fernet key
+python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
+
+# Add to .env
+SETTINGS_ENCRYPTION_KEY=your-generated-fernet-key
+```
+
+### 15.4 Middleware Execution Order
+
+```mermaid
+flowchart LR
+ A[Request] --> B[1.CORS] --> C[2.Auth] --> D[3.Audit] --> E[4.UserConfig] --> F[Handler]
+```
+
+Middlewares are added in reverse order in FastAPI (last added = first executed).
+
+---
+
+## 16. Scaling & Performance
+
+### 16.1 Horizontal Scaling
+
+```mermaid
+flowchart LR
+ LB[Load Balancer] --> API[API x N]
+ API --> PG[(Managed PostgreSQL)]
+ API --> RD[(Managed Redis)]
+ W[Workers x N] --> PG
+ W --> RD
+ API --> S3[S3 / CDN]
+ W --> S3
+```
+
+| Component | Scaling Strategy |
+|-----------|-----------------|
+| **API** | Run multiple replicas behind load balancer |
+| **Worker** | Run multiple instances (ARQ handles job locking) |
+| **PostgreSQL** | Use managed service (RDS, Cloud SQL) with read replicas |
+| **Redis** | Use managed service (ElastiCache) with clustering |
+| **Files** | Replace local volume with S3 + CDN |
+| **nginx** | Replace with cloud load balancer (ALB, Cloud Load Balancing) |
+
+### 16.2 Vertical Scaling
+
+| Setting | Location | Effect |
+|---------|----------|--------|
+| `max_jobs` | `WorkerSettings` | More concurrent background jobs |
+| `job_timeout` | `WorkerSettings` | Allow longer-running LLM operations |
+| `worker_connections` | `nginx.conf` | More concurrent connections |
+| `client_max_body_size` | `nginx.conf` | Larger file uploads |
+
+### 16.3 Performance Optimization
+
+- **Database indexes** already exist on:
+ - `auditlogmodel.created_at`
+ - `teammembershipmodel(user_id, team_id)` (unique)
+ - `usermodel.email` (unique)
+ - `clientmodel.slug` (unique)
+
+- **Async operations**: All database queries use `asyncpg` (async PostgreSQL driver)
+- **Sync LLM calls**: Wrapped in `asyncio.to_thread()` to avoid blocking the event loop
+- **SSE streaming**: Server-Sent Events for real-time progress (no polling overhead)
+- **Fire-and-forget audit**: Audit logs don't block request processing
+
+---
+
+
+## 17. Troubleshooting
+
+### 17.1 Common Issues
+
+| Issue | Cause | Solution |
+|-------|-------|----------|
+| API won't start | PostgreSQL not ready | Wait for health check (15–25s) |
+| Worker jobs not processing | Redis down or worker crashed | `docker compose restart worker` |
+| Auth failures after restart | JWT_SECRET_KEY changed | All users must re-login |
+| File uploads fail | Disk full or wrong permissions | Check `du -sh /app_data/*` and permissions |
+| PPTX export 500 | Puppeteer / Chromium issue | Restart web service, check memory |
+| Slide edit timeout | LLM response too slow | Check provider status, increase timeouts |
+| Master deck stuck "processing" | Worker died during parse | Restart worker, reparse the deck |
+| Images not showing | Static files not served | Check FastAPI mounts and nginx config |
+| SSE not working | Proxy buffering enabled | Ensure nginx `proxy_buffering off` |
+
+### 17.2 Diagnostic Commands
+
+```bash
+# Service health
+docker compose ps
+
+# Container resource usage
+docker stats
+
+# API application logs
+docker compose logs --tail=50 api
+
+# Worker job processing logs
+docker compose logs --tail=50 worker
+
+# Database connection check
+docker compose exec postgres pg_isready -U deckforge
+
+# Redis connectivity
+docker compose exec redis redis-cli ping
+
+# Database query — check failed jobs
+docker compose exec postgres psql -U deckforge -c \
+ "SELECT id, job_type, status, error_message FROM jobmodel WHERE status='failed' ORDER BY created_at DESC LIMIT 10;"
+
+# Disk usage
+docker compose exec api du -sh /app_data/*
+```
+
+### 17.3 Resetting the System
+
+```bash
+# Full reset (WARNING: destroys all data)
+docker compose down -v
+docker compose up --build -d
+make migrate
+make seed
+```
+
+---
+
+## 18. API Reference
+
+### 18.1 Authentication Endpoints
+
+| Method | Path | Description |
+|--------|------|-------------|
+| GET | `/api/v1/auth/login` | Redirect to Azure AD login |
+| GET | `/api/v1/auth/callback` | OAuth callback handler |
+| POST | `/api/v1/auth/dev-login` | Dev mode authentication |
+| GET | `/api/v1/auth/dev-status` | Check if dev mode is enabled |
+| POST | `/api/v1/auth/logout` | Clear session |
+| GET | `/api/v1/auth/me` | Current user info |
+
+### 18.2 Admin Endpoints
+
+| Method | Path | Access |
+|--------|------|--------|
+| GET/PUT/DELETE | `/api/v1/admin/users/*` | Super Admin |
+| POST/GET/PUT/DELETE | `/api/v1/admin/clients/*` | Admin |
+| POST/GET/DELETE | `/api/v1/admin/teams/*` | Admin |
+| GET/PUT | `/api/v1/admin/settings` | Super Admin |
+| GET/POST | `/api/v1/admin/settings/models` | Super Admin |
+| POST | `/api/v1/admin/settings/test-connection` | Super Admin |
+| GET/DELETE/POST | `/api/v1/admin/storage/*` | Admin |
+| GET | `/api/v1/admin/analytics/*` | Admin |
+| GET | `/api/v1/admin/audit-log` | Admin |
+| GET/PUT/POST/DELETE | `/api/v1/admin/master-decks/*` | Admin |
+| GET/PUT/POST/DELETE | `/api/v1/admin/brand-config/*` | Admin |
+
+### 18.3 Presentation Endpoints
+
+| Method | Path | Description |
+|--------|------|-------------|
+| POST | `/api/v1/ppt/presentation/create` | Create new presentation |
+| GET | `/api/v1/ppt/presentation/all` | List presentations |
+| GET | `/api/v1/ppt/presentation/{id}` | Get presentation detail |
+| PUT | `/api/v1/ppt/presentation/{id}` | Update presentation |
+| DELETE | `/api/v1/ppt/presentation/{id}` | Delete presentation |
+| POST | `/api/v1/ppt/presentation/decompose` | Decompose content |
+| POST | `/api/v1/ppt/presentation/prepare` | Prepare for generation |
+| GET | `/api/v1/ppt/presentation/{id}/review` | Get review status |
+| PUT | `/api/v1/ppt/presentation/{id}/status` | Change review status |
+| POST | `/api/v1/ppt/presentation/{id}/comment` | Add review comment |
+| POST | `/api/v1/ppt/jobs/generate` | Start generation job |
+| GET | `/api/v1/ppt/jobs/{id}/status` | Job status (SSE) |
+| POST | `/api/v1/ppt/export/pptx` | Export as PPTX |
+| POST | `/api/v1/ppt/export/pdf` | Export as PDF |
+
+---
+
+
+
+**Oliver DeckForge** | System Administration Guide
+
+Version 1.0 | © 2026 All Rights Reserved
+
+
diff --git a/docs/02-System-Administration-Guide.pdf b/docs/02-System-Administration-Guide.pdf
new file mode 100644
index 0000000..7aa71ba
Binary files /dev/null and b/docs/02-System-Administration-Guide.pdf differ
diff --git a/docs/build-pdfs.mjs b/docs/build-pdfs.mjs
new file mode 100644
index 0000000..7c3c8c7
--- /dev/null
+++ b/docs/build-pdfs.mjs
@@ -0,0 +1,62 @@
+#!/usr/bin/env node
+/**
+ * Build script for Oliver DeckForge documentation PDFs.
+ *
+ * 1. Renders Mermaid diagrams to PNG via @mermaid-js/mermaid-cli (mmdc)
+ * 2. Converts the resulting Markdown (with images) to styled PDFs via md-to-pdf
+ * 3. Cleans up intermediate files
+ *
+ * Usage: node docs/build-pdfs.mjs
+ */
+
+import { execSync } from 'child_process';
+import { readdirSync, unlinkSync, existsSync } from 'fs';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+
+const sources = [
+ '01-User-Manual.md',
+ '02-System-Administration-Guide.md',
+];
+
+for (const file of sources) {
+ const input = join(__dirname, file);
+ const rendered = join(__dirname, file.replace('.md', '-rendered.md'));
+ const renderedPdf = rendered.replace('.md', '.pdf');
+ const finalPdf = join(__dirname, file.replace('.md', '.pdf'));
+
+ console.log(`\n=== ${file} ===`);
+
+ // 1. Pre-render Mermaid diagrams → PNG, output modified markdown
+ console.log(' [1/3] Rendering Mermaid diagrams...');
+ execSync(
+ `npx -y @mermaid-js/mermaid-cli -i "${input}" -o "${rendered}" -e png --scale 2`,
+ { cwd: __dirname, stdio: 'inherit' },
+ );
+
+ // 2. Convert rendered markdown → PDF
+ console.log(' [2/3] Converting to PDF...');
+ execSync(`npx -y md-to-pdf "${rendered}"`, {
+ cwd: __dirname,
+ stdio: 'inherit',
+ });
+
+ // 3. Move PDF to final name and clean up temp files
+ console.log(' [3/3] Cleaning up...');
+ execSync(`mv "${renderedPdf}" "${finalPdf}"`);
+
+ // Remove rendered markdown
+ if (existsSync(rendered)) unlinkSync(rendered);
+
+ // Remove generated diagram PNGs
+ const prefix = file.replace('.md', '-rendered-');
+ readdirSync(__dirname)
+ .filter((f) => f.startsWith(prefix) && f.endsWith('.png'))
+ .forEach((f) => unlinkSync(join(__dirname, f)));
+
+ console.log(` -> ${file.replace('.md', '.pdf')} created`);
+}
+
+console.log('\nDone.');