# Architecture Overview

```mermaid
graph LR
    %% ── External ──────────────────────────────────────────────────────────
    NF([novelfire.net])
    KK([Kokoro-FastAPI TTS])
    LE([Let's Encrypt])
    CL([Browser / iOS App])

    %% ── Init containers ───────────────────────────────────────────────────
    subgraph INIT["Init containers (one-shot)"]
        MI[minio-init\nmc: create buckets]
        PI[pb-init\nbootstrap collections]
    end

    %% ── Storage ───────────────────────────────────────────────────────────
    subgraph STORAGE["Storage"]
        MN[(MinIO :9000\nchapters · audio\navatars · browse)]
        PB[(PocketBase :8090\nbooks · chapters_idx\naudio_cache · progress\nscrape_jobs · app_users · ranking)]
        VK[(Valkey :6379\npresign URL cache\nTTL-based · shared)]
        MS[(Meilisearch :7700\nindex: books)]
    end

    %% ── Application ───────────────────────────────────────────────────────
    subgraph APP["Application"]
        CD["Caddy :443/:80\ncustom build + caddy-ratelimit\nauto-HTTPS · security headers\nrate limiting · error pages"]
        BE[Backend API :8080\nGo HTTP server]
        RN[Runner :9091\nGo background worker\n/metrics endpoint]
        UI[SvelteKit UI :3000\nadapter-node]
    end

    %% ── Ops ───────────────────────────────────────────────────────────────
    subgraph OPS["Ops"]
        WT[Watchtower\npolls every 5 min\nautopull + redeploy\nbackend · runner · ui]
    end

    %% ── Init → Storage ────────────────────────────────────────────────────
    MI -.->|create buckets| MN
    PI -.->|bootstrap schema| PB

    %% ── App → Storage ─────────────────────────────────────────────────────
    BE -->|blobs| MN
    BE -->|structured records| PB
    BE -->|cache presigned URLs| VK
    RN -->|chapter markdown & audio| MN
    RN -->|read/update jobs & books| PB
    RN -->|index books on scrape| MS
    UI -->|read presign cache| VK
    UI -->|auth · progress · comments| PB

    %% ── App internal ──────────────────────────────────────────────────────
    UI -->|"REST API (server-side)\n/api/catalogue /api/book-preview\n/api/chapter-text /api/audio"| BE

    %% ── Caddy routing ─────────────────────────────────────────────────────
    CD -->|"/* catch-all\n/api/scrape/*\n/api/chapter-text-preview/*\n→ SvelteKit (auth enforced)"| UI
    CD -->|"/health /scrape*\n/api/browse /api/book-preview/*\n/api/chapter-text/*\n/api/reindex/* /api/cover/*\n/api/audio-proxy/*"| BE
    CD -->|/avatars/* presigned GETs| MN

    %% ── Runner → External ─────────────────────────────────────────────────
    RN -->|scrape HTTP GET| NF
    RN -->|TTS HTTP POST| KK
    CD -->|ACME certificate| LE

    %% ── Ops ───────────────────────────────────────────────────────────────
    WT -->|watch label-enabled| BE
    WT -->|watch label-enabled| RN
    WT -->|watch label-enabled| UI

    %% ── Client ────────────────────────────────────────────────────────────
    CL -->|HTTPS :443 single entry| CD
```
