---
pdf_options:
  format: A4
  margin: 25mm 20mm
  headerTemplate: '<div style="font-size:8px;color:#999;width:100%;text-align:right;margin-right:20mm;">Oliver DeckForge &mdash; System Administration Guide</div>'
  footerTemplate: '<div style="font-size:8px;color:#999;width:100%;text-align:center;"><span class="pageNumber"></span> / <span class="totalPages"></span></div>'
  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 <repository-url>
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`

<div class="danger">

**Security Warning:** Development mode should never be used in production. Always configure Azure AD or another SSO provider for production deployments.

</div>

### 4.4 JWT Configuration

| Parameter | Value |
|-----------|-------|
| Algorithm | HS256 |
| Expiry | 24 hours |
| Storage | `session_token` HTTP cookie |
| Payload | `sub` (user UUID), `email`, `role`, `exp`, `iat` |

<div class="warning">

**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.

</div>

---


## 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`

<div class="warning">

You cannot change your own role. This prevents accidental lockout.

</div>

#### 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

<div class="tip">

After deleting layouts, the system automatically re-registers the template by recreating `PresentationLayoutCodeModel` records for the remaining layouts.

</div>

### 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
```

<div class="warning">

Always review auto-generated migrations before applying. SQLAlchemy may miss rename operations (interpreting them as drop + create) or produce incorrect defaults.

</div>

### 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<br/>deleted_at = now]
    B -->|30 days later| C[Hard Purge<br/>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

<div class="danger">

Before deploying to production, complete every item:

</div>

| 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 |

---

<div style="text-align: center; margin-top: 60px; color: #999;">

**Oliver DeckForge** | System Administration Guide

Version 1.0 | &copy; 2026 All Rights Reserved

</div>
