# 🤖 JARVIS - Übergabe Dokumentation **Projekt:** JARVIS KI-Assistent + Business Automation **Erstes Deployment:** 12.09.2026 **Status:** ✅ Live auf VPS 72.61.186.98 (Postgres, Redis, Ollama, n8n, JARVIS-API, JARVIS-Web) **Owner:** Jonny (MBO-Tech-IT) --- ## 📋 Übersicht JARVIS ist ein **vollständig selbst gehosteter AI-Assistant + Business Automation Stack** auf einem VPS mit Docker-Orchestrierung. Chat-Zugriff läuft über eine eigene Web-Oberfläche (React) und spricht über Claude Tool Use direkt mit Kalender und E-Mail-Postfach. **Kernkomponenten:** - 🤖 **KI-Backend**: Claude API (Chat + Tool Use) + Ollama (lokale Embeddings) - 💾 **Datenbank**: PostgreSQL mit `pgvector`-Extension (strukturierte Daten + Vector Search in einer DB, kein separates Milvus) - ⚡ **Cache**: Redis (aktuell nur fürs Health-Check vorbereitet, noch nicht aktiv genutzt) - 🔄 **Automation**: n8n (Workflow Engine) - Wetter- und E-Mail-Cache laufen bereits darüber - 📡 **API**: FastAPI (Python Backend, `main.py`) - 🌐 **Web-Frontend**: React/Vite, nginx-served - 🔌 **Externe Integrationen**: Nextcloud-Kalender (CalDAV), E-Mail-Postfach (IMAP/SMTP) - 🌐 **Reverse Proxy**: bereits vorhandener, JARVIS-fremder Traefik auf dem VPS (siehe unten) - 🐳 **Container**: 6 Docker-Container (siehe "Docker Container Status") --- ## 🔑 Zugriffsdaten ``` VPS IP: 72.61.186.98 User: jarvis-core SSH Key: ~/.ssh/jarvis_core_key Domain: jarvis.mbo-tech-it.de Location: /home/jarvis-core/jarvis ``` ### SSH-Zugriff ```bash ssh -F /dev/null -o IdentitiesOnly=yes -i ~/.ssh/jarvis_core_key jarvis-core@72.61.186.98 ``` Die Flags `-F /dev/null -o IdentitiesOnly=yes` sind nötig, damit die globale lokale `~/.ssh/config` nicht in die Schlüsselauswahl reinpfuscht. **Wichtig:** `jarvis-core` muss in `/etc/ssh/sshd_config` unter `AllowUsers` stehen, sonst wird jeder Key mit "Permission denied" abgelehnt, obwohl der Key korrekt ist (passiert am 12.09.2026, gefixt durch Ergaenzen von `jarvis-core` in der `AllowUsers`-Zeile + `systemctl reload ssh`). ### n8n-Zugriff ``` URL: https://n8n.jarvis.mbo-tech-it.de User: jonny@mbo-tech-it.de (Owner-Account, Passwort nicht in dieser Doku) ``` API-Keys fuer die n8n-REST-API werden unter Settings -> n8n API verwaltet (pro Key eigenes Ablaufdatum). Aktuell existieren: "JARVIS Weather Workflow" (Ablauf 12.10.2026) und "JARVIS Email Workflow Runtime" (Ablauf 13.10.2026, fuer den E-Mail-Cache-Workflow angelegt). Die Key-Werte selbst sind nirgends dauerhaft gespeichert (nur beim Erstellen einmalig sichtbar) - bei Bedarf einfach einen neuen erstellen. ### Database Credentials ``` PostgreSQL User: jarvis PostgreSQL Database: jarvis (n8n hat eine eigene DB "n8n" in derselben Instanz) PostgreSQL Port: 5432 Password: in .env (DB_PASSWORD) ``` ### Externe Dienste (Zugangsdaten in `.env` auf dem VPS) - **Nextcloud-Kalender** (`NEXTCLOUD_CALDAV_URL`, `NEXTCLOUD_USER`, `NEXTCLOUD_APP_PASSWORD`): `https://cloud.ffw-onza.de/.../ffw-onza-alle/`, App-Passwort-Auth - **E-Mail-Postfach** (`EMAIL_USER`, `EMAIL_PASSWORD`, `EMAIL_IMAP_HOST/PORT`, `EMAIL_SMTP_HOST/PORT`): `kontakt@mbo-tech-it.de` auf `mx2f35.netcup.net` (IMAP 143 STARTTLS, SMTP 465 implizites TLS) - **Claude API** (`CLAUDE_API_KEY`): console.anthropic.com, separat vom Claude-Pro-Abo --- ## 📍 Services & Ports | Service | URL | Port | Status | Notes | |---------|-----|------|--------|-------| | **Web-Frontend** | https://jarvis.mbo-tech-it.de | 80 (intern, nginx) | 🟢 Live | Chat + Dashboard, Shared-Secret-Login mit `API_KEY_ADMIN` | | **API** | https://api.jarvis.mbo-tech-it.de | 8000 (intern) | 🟢 Live | FastAPI Swagger UI: `/docs`. Alle `/api/v1/*` Routen ausser `/health` brauchen Header `X-Admin-Key` | | **n8n** | https://n8n.jarvis.mbo-tech-it.de | 5678 (intern) | 🟢 Live | Workflow Automation - 2 aktive Workflows (Wetter, E-Mail-Cache) | | **Traefik** | - | 80/443 | 🟢 Genutzt (fremd) | Server hat bereits einen eigenen Traefik fuer andere Projekte. JARVIS-eigener Traefik-Service wurde entfernt (Port-Konflikt), stattdessen haengt JARVIS per Labels + externem Netzwerk `proxy-network` am bestehenden Traefik. TLS ueber Let's-Encrypt-Resolver `netcup` (DNS-01) | | **PostgreSQL** | localhost | 5432 | 🟢 Live | Hauptdatenbank + Vector Search, Image `pgvector/pgvector:pg16` | | **Redis** | localhost | 6379 | 🟢 Live | Läuft und wird im Health-Check geprüft, im Code aber noch nicht aktiv fürs Caching genutzt | | **Ollama** | http://72.61.186.98:11434 | 11434 | 🟢 Live | Local LLM Runtime, genutzt für Embeddings (`nomic-embed-text` gepullt) | | **Milvus** | - | - | ⛔ Nie deployt | Ersetzt durch `pgvector` direkt in Postgres (siehe Phase 3a). Platzhalter-Services `milvus`/`etcd` stehen noch in `docker-compose.yml`, laufen aber nicht | **Traefik-Integration (12.09.2026):** JARVIS ist am bestehenden VPS-weiten Traefik (`docker-compose.yml` Projekt `traefik`, `/data/docker/compose/traefik/`) angehaengt: externes Docker-Netzwerk `proxy-network`, Labels an `jarvis-api`, `jarvis-web` und `n8n` (`traefik.docker.network=proxy-network`, `entrypoints=websecure`, `tls.certresolver=netcup`). DNS (`jarvis` A-Record + `*.jarvis` Wildcard) liegt bei Netcup. Falls eine neue Subdomain (z.B. `foo.jarvis.mbo-tech-it.de`) mal nicht auflöst, obwohl der Wildcard existiert: pruefen, ob ein expliziter (auch leerer) Eintrag exakt fuer diesen Namen existiert - der blockiert laut DNS-Wildcard-Regeln (RFC 1034) die Wildcard-Aufloesung nur fuer diesen einen Namen. **Beobachtet (12.09.2026):** Auch ohne sichtbaren blockierenden Eintrag im Netcup-Panel kann die Aufloesung eines frisch fuer eine DNS-01-Challenge verwendeten Namens (z.B. `api.jarvis...`) deutlich laenger brauchen als ein unberuehrter Wildcard-Name (bis zu ~20 Minuten beobachtet, vermutlich Netcup-interner Nebeneffekt der ACME-TXT-Record-Erstellung) - einfach abwarten, es loest sich von selbst. --- ## 🏗️ Architektur ``` ┌───────────────────────────────────────────────────────────┐ │ Bestehender, JARVIS-fremder Traefik │ │ (VPS-weit, andere Projekte inklusive) │ └───────────────────────────────────────────────────────────┘ │ proxy-network (Labels) ┌───────────┼──────────────┬──────────────────┐ ▼ ▼ ▼ ┌─────────┐ ┌──────────┐ ┌───────────┐ │jarvis-web│ │jarvis-api│ │ n8n │ │(nginx) │ │(FastAPI) │ │ :5678 │ └─────────┘ └────┬─────┘ └─────┬─────┘ │ jarvis-net (intern) │ ┌───────────┼───────────┐ │ ▼ ▼ ▼ │ ┌───────────┐ ┌───────┐ ┌────────┐ │ │ Postgres │ │ Redis │ │ Ollama │ │ │ +pgvector │ │ :6379 │ │ :11434 │ │ │ :5432 │ └───────┘ └────────┘ │ └─────┬─────┘ │ └─── n8n schreibt Cache-Tabellen ─────┘ (weather_cache, email_cache) Externe Integrationen (kein eigener Container, direkter API-Zugriff aus jarvis-api): - Nextcloud CalDAV (Kalender lesen/schreiben) - IMAP/SMTP-Postfach kontakt@mbo-tech-it.de (E-Mails lesen/senden) - Claude API (Chat + Tool Use) ``` --- ## 🐳 Docker Container Status ```bash cd /home/jarvis-core/jarvis docker compose ps # Erwartete Ausgabe (6 Container): # jarvis-postgres Up (healthy) # jarvis-redis Up (healthy) # jarvis-ollama Up # jarvis-n8n Up # jarvis-api Up # jarvis-web Up ``` `milvus`/`etcd` stehen zwar noch als Service-Definitionen in `docker-compose.yml` (Platzhalter, nie funktionsfähig konfiguriert), werden aber mit `docker compose up -d` normalerweise nicht mit hochgezogen, wenn man gezielt einzelne Services neu startet (`docker compose up -d jarvis-api` etc., so wie es in diesem Projekt durchgängig gemacht wird statt einem globalen `up -d`). --- ## 📁 Projektstruktur ``` /home/jarvis-core/jarvis/ ├── docker-compose.yml # Container-Orchestrierung (Docker Compose v2 - "docker compose", kein Bindestrich) ├── .env # Umgebungsvariablen (GEHEIM!) ├── init-db.sql # PostgreSQL Basis-Initialisierung (users/conversations/messages/tasks/documents/audit_logs) ├── migrations/ # Nachträgliche Schema-Änderungen, manuell per psql angewendet │ ├── 002_document_chunks.sql │ ├── 003_drop_low_data_ivfflat_index.sql │ ├── 004_weather_cache.sql │ └── 005_email_cache.sql ├── api/ │ ├── main.py # FastAPI Hauptanwendung (einzige Backend-Datei) │ └── requirements.txt # Python Dependencies ├── web/ # React/Vite Frontend-Quellcode + Dockerfile + nginx.conf ├── config/ # (ungenutzt) ├── data/ # (ungenutzt - Postgres/Redis/n8n/Ollama persistieren tatsächlich über benannte Docker-Volumes, siehe docker-compose.yml `volumes:`) └── logs/ # (ungenutzt, kein Service schreibt aktuell hierhin) ``` Lokal (Entwicklung, dieses Repo): Backend-Code liegt unter `Claude outputs/`, Frontend unter `web/`. Kein dediziertes Git-Repo für dieses Projekt - Dateien werden per `scp` direkt auf den VPS deployt. --- ## 🔧 Wichtige Befehle **Hinweis:** Die installierte Docker-Compose-Version ist v2 (Plugin-Syntax `docker compose`, ohne Bindestrich) - das alte `docker-compose` (mit Bindestrich) ist auf dem VPS nicht garantiert vorhanden. ### Container Management ```bash cd /home/jarvis-core/jarvis # Alle starten docker compose up -d # Alle stoppen docker compose down # Logs anschauen docker compose logs -f # Spezifischen Service neustarten (Backend-Codeänderung reicht ein restart, # da main.py per Volume gemountet ist und uvicorn mit --reload läuft) docker compose restart jarvis-api # Frontend-Änderung braucht einen echten Rebuild (Multi-Stage-Dockerfile) docker compose build jarvis-web && docker compose up -d jarvis-web # In Container gehen docker compose exec jarvis-api bash docker compose exec postgres bash ``` ### Database Management ```bash # PostgreSQL CLI öffnen docker exec -it jarvis-postgres psql -U jarvis -d jarvis # Query ausführen docker exec jarvis-postgres psql -U jarvis -d jarvis -c "SELECT * FROM users;" # Migration anwenden (nach scp der .sql-Datei nach migrations/) docker exec -i jarvis-postgres psql -U jarvis -d jarvis < migrations/00X_name.sql # Backup erstellen docker exec jarvis-postgres pg_dump -U jarvis jarvis > jarvis_backup.sql # Backup wiederherstellen docker exec -i jarvis-postgres psql -U jarvis jarvis < jarvis_backup.sql ``` ### API Testing ```bash # Health Check (kein Header noetig) curl http://localhost:8000/health # Admin Stats (Header noetig) curl -H "X-Admin-Key: $API_KEY_ADMIN" http://localhost:8000/api/v1/admin/stats # Swagger UI im Browser https://api.jarvis.mbo-tech-it.de/docs ``` ### Backend-Tests lokal ausführen (vor jedem Deploy) Lokales Python (3.14) kann `psycopg2-binary` nicht bauen (kein Wheel) - Tests laufen deshalb in einem `python:3.11-slim`-Container per SSH: ```bash scp -F /dev/null -o IdentitiesOnly=yes -i ~/.ssh/jarvis_core_key \ "Claude outputs/main.py" "Claude outputs/requirements.txt" \ "Claude outputs/requirements-dev.txt" "Claude outputs/pytest.ini" \ jarvis-core@72.61.186.98:/tmp/jarvis-test/ scp -F /dev/null -o IdentitiesOnly=yes -i ~/.ssh/jarvis_core_key \ "Claude outputs/tests/"*.py jarvis-core@72.61.186.98:/tmp/jarvis-test/tests/ ssh -F /dev/null -o IdentitiesOnly=yes -i ~/.ssh/jarvis_core_key jarvis-core@72.61.186.98 \ "docker run --rm -v /tmp/jarvis-test:/app -w /app python:3.11-slim bash -c \ 'pip install -q -r requirements-dev.txt -r requirements.txt && python -m pytest tests/ -v'" ``` --- ## ⚙️ Konfiguration (.env, auf dem VPS unter `/home/jarvis-core/jarvis/.env`) ``` # Domain DOMAIN=jarvis.mbo-tech-it.de # Database DB_PASSWORD= # Claude / Ollama CLAUDE_API_KEY= CLAUDE_MODEL=claude-sonnet-5 # (Default in main.py, muss nicht in .env stehen) OLLAMA_EMBED_MODEL=nomic-embed-text # (Default in main.py) # Admin-Zugriff (Shared Secret fürs Frontend UND alle /api/v1/* Routen) API_KEY_ADMIN= # Nextcloud-Kalender NEXTCLOUD_APP_PASSWORD= # E-Mail-Postfach kontakt@mbo-tech-it.de EMAIL_PASSWORD= LOG_LEVEL=info ``` **⚠️ WICHTIG:** `.env` enthält Secrets - nicht in Git committen (für dieses Projekt ohnehin kein Git-Repo im Einsatz)! --- ## 🔌 Datenbankschema ### PostgreSQL Tables (Datenbank `jarvis`) **users** (`init-db.sql`) - Felder: id, username, email, password_hash, api_key, role, is_active - Es gibt kein echtes Login-System pro Nutzer (siehe "Security" unten) - die einzige vorhandene Zeile ist ein Seed-User `admin@jarvis.local` aus `init-db.sql`. `ensure_default_user()` in `main.py` nimmt schlicht den ersten User per ID als `DEFAULT_USER_ID` für alle Chat-Aktivität (in der Praxis also dieser `admin`-Seed-User, nicht ein separat angelegter "jarvis-service"-User, obwohl der Code-Pfad dafür existiert) **conversations** / **messages** (`init-db.sql`) - Chat-Verlauf, Multi-Turn über `conversation_id` - `messages.tokens_used` trackt Claude-Token-Verbrauch pro Antwort **tasks** (`init-db.sql`) - Tabelle existiert, wird aber von den `/api/v1/tasks`-Endpoints noch NICHT genutzt (die sind aktuell Stubs mit hartcodierten Werten, siehe "API Endpoints" unten) - offener Punkt für Phase 4b **documents** + **document_chunks** (`init-db.sql` + `migrations/002_document_chunks.sql`) - Knowledge Base fürs RAG: `documents` hält Titel/Volltext, `document_chunks` die 768-dim `pgvector`-Embeddings (Ollama `nomic-embed-text`) pro Chunk - `ivfflat`-Index wurde wieder entfernt (`003_drop_low_data_ivfflat_index.sql`), da er bei wenig Daten 0 Treffer lieferte - aktuell Sequential Scan **weather_cache** (`migrations/004_weather_cache.sql`) - Wird ausschließlich vom n8n-Workflow "Wetter Crailsheim" befüllt (Cache, kein Live-API-Call aus `main.py`) **email_cache** (`migrations/005_email_cache.sql`) - Wird ausschließlich vom n8n-Workflow "Neue E-Mails Cache" befüllt (Absender/Betreff/Empfangsdatum, keine Volltexte). Der Chat greift für Live-Anfragen separat direkt per IMAP zu, nicht über diesen Cache **audit_logs** (`init-db.sql`) - Tabelle existiert, wird aber aktuell von keinem Code-Pfad beschrieben (vorbereitet für spätere Compliance-Anforderungen) ### Redis - Läuft und wird im Health-Check geprüft (`check_redis()`), im Code aber noch nirgends für Sessions/Caching/Queues genutzt - reiner Platzhalter für spätere Erweiterung --- ## 🚀 API Endpoints Alle `/api/v1/*`-Routen (ausser keine - `/health` ist die einzige offene Route) verlangen den Header `X-Admin-Key: `. ### Health ``` GET /health # Oeffentlich, kein Header noetig GET /api/v1/admin/stats # Conversations/Tasks/Documents-Zaehler GET /api/v1/admin/health/detailed # Postgres/Redis/Milvus(TCP-Check)/Ollama/n8n ``` ### Chat (Claude, mit Tool Use für Kalender + E-Mail) ``` POST /api/v1/chat Body: {"conversation_id": 1, "message": "...", "context": {}} -> {"conversation_id", "response", "tokens_used", "timestamp"} GET /api/v1/conversations/{id} # Voller Nachrichtenverlauf ``` Claude kann in `run_chat_completion()` bis zu `MAX_TOOL_ROUNDS = 5` Tool-Runden hintereinander ausführen: `list_calendar_events`, `create_calendar_event`, `update_calendar_event`, `delete_calendar_event` (Löschen nur nach expliziter Chat-Bestätigung), `list_recent_emails`, `send_email` (Senden nur nach expliziter Chat-Bestätigung). ### Kalender ``` GET /api/v1/calendar/events?days=14 # Naechste Termine (Nextcloud CalDAV) ``` ### Wetter ``` GET /api/v1/weather # Neuester Cache-Eintrag (503 wenn leer) ``` ### E-Mail ``` GET /api/v1/emails?limit=10 # Neueste gecachte Mails ([] wenn leer, kein 503) ``` ### Tasks (⚠️ noch nicht mit Postgres verbunden - TODO) ``` POST /api/v1/tasks # Stub: gibt immer id=1 zurueck, speichert nichts GET /api/v1/tasks?status=pending # Stub: gibt immer leere Liste zurueck ``` ### Documents (Knowledge Base) ``` POST /api/v1/documents?title=...&content=...&document_type=... # Chunking + Ollama-Embedding + Insert GET /api/v1/documents?query=...&limit=10 # Vector-Similarity-Suche ueber document_chunks ``` ### Workflows (⚠️ noch nicht mit n8n verbunden - TODO) ``` POST /api/v1/workflows/trigger # Stub: ruft n8n nicht wirklich auf ``` --- ## 🔐 Security Es gibt **kein** vollwertiges Auth-System mit Login pro Nutzer. Stattdessen: - Frontend-Login = ein geteiltes Secret (`API_KEY_ADMIN`), das im Browser in `localStorage` liegt und bei jedem Request als `X-Admin-Key`-Header mitgeschickt wird (`require_admin_key`-Dependency in `main.py`) - Der `admin`-User in der `users`-Tabelle (aus `init-db.sql`) ist nur ein DB-Seed für die `user_id`-Fremdschluessel, kein aktives Login - das Feld `password_hash` wird von keinem Code-Pfad geprüft - Für ein echtes Multi-User-System (eigene Logins, Rollen) wäre eine separate Architektur-Runde nötig - bewusst "Out of Scope" laut Frontend-Spec ### Secrets (alle in `.env` auf dem VPS, nicht im Repo) - **API_KEY_ADMIN**: Shared Secret fürs Frontend-Login + alle geschützten API-Routen - **DB_PASSWORD**: Postgres-Passwort - **CLAUDE_API_KEY**: Anthropic API Key - **NEXTCLOUD_APP_PASSWORD**: Nextcloud CalDAV App-Passwort - **EMAIL_PASSWORD**: IMAP/SMTP-Passwort für kontakt@mbo-tech-it.de --- ## 📊 Monitoring & Logs ```bash # Real-time Logs docker compose logs -f # Logs für spezifischen Service docker compose logs -f jarvis-api # Alte Logs anschauen docker logs --tail 100 jarvis-api # System Resources docker stats ``` --- ## 🛠️ Troubleshooting ### Container startet nicht ```bash docker compose logs jarvis-api docker compose restart jarvis-api docker compose down && docker compose up -d ``` ### Database Connection Error ```bash docker exec jarvis-postgres pg_isready -U jarvis docker exec jarvis-postgres psql -U jarvis -d jarvis -c "SELECT 1" ``` ### API antwortet nicht ```bash docker compose ps curl -v http://localhost:8000/health docker compose logs jarvis-api ``` ### Frontend zeigt eine neue Aenderung nicht an, obwohl deployt `web/nginx.conf` setzt `index.html` auf `no-cache` und `/assets/*` auf `immutable` - trotzdem: harter Reload (Strg+Shift+R) probieren, bevor man tiefer sucht. Ursache war einmal ein fehlender Cache-Header (siehe Phase 4d). ### n8n-Workflow läuft nicht wie erwartet Executions-Tab des jeweiligen Workflows in der n8n-UI ansehen (zeigt Input/ Output pro Node) - schneller als Logs raten. --- ## 📈 Entwicklungsverlauf (chronologisch) ### Phase 1: Grundsetup ✅ - [x] Docker Stack deployed, alle Kern-Services laufen, Datenbank initialisiert, API verfügbar ### Phase 2: KI-Integration ✅ - [x] Claude API Key eingetragen (console.anthropic.com, separat vom Claude Pro Abo) - [x] Claude Integration in API (Multi-Turn ueber `conversation_id`) - [x] Token-Tracking implementiert (`tokens_used` pro Message + Response) - [x] Postgres-Persistenz fuer Conversations/Messages - [x] Echte Health-Checks (Postgres, Redis, Ollama, n8n; Milvus als TCP-Check vorbereitet) ### Phase 3a: Knowledge Base ✅ - [x] pgvector-Extension in Postgres (Image gewechselt auf `pgvector/pgvector:pg16`) - [x] `document_chunks`-Tabelle (768-dim Embeddings, Chunking 1000/100 Zeichen Overlap) - [x] Embeddings via Ollama `nomic-embed-text` (lokal, keine externen Kosten) - [x] `POST/GET /api/v1/documents` funktionsfaehig - [x] Bug gefunden+gefixt: `ivfflat`-Index lieferte bei wenig Daten 0 Treffer - Index vorerst entfernt, Sequential Scan aktiv - [ ] Follow-up: `ivfflat`/`hnsw`-Index neu anlegen, sobald genug Dokumente vorhanden sind ### Phase 3b: Frontend ✅ - [x] React/Vite Web UI (`jarvis-web` Container, nginx-served) unter https://jarvis.mbo-tech-it.de - [x] Shared-Secret-Login (wiederverwendet `API_KEY_ADMIN`, Header `X-Admin-Key`) - [x] Chat Interface (Multi-Turn) + Dashboard (Stats + Health-Tabelle) - [x] API auf eigene Subdomain umgezogen: https://api.jarvis.mbo-tech-it.de - [x] Bug gefixt (13.09.2026): Chat verlor Verlauf bei Tab-Wechsel/Reload, da `conversationId` nur im React-State lag. Fix: `conversationId` in `localStorage`, Verlauf wird beim Mounten via `GET /api/v1/conversations/{id}` wiederhergestellt (`web/src/components/Chat.tsx`) - [ ] Vollwertiges User-/Auth-System (Login pro Nutzer, Rollen) - weiterhin offen ### Phase 4a: Wetter-Widget ✅ (erster echter n8n-Workflow) - [x] n8n Owner-Account eingerichtet (`jonny@mbo-tech-it.de`) - [x] n8n-Workflow "Wetter Crailsheim" (id `BwNCJ2TkuZfqUzst`): Schedule-Trigger (alle 30 Min) -> Open-Meteo API (kein Key noetig) -> Code-Node (WMO-Code -> deutscher Text) -> Postgres-Insert in `weather_cache` - [x] n8n-Credential "JARVIS Postgres" (zeigt auf dieselbe `jarvis`-DB wie die API) - [x] `GET /api/v1/weather` liest die neueste Zeile - [x] `WeatherWidget` oben rechts in der Frontend-Nav-Leiste ### Phase 4d: Nextcloud-Kalender-Integration ✅ - [x] CalDAV-Zugriff auf `https://cloud.ffw-onza.de/.../ffw-onza-alle/` via App-Passwort, Python-Library `caldav` - [x] `GET /api/v1/calendar/events` + "Naechste Termine"-Widget im Dashboard - [x] Chat kann Kalender abfragen, Termine anlegen und verschieben (direkt, ohne Rueckfrage) sowie loeschen (nur nach Bestaetigung im Chat) - Claude Tool Use mit `list_calendar_events` / `create_calendar_event` / `update_calendar_event` / `delete_calendar_event` - [x] Bug gefixt (13.09.2026): `run_chat_completion` unterstuetzte nur eine Tool-Runde - "Termin verschieben" braucht aber zwei (erst uid per `list_calendar_events` finden, dann `update_calendar_event`), die zweite Runde wurde still verworfen (leere Antwort). Jetzt eine echte Schleife (`MAX_TOOL_ROUNDS = 5`) - [x] Bug gefixt: neu angelegte Termine landeten als UTC statt Europe/Berlin (2h Verschiebung) - naive Datumswerte werden jetzt explizit lokalisiert (`_as_calendar_local`) - [x] Bug gefixt: `web/nginx.conf` hatte keine Cache-Header - `index.html` wurde vom Browser gecacht, wodurch ein Deploy im Browser nicht ankam, obwohl der Server bereits den neuen Build auslieferte. Jetzt: `index.html` = `no-cache`, `/assets/*` = `immutable` ### Phase 4e: Sprach-Ein-/Ausgabe im Chat ✅ - [x] Mikrofon-Button (🎤) nutzt die Browser-eigene Web Speech API (`SpeechRecognition`, `de-DE`) - reine Frontend-Loesung, blendet sich selbst aus, wenn der Browser das nicht unterstuetzt (v.a. Firefox) - [x] Assistant-Antworten werden automatisch per `SpeechSynthesis` vorgelesen, Standard "an"; Mute-Button (🔊/🔇) merkt sich Zustand in `localStorage` (`jarvis_speech_muted`) - [x] Neues Modul `web/src/speech.ts` buendelt beide APIs (nach Vorbild von `api.ts`) - [x] Bug gefixt (13.09.2026): `continuous=false` beendete die Erkennung bei jeder Sprechpause. Jetzt `continuous=true` mit automatischem Neustart bei Chrome's internem Session-Timeout (akkumuliert Text ueber Neustarts hinweg, damit nichts verloren geht) - [x] Sprachausgabe filtert vor dem Vorlesen: Emojis (inkl. Zahlen-Emoji wie 1⃣2⃣3⃣), Markdown-Formatierung (`**fett**`, `#Ueberschriften`) und dekorative Trennlinien/Tabellenzeichen (`---`, `═══`, `|`) - siehe `cleanForSpeech()` in `web/src/speech.ts` ### Phase 4f: E-Mail-Integration ✅ - [x] Postfach `kontakt@mbo-tech-it.de` (IMAP `mx2f35.netcup.net:143` STARTTLS, SMTP `mx2f35.netcup.net:465` implizites TLS) via Python-Standardbibliothek (`imaplib`, `smtplib`, `email`) angebunden - keine neue Dependency - [x] Chat-Tools `list_recent_emails` (liest live per IMAP, `BODY.PEEK` damit der Lese-Status nicht veraendert wird, RFC-2047-Betreffs dekodiert) und `send_email` (SMTP_SSL) in `run_chat_completion()`, analog zum Kalender. `send_email` verlangt wie `delete_calendar_event` immer erst eine Bestaetigung im Chat, bevor tatsaechlich versendet wird - [x] n8n-Workflow "Neue E-Mails Cache" (id `VaAQdw18ElYWkEfr`, aktiv): eingebauter `Email Trigger (IMAP)`-Node (`postProcessAction: "nothing"`, damit das Cachen den echten Lese-Status im Postfach nicht veraendert) -> Postgres-Insert in `email_cache` (nur Metadaten, keine Volltexte) - [x] `GET /api/v1/emails` liest die neuesten Zeilen; leerer Cache liefert `[]` statt 503 (anders als beim Wetter kein Fehlerzustand) - [x] `EmailWidget` ("Neue E-Mails") im Dashboard, nach demselben Muster wie `CalendarWidget`/`WeatherWidget` - [x] Live end-to-end getestet: Chat hat echte Inbox-Mails aufgelistet, Testmail per Chat mit Bestaetigungsdialog versendet und im Postfach verifiziert, n8n-Workflow hat dieselbe Mail in `email_cache` erfasst, Dashboard-Widget zeigt sie an - [x] Kleiner Fix waehrend der Verifikation: von JARVIS selbst versendete Mails hatten keinen `Date`-Header (smtplib/EmailMessage setzt den nicht automatisch) - `_send_email_sync` setzt jetzt `Date` und `Message-ID` - Out of Scope (siehe Spec): Anhaenge, Antworten/Weiterleiten, Loeschen/ Verschieben von Mails, mehrere Postfaecher/Ordner ### Phase 4b: Weitere Automation ⏳ (offen) - [ ] `POST/GET /api/v1/tasks` an die vorhandene `tasks`-Tabelle anbinden (aktuell Stub) - [ ] `POST /api/v1/workflows/trigger` tatsaechlich an die n8n-API anbinden (aktuell Stub) - [ ] Weitere n8n Business-Workflows definieren (Kundenbericht-Automation o.ä.) ### Phase 5: Production ⏳ (offen) - [ ] Backup-Strategie (Postgres-Dumps automatisieren, aktuell nur manuell per `pg_dump`) - [ ] Monitoring & Alerts (aktuell nur manuelles Ansehen von `/api/v1/admin/health/detailed`) - [ ] Sicherheits-Hardening / echtes Multi-User-Auth-System --- ## 📞 Kontakt & Support **Owner:** Jonny (Markus) **Email:** jonny@mbo-tech-it.de **Company:** MBO-Tech-IT **Location:** Crailsheim, Baden-Württemberg --- ## 📚 Dokumentation & Links - [FastAPI Docs](https://api.jarvis.mbo-tech-it.de/docs) - [n8n Docs](https://docs.n8n.io) - [PostgreSQL Docs](https://www.postgresql.org/docs/) - [pgvector Docs](https://github.com/pgvector/pgvector) - [Claude API Docs](https://docs.anthropic.com) - [Docker Docs](https://docs.docker.com) - [Traefik Docs](https://doc.traefik.io) Spezifikationen und Implementierungspläne aller Phasen liegen lokal unter `docs/superpowers/specs/` und `docs/superpowers/plans/`. --- ## 🎯 Quick Reference ### Start JARVIS ```bash cd /home/jarvis-core/jarvis docker compose up -d ``` ### Check Status ```bash docker compose ps curl http://localhost:8000/health ``` ### View Logs ```bash docker compose logs -f ``` ### Backend-Code aktualisieren ```bash scp -F /dev/null -o IdentitiesOnly=yes -i ~/.ssh/jarvis_core_key \ "Claude outputs/main.py" jarvis-core@72.61.186.98:/home/jarvis-core/jarvis/api/main.py ssh -F /dev/null -o IdentitiesOnly=yes -i ~/.ssh/jarvis_core_key jarvis-core@72.61.186.98 \ "cd /home/jarvis-core/jarvis && docker compose restart jarvis-api" ``` ### Frontend-Code aktualisieren ```bash scp -F /dev/null -o IdentitiesOnly=yes -i ~/.ssh/jarvis_core_key -r \ web/src jarvis-core@72.61.186.98:/home/jarvis-core/jarvis/web/ ssh -F /dev/null -o IdentitiesOnly=yes -i ~/.ssh/jarvis_core_key jarvis-core@72.61.186.98 \ "cd /home/jarvis-core/jarvis && docker compose build jarvis-web && docker compose up -d jarvis-web" ``` ### Backup Database ```bash docker exec jarvis-postgres pg_dump -U jarvis jarvis > backup.sql ``` --- **🎉 JARVIS ist bereit!** *Letzte Aktualisierung: 13.09.2026* *Deployment Status: ✅ LIVE*