jarvis-assist/Claude outputs/JARVIS_HANDOFF.md

667 lines
28 KiB
Markdown

# 🤖 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=<generiert>
# Claude / Ollama
CLAUDE_API_KEY=<gesetzt>
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=<generiert>
# Nextcloud-Kalender
NEXTCLOUD_APP_PASSWORD=<gesetzt>
# E-Mail-Postfach kontakt@mbo-tech-it.de
EMAIL_PASSWORD=<gesetzt>
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: <API_KEY_ADMIN>`.
### 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*