# ManaMail — Module Plan ## Status (2026-04-13) Phase 1 Code ist committed. Service + Frontend-Modul existieren, aber der Service läuft noch nicht in Production. ### Offene Punkte — Phase 1 Betrieb - [ ] **Fritz!Box Port-Forwarding** — Ports 25, 587, 465 (TCP) → Mac Mini (192.168.178.131) für eingehende Mails - [ ] **Stalwart JMAP-Port verifizieren** — `http://mana-mail:8080` im Docker-Netzwerk erreichbar? Ggf. Port 8080 in `docker-compose.macmini.yml` zum Stalwart-Container hinzufügen - [ ] **Docker-Compose Production** — `mana-mail-service` Container zu `docker-compose.macmini.yml` hinzufügen (Port 3042, depends_on: postgres + mana-mail) - [ ] **Cloudflare Tunnel** — Route `mail-api.mana.how` → `http://mana-mail-service:3042` in `cloudflared-config.yml` - [ ] **Env-Vars Production** — `STALWART_JMAP_URL`, `STALWART_ADMIN_PASSWORD`, `MANA_MAIL_URL` in `.env.macmini` setzen - [ ] **DB Schema pushen** — `./scripts/setup-databases.sh mail` auf Production ausführen - [ ] **Frontend URL** — `PUBLIC_MANA_MAIL_URL=https://mail-api.mana.how` in Production-Env für die Mana Web App - [ ] **Testen** — Account-Provisioning bei User-Registrierung, Thread-Abfrage, Mail-Senden ### Offene Punkte — Phase 2 KI-Features - [ ] Thread-Zusammenfassungen via `mana-llm` (summary → `mail.thread_metadata`) - [ ] Smart Reply — 3 KI-generierte Antwortvorschläge pro Thread - [ ] Auto-Kategorisierung — `important` / `newsletter` / `social` / `todo` - [ ] Aktions-Extraktion — Termine, Aufgaben, Rechnungen aus Mails erkennen - [ ] SSE-Stream `/api/v1/mail/live` für Echtzeit-Benachrichtigungen ### Offene Punkte — Phase 3 Multi-Account - [ ] Gmail OAuth + Gmail API Integration - [ ] Outlook OAuth + Microsoft Graph Integration - [ ] Generisches IMAP/SMTP für andere Provider - [ ] Unified Inbox über alle Accounts ### Offene Punkte — Phase 4 Erweitert - [ ] "Später senden" (Scheduled Send) - [ ] "Erinnere mich" (Snooze) - [ ] Mail-Templates mit Variablen - [ ] Mail-Regeln (Auto-Label, Auto-Forward) - [ ] Cross-Module-Linking: Mail → Todo, Mail → Kalender, Mail → Kontakte, Mail → Finance --- ## Motivation Mana hat bereits eine vollständige Mail-Infrastruktur (Stalwart, mana-notify, DNS/DKIM/SPF), die bisher nur für Transaktionsmails genutzt wird. Ein Mail-Modul macht `@mana.how`-Adressen für jeden User möglich und schafft einen integrierten Mail-Client mit KI-Features — tief verknüpft mit Todo, Kalender, Kontakte und anderen Modulen. ## Bestehendes | Komponente | Status | Details | |---|---|---| | Stalwart Mail Server | Produktiv | Rust, Container `mana-mail`, Ports 25/587/465/993/8443 | | DNS (MX, SPF, DKIM, DMARC) | Konfiguriert | `mail.mana.how` → 194.191.241.139 | | mana-notify | Produktiv | Go, Port 3013, SMTP-Gateway mit Retry/Queue | | Mailpit (Dev) | Konfiguriert | Port 1025/8025 für lokale Entwicklung | | Stalwart Admin API | Verfügbar | Account-CRUD, DKIM-Management | | Inbound Port-Forwarding | **TODO** | Fritz!Box Ports 25/587/465 → Mac Mini | ### Stalwart JMAP-Support Stalwart unterstützt **JMAP** (RFC 8620) nativ — ein modernes, REST-basiertes Mail-Protokoll. Vorteile gegenüber IMAP: - JSON über HTTP (kein kompliziertes IMAP-State-Management) - Push-Benachrichtigungen bei neuen Mails (EventSource) - Effizientes Delta-Sync (nur Änderungen seit letztem State) - Batch-Requests (mehrere Operationen in einem Call) - Attachment-Upload via Blob-Store **JMAP wird der primäre Zugriffspfad** für den mana-mail Service. --- ## Architektur ``` Browser (Mana Web) ↓ REST / SSE mana-mail Service (Hono/Bun, Port 3042) ↓ JMAP (HTTP) ↓ SMTP (Port 587) Stalwart Stalwart (Lesen/Sync) (Senden) ↓ Internet ← MX: mail.mana.how ``` ### Warum ein eigener Service (nicht direkt JMAP vom Browser)? 1. **Credentials** — JMAP-Auth-Credentials dürfen nicht im Browser landen 2. **KI-Processing** — Zusammenfassungen, Kategorisierung laufen serverseitig 3. **Cross-Module-Linking** — Server kann Mails mit Todo/Kalender/Kontakte verknüpfen 4. **Rate-Limiting & Caching** — Server cached Thread-Listen, limitiert API-Calls 5. **Account-Provisioning** — Stalwart-Account-Erstellung bei User-Registrierung --- ## Phase 1: Fundament (MVP) ### 1.1 Service: `services/mana-mail/` **Stack:** Hono/Bun, Drizzle ORM, PostgreSQL (`mana_platform.mail` Schema) **Port:** 3042 ``` services/mana-mail/ ├── src/ │ ├── index.ts # Bootstrap + Route-Mounting │ ├── config.ts # Env-Vars laden │ ├── routes/ │ │ ├── threads.ts # GET /threads, GET /threads/:id │ │ ├── messages.ts # PUT /messages/:id (read/star/archive) │ │ ├── send.ts # POST /send, POST /draft │ │ ├── labels.ts # GET /labels, POST /labels │ │ ├── accounts.ts # GET /accounts (user's mail accounts) │ │ └── internal.ts # POST /internal/on-user-created │ ├── services/ │ │ ├── jmap-client.ts # JMAP-Verbindung zu Stalwart │ │ ├── mail-service.ts # Business-Logik (Thread-Aufbau, Suche) │ │ ├── send-service.ts # SMTP-Senden via Stalwart │ │ └── account-service.ts# Stalwart Account-Provisioning │ ├── middleware/ │ │ └── jwt-auth.ts # JWT-Validierung (wie mana-credits) │ └── db/ │ └── schema/ │ └── mail.ts # Drizzle Schema (pgSchema('mail')) ├── package.json ├── tsconfig.json ├── CLAUDE.md └── Dockerfile ``` ### 1.2 Datenbank-Schema (`mana_platform`, Schema `mail`) ```sql -- User-Mailbox-Einstellungen (nicht die Mails selbst — die leben in Stalwart) mail.accounts ( id UUID PK, user_id TEXT NOT NULL REFERENCES auth.users(id), email TEXT NOT NULL UNIQUE, -- z.B. till@mana.how display_name TEXT, provider TEXT DEFAULT 'stalwart', -- Phase 2: 'gmail', 'outlook' is_default BOOLEAN DEFAULT true, signature TEXT, -- HTML-Signatur created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ ) -- Lokaler Label-Cache (Stalwart Labels + User-eigene) mail.labels ( id UUID PK, account_id UUID REFERENCES mail.accounts(id), stalwart_id TEXT, -- JMAP mailboxId name TEXT NOT NULL, color TEXT, type TEXT DEFAULT 'user', -- 'system' | 'user' sort_order INT DEFAULT 0 ) -- KI-generierte Metadaten pro Thread (Cache) mail.thread_metadata ( id UUID PK, account_id UUID REFERENCES mail.accounts(id), thread_id TEXT NOT NULL, -- JMAP threadId summary TEXT, -- KI-Zusammenfassung category TEXT, -- 'important'|'newsletter'|'social'|'todo' sentiment TEXT, -- 'positive'|'neutral'|'negative' linked_items JSONB, -- [{appId, recordId}] Cross-Module-Links created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, UNIQUE(account_id, thread_id) ) ``` **Keine Mail-Inhalte in der DB** — Mails leben in Stalwart, der Service ist ein Proxy/Cache. ### 1.3 API-Endpunkte **User-Endpoints (JWT-Auth):** | Method | Path | Beschreibung | |---|---|---| | `GET` | `/api/v1/mail/threads` | Thread-Liste (paginiert, Filter nach Label/Unread) | | `GET` | `/api/v1/mail/threads/:id` | Thread mit allen Messages | | `PUT` | `/api/v1/mail/messages/:id` | Status ändern (read/unread, star, archive, label) | | `POST` | `/api/v1/mail/send` | Mail senden (oder Reply) | | `POST` | `/api/v1/mail/draft` | Entwurf speichern | | `DELETE` | `/api/v1/mail/draft/:id` | Entwurf löschen | | `GET` | `/api/v1/mail/labels` | Labels abrufen | | `POST` | `/api/v1/mail/labels` | Label erstellen | | `GET` | `/api/v1/mail/accounts` | User's Mail-Accounts | | `PUT` | `/api/v1/mail/accounts/:id` | Account-Einstellungen (Signatur etc.) | | `GET` | `/api/v1/mail/live` | SSE-Stream für neue Mails (JMAP EventSource) | **Service-Endpoints (X-Service-Key):** | Method | Path | Beschreibung | |---|---|---| | `POST` | `/api/v1/internal/mail/on-user-created` | Account in Stalwart anlegen | | `POST` | `/api/v1/internal/mail/on-user-deleted` | Account in Stalwart deaktivieren | ### 1.4 JMAP-Client ```typescript // services/mana-mail/src/services/jmap-client.ts class JmapClient { constructor(private baseUrl: string, private adminToken: string) {} // Authentifizierung: Service nutzt Admin-Token, scoped auf User-Account async getThreads(accountId: string, opts: { limit?: number; position?: number; filter?: { inMailbox?: string; isUnread?: boolean }; sort?: { property: string; isAscending: boolean }[]; }): Promise async getThread(accountId: string, threadId: string): Promise async getEmails(accountId: string, emailIds: string[]): Promise async setEmailFlags(accountId: string, emailId: string, flags: { isRead?: boolean; isFlagged?: boolean; mailboxIds?: Record; }): Promise async sendEmail(accountId: string, email: { to: Address[]; cc?: Address[]; bcc?: Address[]; subject: string; body: string; htmlBody?: string; inReplyTo?: string; references?: string[]; attachments?: Blob[]; }): Promise // returns emailId async subscribe(accountId: string, onNewEmail: (email: Email) => void): EventSource } ``` ### 1.5 Account-Provisioning (bei User-Registrierung) In `mana-auth/src/routes/auth.ts` ergänzen (fire-and-forget Pattern): ```typescript // Nach erfolgreicher Registrierung: fetch(`${config.manaMailUrl}/api/v1/internal/mail/on-user-created`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Service-Key': config.serviceKey }, body: JSON.stringify({ userId: response.user.id, email: body.email, name: body.name, }), }).catch(() => {}); ``` `mana-mail` erstellt dann: 1. Stalwart-Account via Admin-API (`POST /api/principal`) 2. `mail.accounts` Eintrag in DB 3. System-Labels (Inbox, Sent, Drafts, Trash, Spam, Archive) **Adress-Vergabe:** - Default: `username@mana.how` (aus Auth-Username) - Fallback bei Kollision: `username123@mana.how` - Aliases möglich: `vorname.nachname@mana.how` ### 1.6 Frontend-Modul: `modules/mail/` ``` modules/mail/ ├── module.config.ts # appId: 'mail', tables: [mailCache, mailDrafts] ├── types.ts # Thread, Message, Label, MailAccount ├── collections.ts # Dexie-Cache-Tabellen ├── queries.ts # useThreads(), useThread(), useLabels() ├── stores/ │ ├── mail.svelte.ts # send, reply, markRead, star, archive, moveToLabel │ └── drafts.svelte.ts # saveDraft, deleteDraft (local-first) ├── api.ts # fetchWithAuth Wrapper für mana-mail Service ├── ListView.svelte # Inbox-Ansicht (Thread-Liste) ├── views/ │ ├── ThreadView.svelte # Thread-Detail mit Message-Liste │ └── ComposeView.svelte # Neue Mail / Reply schreiben └── index.ts ``` **Dexie-Cache (local-first für Offline):** ``` // database.ts v9 mailCache: 'id, threadId, accountId, date, isRead, isFlagged, [accountId+date]' mailDrafts: 'id, accountId, replyToId' ``` - `mailCache` speichert die letzten ~500 Thread-Headers für Offline-Zugriff - `mailDrafts` speichert Entwürfe lokal (wie alle Module, encrypted) - Encryption: `title` (subject), `snippet`, `body` in mailCache; `to`, `subject`, `body` in mailDrafts **ListView.svelte (Inbox):** - Thread-Liste mit Absender, Betreff, Snippet, Datum, Unread-Badge - Filter: Inbox / Sent / Drafts / Archive / Labels - Suche über Betreff + Absender - Pull-to-refresh / SSE für Live-Updates - Swipe-Gesten: Links = Archivieren, Rechts = Markieren **ThreadView.svelte (Detail):** - Chronologische Message-Liste im Thread - Reply/Reply-All/Forward Buttons - Attachment-Anzeige + Download - "Als Aufgabe erstellen" → Todo-Modul - "Termin erstellen" → Kalender-Modul **ComposeView.svelte (Schreiben):** - To/Cc/Bcc mit Kontakte-Autocomplete - Rich-Text-Editor (oder Markdown) - Attachment-Upload via mana-media - Signatur automatisch anhängen - Entwurf-Auto-Save (local-first in Dexie) --- ## Phase 2: KI-Features ### 2.1 Thread-Zusammenfassungen - Bei jedem neuen Thread: `mana-llm` oder `local-llm` erstellt Summary - Gespeichert in `mail.thread_metadata.summary` - Im ListView als Tooltip oder aufklappbarer Abschnitt ### 2.2 Smart Reply - 3 Antwort-Vorschläge basierend auf Thread-Kontext - Generiert via `mana-llm` API - One-Click-Send oder als Basis für eigene Antwort ### 2.3 Auto-Kategorisierung - Neue Mails werden automatisch kategorisiert: `important` / `newsletter` / `social` / `todo` - Basis: Absender-Reputation, Betreff-Analyse, Inhalt - User kann Kategorisierung korrigieren (→ lernt pro User) ### 2.4 Aktions-Extraktion - KI erkennt: "Meeting am Donnerstag um 14 Uhr" → Kalender-Vorschlag - "Bitte bis Freitag erledigen" → Todo-Vorschlag - "Rechnung anbei" → Finance-Modul Vorschlag - Angezeigt als Action-Chips unter der Mail --- ## Phase 3: Multi-Account ### 3.1 Externe Accounts - Gmail via Google OAuth + Gmail API - Outlook via Microsoft OAuth + Microsoft Graph - Generisches IMAP/SMTP für andere Provider ### 3.2 Unified Inbox - Alle Accounts in einer Thread-Liste - Account-Badge pro Thread (welcher Account) - Antwort automatisch vom richtigen Account --- ## Phase 4: Erweitert ### 4.1 Scheduling - "Später senden" (Mail in Queue mit Zeitstempel) - "Erinnere mich" (Snooze — Mail verschwindet und taucht zu Zeitpunkt wieder auf) ### 4.2 Templates - Häufige Antworten als wiederverwendbare Templates - Variablen-Platzhalter: `{{name}}`, `{{datum}}` ### 4.3 Mail-Regeln - Automatische Label-Zuweisung basierend auf Absender/Betreff - Auto-Forward, Auto-Archive - Integration mit `automations` Modul --- ## Infrastruktur-Voraussetzungen ### Sofort nötig (vor Phase 1) - [ ] Fritz!Box Port-Forwarding: 25, 587, 465 → Mac Mini (192.168.178.131) - [ ] Stalwart JMAP-Port (8080 intern) im Docker-Netzwerk verfügbar machen - [ ] Port 3042 in `PORT_SCHEMA.md` reservieren - [ ] `MANA_MAIL_URL` zu `.env.development` hinzufügen ### Docker-Compose Erweiterung ```yaml mana-mail: build: ./services/mana-mail container_name: mana-mail-service ports: - "3042:3042" environment: PORT: 3042 DATABASE_URL: postgresql://mana:${DB_PASSWORD}@postgres:5432/mana_platform MANA_AUTH_URL: http://mana-auth:3001 MANA_SERVICE_KEY: ${MANA_SERVICE_KEY} STALWART_JMAP_URL: http://mana-mail:8080 STALWART_ADMIN_USER: admin STALWART_ADMIN_PASSWORD: ${STALWART_ADMIN_PASSWORD} CORS_ORIGINS: http://localhost:5173,https://mana.how depends_on: - postgres - mana-mail # Stalwart container networks: - mana-network ``` --- ## Cross-Module-Integration | Aktion | Source | Target | Mechanismus | |---|---|---|---| | Mail → Aufgabe | mail | todo | `manaLinks` + `tasksStore.createTask()` | | Mail → Termin | mail | calendar | `manaLinks` + Datums-Extraktion | | Mail → Kontakt | mail | contacts | Absender-Matching / Auto-Create | | Mail → Notiz | mail | notes | Inhalt kopieren + `manaLinks` | | Mail → Datei | mail | storage | Attachment in MinIO ablegen | | Mail → Rechnung | mail | finance | KI-Erkennung "Rechnung" | | Kontakt → Mail | contacts | mail | "E-Mail senden" Button | | Todo → Mail | todo | mail | "Per Mail teilen" | --- ## Implementierungs-Reihenfolge 1. **Infra**: Port-Forwarding, JMAP-Port, Port reservieren 2. **Service**: `mana-mail` scaffolden (Hono/Bun, Config, Health) 3. **JMAP-Client**: Stalwart-Verbindung, Thread/Email-Queries 4. **Account-Provisioning**: on-user-created Hook in mana-auth 5. **API**: Thread-Liste, Thread-Detail, Send, Draft 6. **Frontend**: Modul-Gerüst (types, collections, queries, stores) 7. **ListView**: Inbox-UI mit Thread-Liste 8. **ThreadView**: Detail-Ansicht mit Messages 9. **ComposeView**: Schreiben/Reply mit Kontakte-Autocomplete 10. **SSE**: Live-Updates für neue Mails 11. **KI Phase 2**: Zusammenfassungen, Smart Reply, Kategorisierung 12. **Multi-Account Phase 3**: Gmail/Outlook OAuth