fix(mana-auth) + chore: rewrite /api/v1/auth/login JWT mint, remove Matrix stack

This commit bundles two unrelated changes that were swept together by an
accidental `git add -A` in another working session. Documented here so the
history reflects what's actually inside.

═══════════════════════════════════════════════════════════════════════
1. fix(mana-auth): /api/v1/auth/login mints JWT via auth.handler instead
   of api.signInEmail
═══════════════════════════════════════════════════════════════════════

Previous attempt (commit 55cc75e7d) tried to fix the broken JWT mint in
/api/v1/auth/login by switching the cookie name from `mana.session_token`
to `__Secure-mana.session_token` for production. That was necessary but
not sufficient: Better Auth's session cookie value isn't just the raw
session token, it's `<token>.<HMAC>` where the HMAC is derived from the
better-auth secret. Reconstructing the cookie from auth.api.signInEmail's
JSON response only gave us the raw token, so /api/auth/token's
get-session middleware still couldn't validate it and the JWT mint kept
silently failing.

Real fix: do the sign-in via auth.handler (the HTTP path) rather than
auth.api.signInEmail (the SDK path). The handler returns a real fetch
Response with a Set-Cookie header containing the fully signed cookie
envelope. We capture that header verbatim and forward it as the cookie
on the /api/auth/token request, which now passes validation and mints
the JWT correctly.

Verified end-to-end on auth.mana.how:

  $ curl -X POST https://auth.mana.how/api/v1/auth/login \
      -d '{"email":"...","password":"..."}'
  {
    "user": {...},
    "token": "<session token>",
    "accessToken": "eyJhbGciOiJFZERTQSI...",   ← real JWT now
    "refreshToken": "<session token>"
  }

Side benefits:
- Email-not-verified path is now handled by checking
  signInResponse.status === 403 directly, no more catching APIError
  with the comment-noted async-stream footgun.
- X-Forwarded-For is forwarded explicitly so Better Auth's rate limiter
  and our security log see the real client IP.
- The leftover catch block now only handles unexpected exceptions
  (network errors etc); the FORBIDDEN-checking logic in it is dead but
  harmless and left in for defense in depth.

═══════════════════════════════════════════════════════════════════════
2. chore: remove the entire self-hosted Matrix stack (Synapse, Element,
   Manalink, mana-matrix-bot)
═══════════════════════════════════════════════════════════════════════

The Matrix subsystem ran parallel to the main Mana product without any
load-bearing integration: the unified web app never imported matrix-js-sdk,
the chat module uses mana-sync (local-first), and mana-matrix-bot's
plugins duplicated features the unified app already ships natively.
Keeping it alive cost a Synapse + Element + matrix-web + bot container
quartet, three Cloudflare routes, an OIDC provider plugin in mana-auth,
and a steady drip of devlog/dependency churn.

Removed:
- apps/matrix (Manalink web + mobile, ~150 files)
- services/mana-matrix-bot (Go bot with ~20 plugins)
- docker/matrix configs (Synapse + Element)
- synapse/element-web/matrix-web/mana-matrix-bot services in
  docker-compose.macmini.yml
- matrix.mana.how/element.mana.how/link.mana.how Cloudflare tunnel routes
- OIDC provider plugin + matrix-synapse trustedClient + matrixUserLinks
  table from mana-auth (oauth_* schema definitions also removed)
- MatrixService import path in mana-media (importFromMatrix endpoint)
- Matrix notification channel in mana-notify (worker, metrics, config,
  channel_type enum, MatrixOptions handler)
- Matrix entries from shared-branding (mana-apps + app-icons),
  notify-client, the i18n bundle, the observatory map, the credits
  app-label list, the landing footer/apps page, the prometheus + alerts
  + promtail tier mappings, and the matrix-related deploy paths in
  cd-macmini.yml + ci.yml

Devlog/manascore/blueprint entries that mention Matrix are left intact
as historical record. The oauth_* + matrix_user_links Postgres tables
stay on existing prod databases — code can no longer write to them, drop
them in a follow-up migration if you want them gone for real.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-08 16:25:55 +02:00
parent 4eb5dfe4a0
commit 8e8b6ac65f
254 changed files with 88 additions and 29437 deletions

View file

@ -61,8 +61,7 @@ Cloudflare Tunnel (bb0ea86d...)
├── Apps (Web + API): chat.mana.how → localhost:5010
├── Services: auth.mana.how → localhost:3001
├── Landing Pages: it.mana.how → localhost:4400 (Nginx)
├── Monitoring: grafana.mana.how → localhost:8000
└── Matrix: matrix.mana.how → localhost:4000
└── Monitoring: grafana.mana.how → localhost:8000
```
**Nginx Landing Container** (`mana-infra-landings`, Port 4400):
@ -119,9 +118,6 @@ Cloudflare Tunnel (bb0ea86d...)
| Domain | Service | Port |
|--------|---------|------|
| `matrix.mana.how` | Matrix Synapse | 4000 |
| `element.mana.how` | Element Web | 4080 |
| `link.mana.how` | Matrix Link | 4090 |
| `grafana.mana.how` | Grafana | 8000 |
| `stats.mana.how` | Umami Analytics | 8010 |
| `glitchtip.mana.how` | GlitchTip Errors | 8020 |

View file

@ -211,14 +211,6 @@ playground.mana.how {
reverse_proxy 10.0.0.2:5090
}
matrix.mana.how {
reverse_proxy 10.0.0.2:4000
}
element.mana.how {
reverse_proxy 10.0.0.2:4080
}
grafana.mana.how {
reverse_proxy 10.0.0.2:8000
}

View file

@ -93,8 +93,6 @@ Cloudflare Tunnel (cloudflared)
| Todo | https://todo.mana.how |
| Calendar | https://calendar.mana.how |
| Clock | https://clock.mana.how |
| Matrix (Synapse) | https://matrix.mana.how |
| Element Web | https://element.mana.how |
## SSH-Zugang
@ -411,8 +409,6 @@ curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage"
| mana-calendar-web | Calendar Frontend |
| mana-clock-backend | Clock API |
| mana-clock-web | Clock Frontend |
| mana-synapse | Matrix Homeserver |
| mana-element | Element Web Client |
### Nützliche Docker-Befehle
@ -530,7 +526,6 @@ curl -s http://localhost:3000/
The health check monitors:
- All backend APIs and web frontends
- Infrastructure (PostgreSQL, Redis)
- Matrix services (Synapse, Element, all bots)
- Monitoring stack (Grafana, Umami, GlitchTip, VictoriaMetrics)
- Alerting stack (vmalert, Alertmanager, Alert Notifier)
- Disk space for `/` and `/Volumes/ManaData` (warning at 80%, critical at 90%)
@ -553,11 +548,11 @@ ssh mana-server "PATH=/Applications/Docker.app/Contents/Resources/bin:\$PATH &&
Wenn ein Service im Health-Check als `HTTP 000` erscheint und `docker ps -a` den Container nicht zeigt, wurde er vermutlich beim letzten Deploy übersprungen:
```bash
# Container erstellen und starten (Beispiel: Project Doc Bot)
docker compose -f docker-compose.macmini.yml up -d matrix-project-doc-bot
# Container erstellen und starten
docker compose -f docker-compose.macmini.yml up -d <service-name>
# Nach Restart prüfen
docker ps --filter name=mana-matrix-bot-projectdoc --format '{{.Names}} {{.Status}}'
docker ps --filter name=mana-<service> --format '{{.Names}} {{.Status}}'
```
## Wartung
@ -642,7 +637,6 @@ Alle 63 Container haben explizite `mem_limit` in `docker-compose.macmini.yml`:
| Core (Hono/Bun) | 5 | 704 MB |
| Go Services | 5 | 384 MB |
| Other Backend | 3 | 576 MB |
| Matrix | 4 | 784 MB |
| Web Apps | 20 | 2.560 MB |
| LLM | 2 | 384 MB |
| Monitoring | 14 | 1.792 MB |
@ -742,8 +736,7 @@ Die externe SSD wird für persistente Daten verwendet - sowohl für große Datei
├── backups/ # PostgreSQL Backups (täglich 3:00)
├── ollama/ # LLM Modelle (~58 GB)
├── flux2/ # FLUX.2 Bildgenerierung (~15 GB)
├── stt-models/ # Speech-to-Text Modelle (~19 GB)
└── matrix/ # Matrix Synapse Daten
└── stt-models/ # Speech-to-Text Modelle (~19 GB)
```
### Docker auf externer SSD
@ -823,81 +816,6 @@ Docker Desktop benötigt "Full Disk Access" für SSD-Mounts:
Systemeinstellungen → Datenschutz & Sicherheit → Voller Festplattenzugriff → Docker.app ✅
```
## Matrix (DSGVO-konformes Messaging)
Matrix ist eine DSGVO-konforme Alternative zu Telegram für Bot-Kommunikation.
### Komponenten
| Service | Port | Beschreibung |
|---------|------|--------------|
| Synapse | 8008 | Matrix Homeserver |
| Element Web | 8087 | Web-Client |
### Matrix Bots
Alle Matrix Bots laufen als Docker Container und werden via GHCR (GitHub Container Registry) deployed. Watchtower aktualisiert sie automatisch bei neuen Images.
| Bot | Port | Beschreibung |
|-----|------|--------------|
| matrix-mana-bot | 4010 | Gateway - alle Features in einem Bot |
| matrix-ollama-bot | 4011 | KI-Chat via GPU-Server Ollama |
| matrix-stats-bot | 4012 | Server-Statistiken & Monitoring |
| matrix-project-doc-bot | 4013 | Projekt-Dokumentation aus Fotos/Voice/Text |
| matrix-todo-bot | 4014 | Aufgabenverwaltung |
| matrix-calendar-bot | 4015 | Termine & Events |
| matrix-nutriphi-bot | 4016 | Ernährungstracking |
| matrix-zitare-bot | 4017 | Tägliche Zitate |
| matrix-clock-bot | 4018 | Timer & Wecker |
| matrix-tts-bot | 4019 | Text-to-Speech |
**Health Checks:**
```bash
# Alle Bots prüfen
for port in 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019; do
echo -n "Port $port: "
curl -s http://localhost:$port/health | jq -r '.status // "error"'
done
```
**Logs:**
```bash
# Logs eines Bots
docker logs matrix-mana-bot -f
# Alle Matrix Bots
docker ps | grep matrix-.*-bot
```
**Bot neu starten:**
```bash
docker compose -f docker-compose.macmini.yml restart matrix-mana-bot
```
**Images manuell aktualisieren:**
```bash
docker compose -f docker-compose.macmini.yml pull matrix-mana-bot
docker compose -f docker-compose.macmini.yml up -d matrix-mana-bot
```
### Setup
```bash
# Matrix initialisieren
./scripts/mac-mini/setup-matrix.sh
# Services starten
docker compose -f docker-compose.macmini.yml up -d synapse element-web
# Admin-User erstellen
docker exec -it mana-synapse register_new_matrix_user \
-c /data/homeserver.yaml http://localhost:8008 -a
```
### Dokumentation
Siehe [MATRIX_SELF_HOSTING.md](./MATRIX_SELF_HOSTING.md) für detaillierte Anleitung.
## Chronologie der Einrichtung
1. **Docker Setup** - PostgreSQL, Redis, App-Container
@ -909,5 +827,4 @@ Siehe [MATRIX_SELF_HOSTING.md](./MATRIX_SELF_HOSTING.md) für detaillierte Anlei
7. **Email Notifications** - Redundante Benachrichtigung
8. ~~**Ollama** - Lokale LLM-Inferenz~~ → Migriert auf GPU-Server (2026-03-28)
9. ~~**Telegram Ollama Bot**~~ → Deaktiviert (2026-03-28)
10. **Matrix Synapse** - DSGVO-konformes Messaging
11. **GPU-Server Offload** - Alle AI-Workloads auf RTX 3090 (2026-03-28)
10. **GPU-Server Offload** - Alle AI-Workloads auf RTX 3090 (2026-03-28)

View file

@ -1,990 +0,0 @@
# Mana Matrix Bot Architecture
**Status:** Production
**Datum:** 1. Februar 2026
**Autor:** Till Schneider
**Letzte Aktualisierung:** 1. Februar 2026
---
## Executive Summary
Mana setzt auf **Matrix** als primäre Messaging-Plattform für Bot-Interaktionen. Mit 19 spezialisierten Matrix-Bots und einem Gateway-Bot bieten wir eine vollständig dezentrale, DSGVO-konforme Alternative zu Cloud-basierten Chat-Diensten.
**Kernprinzipien:**
- **Volle Kontrolle** - Eigene Infrastruktur, eigene Daten
- **DSGVO-Konformität** - Alle Daten auf eigenen Servern
- **Unabhängigkeit** - Keine Abhängigkeit von Drittanbieter-Plattformen
- **Einheitliche UX** - Konsistente Erfahrung über alle Bots
---
## 1. Warum Matrix?
### 1.1 Die Entscheidung gegen Telegram/Discord/Slack
Bei der Wahl der Messaging-Plattform für Mana hatten wir mehrere Optionen:
| Plattform | Vorteile | Nachteile |
|-----------|----------|-----------|
| **Telegram** | Große Reichweite, einfache API | Zentral, Daten bei Telegram, keine Kontrolle über UX |
| **Discord** | Gaming-Community, Webhooks | US-basiert, DSGVO-Bedenken, Werbung |
| **Slack** | Business-Standard | Teuer, Vendor Lock-in, keine Self-Hosting Option |
| **Matrix** | Dezentral, Self-Hosted, E2E-Verschlüsselung | Kleinere Community, mehr Setup-Aufwand |
**Unsere Entscheidung:** Matrix bietet die einzige Möglichkeit, eine **vollständig unabhängige** Plattform zu betreiben mit:
- Voller Kontrolle über Nutzerdaten
- Eigener UI/UX (Element, eigene Clients)
- End-to-End-Verschlüsselung
- Federation für Inter-Server-Kommunikation
### 1.2 Matrix Grundkonzepte
```
┌─────────────────────────────────────────────────────────────────┐
│ Matrix Ökosystem │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Homeserver │<───>│ Homeserver │ Federation │
│ │ (mana.how) │ │ (matrix.org) │ │
│ └────────┬────────┘ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Räume ││
│ ├─────────────────────────────────────────────────────────────┤│
│ │ !abc:mana.how │ Bot-Interaktion (1:1) ││
│ │ !xyz:mana.how │ Gruppen-Chat (Multi-User) ││
│ │ #public:mana.how │ Öffentlicher Raum ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Clients ││
│ ├─────────────────────────────────────────────────────────────┤│
│ │ Element (Web/Desktop/Mobile) ││
│ │ FluffyChat, Nheko, SchildiChat, ... ││
│ │ Mana Bots (matrix-bot-sdk) ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────┘
```
**Kernkonzepte:**
- **Homeserver:** Der Server, der Nutzerkonten und Räume hostet (wir nutzen Synapse)
- **Räume:** Container für Nachrichten, Events und State
- **Federation:** Server können miteinander kommunizieren
- **E2E-Verschlüsselung:** Megolm/Olm für sichere Kommunikation
---
## 2. Bot-Architektur Übersicht
### 2.1 Gesamtarchitektur
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Mana Bot Ecosystem │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ @mana/bot-services (Shared Business Logic) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ │ │
│ │ │ TodoSvc │ │ CalSvc │ │ AiSvc │ │ ClockSvc │ │ ... │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └─────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Matrix Transport Layer │ │
│ │ (matrix-bot-sdk) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────┼──────────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 19 Matrix Bots │ │ Gateway Bot │ │ Shared Services │ │
│ │ (Specialized) │ │ (All-in-One) │ │ (mana-llm, etc) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Backend APIs │ │
│ │ chat │ todo │ contacts │ calendar │ clock │ picture │ ... │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Data Layer │ │
│ │ PostgreSQL │ S3/MinIO │ JSON Files │ Redis │ Ollama │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.2 Bot-Typen
Wir unterscheiden drei Hauptkategorien von Bots:
#### Typ 1: Backend-integrierte Bots
Diese Bots fungieren als Interface zu bestehenden NestJS-Backend-APIs:
```
User → Matrix Bot → REST API → PostgreSQL
```
**Beispiele:**
- `matrix-contacts-bot` → Contacts Backend (Port 3015)
- `matrix-chat-bot` → Chat Backend (Port 3002)
- `matrix-picture-bot` → Picture Backend (Port 3006)
**Vorteile:**
- Konsistente Geschäftslogik (Web + Bot identisch)
- Zentralisierte Datenhaltung
- Einheitliche Auth via JWT
#### Typ 2: DSGVO-konforme Standalone-Bots
Diese Bots speichern Daten lokal ohne externe Services:
```
User → Matrix Bot → JSON File (lokal)
```
**Beispiele:**
- `matrix-todo-bot` → Lokale JSON-Datei
- `matrix-calendar-bot` → Lokale JSON-Datei
- `matrix-ollama-bot` → In-Memory + lokales Ollama
**Vorteile:**
- Keine Daten verlassen den Server
- Volle DSGVO-Konformität
- Offline-fähig
#### Typ 3: Gateway-Bot
Kombiniert alle Features in einem Bot:
```
User → matrix-mana-bot → @mana/bot-services → Multiple Backends
```
**Features:**
- Einheitlicher Einstiegspunkt (`!mana`)
- Intelligentes Command-Routing
- Cross-Feature-Integration (z.B. "Termin mit Kontakt erstellen")
---
## 3. Shared Package: @mana/bot-services
### 3.1 Architektur
Das Package `@mana/bot-services` stellt transport-agnostische Geschäftslogik bereit:
```typescript
// Business Logic Services
export { TodoModule, TodoService } from './todo';
export { CalendarModule, CalendarService } from './calendar';
export { AiModule, AiService } from './ai';
export { ClockModule, ClockService } from './clock';
// Infrastructure Services (NEU: Konsolidiert aus 11+ Bots)
export { SessionModule, SessionService } from './session'; // Auth via mana-auth
export { TranscriptionModule, TranscriptionService } from './transcription'; // STT via mana-stt
// Storage Provider (pluggable)
export { FileStorageProvider } from './shared/storage/file-storage.provider';
export { MemoryStorageProvider } from './shared/storage/memory-storage.provider';
// Utilities
export { generateId, getTodayISO, formatDateDE } from './shared/utils';
export { parseGermanDateKeyword } from './shared/date-parser';
```
### 3.1.1 Konsolidierte Services
Die folgenden Services wurden aus den einzelnen Bots konsolidiert:
| Service | Vorher | Nachher | Migrierte Bots |
|---------|--------|---------|----------------|
| `SessionService` | 11x dupliziert | 1x in bot-services | picture, contacts, chat, zitare, skilltree, presi, questions, storage, planta, cards, nutriphi |
| `TranscriptionService` | 6x dupliziert | 1x in bot-services | todo, clock, zitare, nutriphi, project-doc |
**Status: Vollständig migriert** - Alle 11 Bots mit SessionService und alle 5 Bots mit TranscriptionService nutzen jetzt die gemeinsamen Services aus `@mana/bot-services`.
### 3.2 TodoService
Vollständige Aufgabenverwaltung mit deutscher Sprachunterstützung:
```typescript
interface TodoService {
// CRUD
addTask(userId: string, text: string): Promise<Task>;
listTasks(userId: string, filter?: TaskFilter): Promise<Task[]>;
completeTask(userId: string, taskId: string): Promise<Task>;
deleteTask(userId: string, taskId: string): Promise<void>;
// Projekte
createProject(userId: string, name: string): Promise<Project>;
listProjects(userId: string): Promise<Project[]>;
// Filter
getTasksDueToday(userId: string): Promise<Task[]>;
getTasksByPriority(userId: string, priority: Priority): Promise<Task[]>;
}
// Deutsche Eingabeverarbeitung
"Morgen Arzt anrufen #gesundheit !hoch"
→ { text: "Arzt anrufen", dueDate: tomorrow, project: "gesundheit", priority: "high" }
```
### 3.3 CalendarService
Terminverwaltung mit natürlicher Spracheingabe:
```typescript
interface CalendarService {
// Events
createEvent(userId: string, input: string): Promise<Event>;
getEventsForDate(userId: string, date: Date): Promise<Event[]>;
getEventsInRange(userId: string, start: Date, end: Date): Promise<Event[]>;
// Kalender
createCalendar(userId: string, name: string): Promise<Calendar>;
listCalendars(userId: string): Promise<Calendar[]>;
}
// Natürliche Eingabe
"Meeting morgen um 14 Uhr im Büro"
→ { title: "Meeting", date: tomorrow, time: "14:00", location: "Büro" }
```
### 3.4 AiService
Integration mit lokalem LLM (Ollama) und mana-llm:
```typescript
interface AiService {
chat(userId: string, message: string): Promise<string>;
setModel(userId: string, model: string): Promise<void>;
setSystemPrompt(userId: string, mode: SystemMode): Promise<void>;
clearHistory(userId: string): Promise<void>;
// Vision (für Bildanalyse)
analyzeImage(userId: string, imageUrl: string, prompt: string): Promise<string>;
}
type SystemMode = 'default' | 'classify' | 'summarize' | 'translate' | 'code';
```
### 3.5 Storage Provider Pattern
Pluggable Storage für flexible Datenhaltung:
```typescript
interface StorageProvider<T> {
get(key: string): Promise<T | null>;
set(key: string, value: T): Promise<void>;
delete(key: string): Promise<void>;
list(prefix?: string): Promise<string[]>;
}
// Implementierungen
class FileStorageProvider<T> implements StorageProvider<T> {
constructor(private basePath: string) {}
// Speichert als JSON-Dateien
}
class MemoryStorageProvider<T> implements StorageProvider<T> {
private store = new Map<string, T>();
// In-Memory für Tests
}
// Zukünftig möglich:
class PostgresStorageProvider<T> implements StorageProvider<T> { }
class RedisStorageProvider<T> implements StorageProvider<T> { }
```
---
## 4. Matrix Bot Implementation
### 4.1 Technologie-Stack
Alle Matrix-Bots nutzen einen einheitlichen Stack:
| Komponente | Technologie | Version |
|------------|-------------|---------|
| **Framework** | NestJS | 10.x |
| **Matrix SDK** | matrix-bot-sdk | 0.7.1 |
| **Language** | TypeScript | 5.x |
| **Runtime** | Node.js | 20.x |
| **Build** | tsc + Docker | - |
### 4.2 Bot-Struktur
```
services/matrix-{name}-bot/
├── src/
│ ├── app.module.ts # NestJS Root Module
│ ├── main.ts # Bootstrap
│ ├── matrix/
│ │ ├── matrix.module.ts # Matrix SDK Integration
│ │ ├── matrix.service.ts # Bot-Logik & Command-Handling
│ │ └── matrix.constants.ts # Konfiguration
│ ├── services/ # Optionale lokale Services
│ └── utils/ # Hilfsfunktionen
├── Dockerfile
├── package.json
└── tsconfig.json
```
### 4.3 Matrix Service Pattern
```typescript
@Injectable()
export class MatrixService implements OnModuleInit, OnModuleDestroy {
private client: MatrixClient;
private storage: SimpleFsStorageProvider;
async onModuleInit() {
// Storage für Sync-State
this.storage = new SimpleFsStorageProvider('./data/matrix-state.json');
// Client initialisieren
this.client = new MatrixClient(
this.configService.get('MATRIX_HOMESERVER_URL'),
this.configService.get('MATRIX_ACCESS_TOKEN'),
this.storage,
);
// Crypto für E2E (optional)
const cryptoStore = new RustSdkCryptoStorageProvider('./data/crypto');
await this.client.crypto.prepare(cryptoStore);
// Event-Handler registrieren
this.client.on('room.message', this.handleMessage.bind(this));
// Sync starten
await this.client.start();
}
private async handleMessage(roomId: string, event: any) {
if (event.sender === this.client.getUserId()) return;
const body = event.content?.body;
if (!body?.startsWith('!')) return;
const [command, ...args] = body.slice(1).split(' ');
switch (command.toLowerCase()) {
case 'help':
case 'hilfe':
await this.sendHelp(roomId);
break;
case 'add':
case 'hinzufuegen':
await this.handleAdd(roomId, event.sender, args.join(' '));
break;
// ... weitere Commands
}
}
private async sendMessage(roomId: string, message: string) {
await this.client.sendMessage(roomId, {
msgtype: 'm.text',
body: message,
format: 'org.matrix.custom.html',
formatted_body: this.markdownToHtml(message),
});
}
}
```
### 4.4 Command-Pattern
Alle Bots nutzen ein einheitliches Command-Schema:
```
!command [args] # Englisch
!befehl [argumente] # Deutsch (Aliase)
```
**Beispiele:**
| Bot | Command | Alias | Beschreibung |
|-----|---------|-------|--------------|
| todo | `!add Task` | `!hinzufuegen` | Aufgabe erstellen |
| todo | `!list` | `!liste` | Aufgaben anzeigen |
| todo | `!done 1` | `!erledigt` | Aufgabe abschließen |
| calendar | `!today` | `!heute` | Termine heute |
| calendar | `!add Meeting morgen 14:00` | `!termin` | Termin erstellen |
| contacts | `!search Max` | `!suche` | Kontakt suchen |
### 4.5 Nummer-basiertes Referenzsystem
Für intuitive Interaktion nutzen Bots ein Listen-Referenz-System:
```
User: !kontakte
Bot: 1. Max Mustermann (max@example.com)
2. Anna Schmidt (anna@example.com)
3. Peter Meyer (peter@example.com)
User: !anrufen 2
Bot: Anruf an Anna Schmidt wird vorbereitet...
Telefon: +49 123 456789
```
**Implementierung:**
```typescript
// Pro User wird die letzte Liste gespeichert
private listCache = new Map<string, Contact[]>();
async handleList(roomId: string, userId: string) {
const contacts = await this.contactsApi.list(userId);
this.listCache.set(userId, contacts);
const message = contacts
.map((c, i) => `${i + 1}. ${c.name} (${c.email})`)
.join('\n');
await this.sendMessage(roomId, message);
}
async handleCall(roomId: string, userId: string, index: number) {
const contacts = this.listCache.get(userId);
if (!contacts || index < 1 || index > contacts.length) {
return this.sendMessage(roomId, 'Ungültige Nummer');
}
const contact = contacts[index - 1];
// ... Anruf-Logik
}
```
---
## 5. Bot-Katalog
### 5.1 Produktivitäts-Bots
| Bot | Port | Storage | Beschreibung |
|-----|------|---------|--------------|
| **matrix-mana-bot** | 3310 | JSON | Gateway - alle Features vereint |
| **matrix-todo-bot** | 3314 | JSON | Aufgabenverwaltung mit Projekten |
| **matrix-calendar-bot** | 3315 | JSON | Terminverwaltung mit Erinnerungen |
| **matrix-clock-bot** | 3318 | API | Timer, Alarme, Weltuhren |
### 5.2 KI & Medien-Bots
| Bot | Port | Backend | Beschreibung |
|-----|------|---------|--------------|
| **matrix-chat-bot** | 3327 | chat:3002 | KI-Konversationen |
| **matrix-ollama-bot** | 3311 | mana-llm:3025 | Lokales LLM (DSGVO) |
| **matrix-picture-bot** | 3319 | picture:3006 | AI-Bildgenerierung |
| **matrix-tts-bot** | 3023 | mana-tts:3022 | Text-to-Speech |
| **matrix-project-doc-bot** | 3313 | PostgreSQL+S3 | Projektdoku → Blog |
### 5.3 App-Integrations-Bots
| Bot | Port | Backend | Beschreibung |
|-----|------|---------|--------------|
| **matrix-contacts-bot** | 3320 | contacts:3015 | Kontaktverwaltung |
| **matrix-storage-bot** | 3323 | storage:3016 | Cloud-Speicher |
| **matrix-nutriphi-bot** | 3316 | nutriphi:3023 | Ernährungstracking |
| **matrix-zitare-bot** | 3321 | zitare:3019 | Tägliche Zitate |
| **matrix-questions-bot** | 3324 | questions:3011 | Q&A mit Web-Recherche |
| **matrix-cards-bot** | 3321 | cards:3009 | Kartendecks & Lernen |
| **matrix-planta-bot** | 3322 | planta:3022 | Pflanzenpflege |
| **matrix-skilltree-bot** | 3324 | skilltree:3024 | Skill Tree & XP |
| **matrix-presi-bot** | 3308 | presi:3008 | Präsentationen |
| **matrix-stats-bot** | 3312 | Umami | Analytics-Reports |
---
## 6. Authentifizierung
### 6.1 Zwei Auth-Modelle
Wir unterstützen zwei Authentifizierungsmodelle:
#### Modell A: Matrix User ID (DSGVO-optimiert)
Für Standalone-Bots ohne Backend-Anbindung:
```
Matrix User ID → Isolierte Daten pro User
@till:mana.how → /data/till-mana-how/todos.json
```
**Vorteile:**
- Kein Login erforderlich
- Daten strikt isoliert
- Funktioniert offline
**Verwendung:** matrix-todo-bot, matrix-calendar-bot, matrix-ollama-bot
#### Modell B: Mana Auth (JWT)
Für Backend-integrierte Bots:
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Matrix User │────>│ Matrix Bot │────>│ mana-auth │
│ !login x y │ │ │ │ (Port 3001) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ JWT Token │
▼ │
┌─────────────────┐ │
│ In-Memory Map │ │
@user → token │ │
└─────────────────┘ │
│ │
▼ │
┌─────────────────┐ │
│ Backend API │◀──────────┘
│ (JWT Validate) │
└─────────────────┘
```
**Login-Flow:**
```
User: !login till@mana.how geheimespasswort
Bot: Login erfolgreich! Token gültig für 7 Tage.
Nutze !logout zum Abmelden.
User: !kontakte
Bot: [Zeigt Kontakte aus Backend]
```
**Verwendung:** matrix-contacts-bot, matrix-chat-bot, matrix-picture-bot, etc.
### 6.2 Token-Management
```typescript
@Injectable()
export class AuthService {
private tokens = new Map<string, TokenData>();
async login(matrixUserId: string, email: string, password: string): Promise<boolean> {
const response = await fetch(`${this.authUrl}/api/v1/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (!response.ok) return false;
const { accessToken, expiresIn } = await response.json();
this.tokens.set(matrixUserId, {
token: accessToken,
expiresAt: Date.now() + expiresIn * 1000,
});
return true;
}
getToken(matrixUserId: string): string | null {
const data = this.tokens.get(matrixUserId);
if (!data || Date.now() > data.expiresAt) return null;
return data.token;
}
logout(matrixUserId: string): void {
this.tokens.delete(matrixUserId);
}
}
```
---
## 7. Datenbank-Anbindung
### 7.1 Vier Speichermodelle
| Modell | Technologie | Bots | Use Case |
|--------|-------------|------|----------|
| **Stateless** | Keine eigene | contacts, chat, picture | Backend delegiert |
| **JSON Files** | Lokale Dateien | todo, calendar, mana-bot | DSGVO, einfach |
| **PostgreSQL** | Drizzle ORM | project-doc-bot | Komplexe Relationen |
| **S3/MinIO** | AWS SDK | project-doc-bot | Medien-Speicherung |
### 7.2 JSON File Storage (DSGVO)
```typescript
// Struktur
/data/
├── {sanitized-matrix-user-id}/
│ ├── todos.json
│ ├── calendar.json
│ └── settings.json
```
```typescript
// FileStorageProvider
class FileStorageProvider<T> {
constructor(private basePath: string) {}
private getPath(key: string): string {
return path.join(this.basePath, `${key}.json`);
}
async get(key: string): Promise<T | null> {
const filePath = this.getPath(key);
if (!fs.existsSync(filePath)) return null;
const data = await fs.promises.readFile(filePath, 'utf-8');
return JSON.parse(data);
}
async set(key: string, value: T): Promise<void> {
const filePath = this.getPath(key);
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
await fs.promises.writeFile(filePath, JSON.stringify(value, null, 2));
}
}
```
### 7.3 PostgreSQL + Drizzle (Komplexe Bots)
```typescript
// schema.ts (project-doc-bot)
export const projects = pgTable('projects', {
id: uuid('id').primaryKey().defaultRandom(),
userId: varchar('user_id', { length: 255 }).notNull(),
name: varchar('name', { length: 255 }).notNull(),
description: text('description'),
createdAt: timestamp('created_at').defaultNow(),
});
export const mediaItems = pgTable('media_items', {
id: uuid('id').primaryKey().defaultRandom(),
projectId: uuid('project_id').references(() => projects.id),
type: varchar('type', { length: 50 }).notNull(), // photo, voice, text
s3Key: varchar('s3_key', { length: 500 }),
transcription: text('transcription'),
createdAt: timestamp('created_at').defaultNow(),
});
```
---
## 8. Matrix-spezifische Features
### 8.1 Rich Media Support
Matrix-Bots können verschiedene Nachrichtentypen senden:
```typescript
// Text mit Markdown/HTML
await client.sendMessage(roomId, {
msgtype: 'm.text',
body: 'Plain text fallback',
format: 'org.matrix.custom.html',
formatted_body: '<b>Bold</b> and <code>code</code>',
});
// Bilder
await client.sendMessage(roomId, {
msgtype: 'm.image',
body: 'Generated image',
url: await client.uploadContent(imageBuffer, 'image/png'),
info: { w: 512, h: 512, mimetype: 'image/png' },
});
// Dateien
await client.sendMessage(roomId, {
msgtype: 'm.file',
body: 'report.pdf',
url: await client.uploadContent(pdfBuffer, 'application/pdf'),
info: { mimetype: 'application/pdf', size: pdfBuffer.length },
});
// Audio (für TTS)
await client.sendMessage(roomId, {
msgtype: 'm.audio',
body: 'Voice message',
url: await client.uploadContent(audioBuffer, 'audio/mp3'),
info: { mimetype: 'audio/mp3', duration: 5000 },
});
```
### 8.2 Reactions
Bots können auf Nachrichten reagieren:
```typescript
// Bestätigung
await client.sendEvent(roomId, 'm.reaction', {
'm.relates_to': {
rel_type: 'm.annotation',
event_id: originalEventId,
key: '✅',
},
});
// Fehler
await client.sendEvent(roomId, 'm.reaction', {
'm.relates_to': {
rel_type: 'm.annotation',
event_id: originalEventId,
key: '❌',
},
});
```
### 8.3 Reply Threading
```typescript
await client.sendMessage(roomId, {
msgtype: 'm.text',
body: '> Original message\n\nMy reply',
format: 'org.matrix.custom.html',
formatted_body: '<mx-reply>...</mx-reply>My reply',
'm.relates_to': {
'm.in_reply_to': {
event_id: originalEventId,
},
},
});
```
### 8.4 End-to-End Encryption
```typescript
// Crypto Storage initialisieren
const cryptoStore = new RustSdkCryptoStorageProvider('./data/crypto');
// Client mit E2E
const client = new MatrixClient(homeserverUrl, accessToken, storage);
await client.crypto.prepare(cryptoStore);
// Verschlüsselten Raum beitreten
await client.joinRoom(encryptedRoomId);
// Nachrichten werden automatisch ver-/entschlüsselt
await client.sendMessage(encryptedRoomId, {
msgtype: 'm.text',
body: 'This will be encrypted',
});
```
---
## 9. Deployment
### 9.1 Docker Configuration
```dockerfile
# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
# Workspace files
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
# Shared packages
COPY packages/bot-services ./packages/bot-services
# Bot
COPY services/matrix-todo-bot ./services/matrix-todo-bot
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
RUN pnpm install --frozen-lockfile
RUN pnpm --filter @mana/bot-services build
RUN pnpm --filter matrix-todo-bot build
# Production
FROM node:20-alpine AS production
WORKDIR /app/services/matrix-todo-bot
COPY --from=builder /app/node_modules/.pnpm /app/node_modules/.pnpm
COPY --from=builder /app/services/matrix-todo-bot/node_modules ./node_modules
COPY --from=builder /app/services/matrix-todo-bot/dist ./dist
COPY --from=builder /app/services/matrix-todo-bot/package.json ./
# Data volume für persistente Speicherung
VOLUME /app/data
ENV NODE_ENV=production
EXPOSE 3314
CMD ["node", "dist/main.js"]
```
### 9.2 Environment Variables
```env
# Matrix Connection
MATRIX_HOMESERVER_URL=https://matrix.mana.how
MATRIX_ACCESS_TOKEN=syt_xxx...
MATRIX_USER_ID=@todo-bot:mana.how
# Auth (für Backend-Integration)
MANA_AUTH_URL=http://mana-auth:3001
# Storage
DATA_PATH=/app/data
# Optional: Backend URLs
TODO_BACKEND_URL=http://todo-backend:3018
CONTACTS_BACKEND_URL=http://contacts-backend:3015
# Optional: AI Services
MANA_LLM_URL=http://mana-llm:3025
```
### 9.3 docker-compose.yml
```yaml
version: '3.8'
services:
matrix-todo-bot:
build:
context: .
dockerfile: services/matrix-todo-bot/Dockerfile
environment:
- MATRIX_HOMESERVER_URL=${MATRIX_HOMESERVER_URL}
- MATRIX_ACCESS_TOKEN=${MATRIX_TODO_BOT_TOKEN}
- MATRIX_USER_ID=@todo-bot:mana.how
volumes:
- todo-bot-data:/app/data
networks:
- mana
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3314/health"]
interval: 30s
timeout: 10s
retries: 3
matrix-calendar-bot:
# ... analog
matrix-mana-bot:
# Gateway mit allen Services
depends_on:
- mana-llm
- todo-backend
- contacts-backend
volumes:
todo-bot-data:
calendar-bot-data:
mana-bot-data:
networks:
mana:
external: true
```
---
## 10. Port-Allokation
### Matrix Bots (3308-3327)
| Port | Service | Beschreibung |
|------|---------|--------------|
| 3308 | matrix-presi-bot | Präsentationen |
| 3310 | matrix-mana-bot | Gateway (All-in-One) |
| 3311 | matrix-ollama-bot | Lokales LLM |
| 3312 | matrix-stats-bot | Analytics |
| 3313 | matrix-project-doc-bot | Projektdoku |
| 3314 | matrix-todo-bot | Aufgaben |
| 3315 | matrix-calendar-bot | Termine |
| 3316 | matrix-nutriphi-bot | Ernährung |
| 3318 | matrix-clock-bot | Timer/Alarme |
| 3319 | matrix-picture-bot | Bildgenerierung |
| 3320 | matrix-contacts-bot | Kontakte |
| 3321 | matrix-zitare-bot | Zitate |
| 3322 | matrix-planta-bot | Pflanzen |
| 3323 | matrix-storage-bot | Cloud-Speicher |
| 3324 | matrix-questions-bot | Q&A |
| 3327 | matrix-chat-bot | KI-Chat |
### Supporting Services
| Port | Service | Beschreibung |
|------|---------|--------------|
| 3001 | mana-auth | Authentifizierung |
| 3020 | mana-stt | Speech-to-Text |
| 3021 | mana-search | Web-Recherche |
| 3022 | mana-tts | Text-to-Speech |
| 3025 | mana-llm | LLM-Abstraction |
---
## 11. Vorteile gegenüber Drittanbieter-Plattformen
### 11.1 Vollständige Kontrolle
| Aspekt | Telegram/Discord | Mana Matrix |
|--------|------------------|-----------------|
| **Datenhoheit** | Bei Anbieter | Bei uns |
| **Verfügbarkeit** | Abhängig von Anbieter | Eigene Infrastruktur |
| **API-Änderungen** | Anbieter entscheidet | Wir entscheiden |
| **Preisänderungen** | Anbieter entscheidet | Keine |
| **Zensur/Sperrung** | Möglich | Nicht möglich |
### 11.2 DSGVO-Konformität
```
┌────────────────────────────────────────────────────────────────┐
│ DSGVO-Compliance │
├────────────────────────────────────────────────────────────────┤
│ │
│ ✅ Datenverarbeitung nur auf eigenen Servern │
│ ✅ Keine Weitergabe an Dritte │
│ ✅ Löschung auf Anfrage (Art. 17) │
│ ✅ Auskunft über gespeicherte Daten (Art. 15) │
│ ✅ Datenportabilität (Art. 20) │
│ ✅ Auftragsverarbeitungsvertrag nicht nötig │
│ │
└────────────────────────────────────────────────────────────────┘
```
### 11.3 Einheitliche UX
Da wir beide Seiten kontrollieren (Bot + Client), können wir:
- Konsistente Command-Syntax über alle Bots
- Deutsche Sprachunterstützung überall
- Einheitliches Fehler-Handling
- Nahtlose Cross-Bot-Integration
---
## 12. Zukünftige Entwicklung
### 12.1 Geplante Erweiterungen
- **Widget-Integration:** Interaktive UIs direkt in Element
- **Voice-Bot:** Sprachsteuerung via Matrix Calls
- **Bot-Discovery:** Automatische Bot-Erkennung in Räumen
- **Mehr @mana/bot-services:** Nutrition, Stats, Docs Services
### 12.2 Konsolidierung
Der Fokus liegt auf der Konsolidierung der Bot-Services in `@mana/bot-services`:
- Alle wiederkehrende Logik zentral
- Einheitliche Storage-Abstraction
- Transport-agnostische Services
---
## 13. Fazit
Mana's Matrix-Bot-Architektur bietet eine **vollständig unabhängige, DSGVO-konforme** Alternative zu Cloud-basierten Chat-Diensten. Mit 19 spezialisierten Bots und einem Gateway-Bot decken wir alle Produktivitäts- und App-Integrationsszenarien ab.
**Kernvorteile:**
1. **Volle Kontrolle** über Daten und Infrastruktur
2. **DSGVO-Konformität** durch lokale Datenhaltung
3. **Einheitliche UX** durch konsistente Command-Patterns
4. **Skalierbarkeit** durch Microservices-Architektur
5. **Erweiterbarkeit** durch @mana/bot-services
---
*Dokument erstellt am 1. Februar 2026*
*Letzte Aktualisierung: 1. Februar 2026*

View file

@ -1,674 +0,0 @@
# Matrix Self-Hosting auf Mac Mini
Plan für DSGVO-konformes Messaging mit Matrix/Synapse auf dem Mana Server.
## Übersicht
```
┌─────────────────────────────────────────────────────────────────────┐
│ Internet │
│ │ │
│ ▼ │
│ Cloudflare Tunnel │
│ │ │
│ ├─── matrix.mana.how ──────► Synapse (Port 8008) │
│ ├─── element.mana.how ─────► Element Web (Port 8087) │
│ └─── (bestehende Services) │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Docker Container │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │
│ │ │ Synapse │ │ Element Web │ │ Matrix Bots │ │ │
│ │ │ (8008) │ │ (8087) │ │ (NestJS) │ │ │
│ │ └──────┬───────┘ └──────────────┘ └────────┬─────────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ PostgreSQL │ │ Ollama │ │ │
│ │ │ (matrix db) │ │ (11434) │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
## DSGVO-Vorteile
| Aspekt | Telegram | Matrix (Self-Hosted) |
|--------|----------|----------------------|
| Datenstandort | Dubai/Singapur | Mac Mini (Deutschland) |
| AV-Vertrag | Nicht möglich | Nicht nötig (eigene Daten) |
| E2E-Verschlüsselung | Nur Secret Chats | Standard für alle Räume |
| Metadaten | Bei Telegram | Lokal gespeichert |
| Löschung | Abhängig von Telegram | Volle Kontrolle |
---
## Phase 1: Synapse Homeserver
### 1.1 Datenbank erstellen
```bash
ssh mana-server
# Neue Datenbank für Matrix
docker exec mana-postgres psql -U postgres -c "CREATE DATABASE matrix;"
docker exec mana-postgres psql -U postgres -c "CREATE USER synapse WITH PASSWORD 'synapse-secure-password';"
docker exec mana-postgres psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE matrix TO synapse;"
```
### 1.2 Synapse Konfiguration erstellen
```bash
# Verzeichnis erstellen
mkdir -p ~/projects/mana-monorepo/docker/matrix
# Synapse Config generieren (einmalig)
docker run -it --rm \
-v ~/projects/mana-monorepo/docker/matrix:/data \
-e SYNAPSE_SERVER_NAME=mana.how \
-e SYNAPSE_REPORT_STATS=no \
matrixdotorg/synapse:latest generate
```
### 1.3 homeserver.yaml anpassen
**Datei:** `docker/matrix/homeserver.yaml`
```yaml
server_name: "mana.how"
pid_file: /data/homeserver.pid
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation]
compress: false
database:
name: psycopg2
args:
user: synapse
password: "synapse-secure-password"
database: matrix
host: postgres
port: 5432
cp_min: 5
cp_max: 10
# Logging
log_config: "/data/mana.how.log.config"
# Media Store (lokaler Speicher für Medien)
media_store_path: /data/media_store
max_upload_size: 50M
# Registrierung
enable_registration: false
enable_registration_without_verification: false
# Admin-Account beim ersten Start erstellen
# Nach dem Start: docker exec -it synapse register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008 -a
# Rate Limiting (für Bots erhöhen)
rc_message:
per_second: 5
burst_count: 20
rc_registration:
per_second: 0.5
burst_count: 5
# Für Bot-Integration: Application Services erlauben
app_service_config_files: []
# DSGVO: Datenaufbewahrung begrenzen
retention:
enabled: true
default_policy:
min_lifetime: 1d
max_lifetime: 365d
allowed_lifetime_min: 1d
allowed_lifetime_max: 365d
purge_jobs:
- longest_max_lifetime: 3d
interval: 12h
- shortest_max_lifetime: 365d
interval: 1d
# Telemetrie deaktivieren
report_stats: false
# Trusted Key Server (Matrix.org)
trusted_key_servers:
- server_name: "matrix.org"
# Signing Key
signing_key_path: "/data/mana.how.signing.key"
```
### 1.4 Docker Compose Ergänzung
Füge zu `docker-compose.macmini.yml` hinzu:
```yaml
# ============================================
# Matrix Synapse (Homeserver)
# ============================================
synapse:
image: matrixdotorg/synapse:latest
container_name: mana-synapse
restart: always
depends_on:
postgres:
condition: service_healthy
environment:
SYNAPSE_CONFIG_PATH: /data/homeserver.yaml
volumes:
- ./docker/matrix:/data
- synapse_media:/data/media_store
ports:
- "8008:8008"
healthcheck:
test: ["CMD", "curl", "-fSs", "http://localhost:8008/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# ============================================
# Element Web (Matrix Client)
# ============================================
element-web:
image: vectorim/element-web:latest
container_name: mana-element
restart: always
depends_on:
synapse:
condition: service_healthy
volumes:
- ./docker/matrix/element-config.json:/app/config.json:ro
ports:
- "8087:80"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80/"]
interval: 30s
timeout: 10s
retries: 3
# Volumes ergänzen:
volumes:
synapse_media:
name: mana-synapse-media
```
### 1.5 Element Web Konfiguration
**Datei:** `docker/matrix/element-config.json`
```json
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.mana.how",
"server_name": "mana.how"
},
"m.identity_server": {
"base_url": ""
}
},
"brand": "Mana Chat",
"integrations_ui_url": "",
"integrations_rest_url": "",
"integrations_widgets_urls": [],
"disable_guests": true,
"disable_3pid_login": true,
"default_country_code": "DE",
"show_labs_settings": false,
"features": {
"feature_video_rooms": true,
"feature_group_calls": true
},
"room_directory": {
"servers": ["mana.how"]
},
"setting_defaults": {
"breadcrumbs": true
},
"default_theme": "dark"
}
```
### 1.6 Cloudflare Tunnel erweitern
**Datei:** `~/.cloudflared/config.yml`
```yaml
# Bestehende Einträge...
- hostname: matrix.mana.how
service: http://localhost:8008
- hostname: element.mana.how
service: http://localhost:8087
```
Nach Änderung:
```bash
launchctl stop com.cloudflare.cloudflared
launchctl start com.cloudflare.cloudflared
```
---
## Phase 2: Synapse starten & Admin erstellen
### 2.1 Container starten
```bash
cd ~/projects/mana-monorepo
# Nur Synapse + Element starten
docker compose -f docker-compose.macmini.yml up -d synapse element-web
# Logs prüfen
docker logs -f mana-synapse
```
### 2.2 Admin-User erstellen
```bash
# Interaktiv einen Admin erstellen
docker exec -it mana-synapse register_new_matrix_user \
-c /data/homeserver.yaml \
http://localhost:8008 \
-a
# Eingeben:
# Username: admin
# Password: (sicheres Passwort)
# Admin: yes
```
### 2.3 Testen
```bash
# Health Check
curl https://matrix.mana.how/health
# Erwartete Antwort: OK
# Federation Check
curl https://matrix.mana.how/_matrix/federation/v1/version
# Erwartete Antwort: {"server":{"name":"Synapse","version":"..."}}
# Element Web aufrufen
open https://element.mana.how
```
---
## Phase 3: Bot-Räume einrichten
### 3.1 Räume erstellen (via Element)
1. **Anmelden** bei https://element.mana.how mit Admin-Account
2. **Räume erstellen:**
- `#ollama-bot:mana.how` - AI Chat Bot
- `#stats-bot:mana.how` - Analytics Reports
- `#project-doc-bot:mana.how` - Projektdokumentation
### 3.2 Bot-User erstellen
```bash
# Bot-User für jeden Bot erstellen (nicht-Admin)
docker exec -it mana-synapse register_new_matrix_user \
-c /data/homeserver.yaml \
http://localhost:8008
# Erstelle:
# - ollama-bot (Password notieren)
# - stats-bot (Password notieren)
# - projectdoc-bot (Password notieren)
```
### 3.3 Access Tokens generieren
```bash
# Für jeden Bot ein Access Token holen
curl -X POST "https://matrix.mana.how/_matrix/client/v3/login" \
-H "Content-Type: application/json" \
-d '{
"type": "m.login.password",
"user": "ollama-bot",
"password": "bot-password"
}'
# Response: {"access_token": "syt_xxx", ...}
# Token für .env speichern
```
---
## Phase 4: Bot-Migration (NestJS)
### 4.1 Neue Package-Struktur
```
services/
├── telegram-ollama-bot/ # Alt (Telegram)
├── telegram-stats-bot/ # Alt (Telegram)
├── telegram-project-doc-bot/# Alt (Telegram)
├── matrix-ollama-bot/ # NEU (Matrix)
├── matrix-stats-bot/ # NEU (Matrix)
└── matrix-project-doc-bot/ # NEU (Matrix)
```
### 4.2 Dependencies
```bash
cd services/matrix-ollama-bot
pnpm add matrix-bot-sdk
```
### 4.3 Bot-Grundstruktur (Beispiel: Ollama Bot)
**Datei:** `services/matrix-ollama-bot/src/bot/matrix.service.ts`
```typescript
import {
MatrixClient,
SimpleFsStorageProvider,
AutojoinRoomsMixin,
RichConsoleLogger,
LogService,
} from 'matrix-bot-sdk';
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class MatrixService implements OnModuleInit, OnModuleDestroy {
private client: MatrixClient;
constructor(private config: ConfigService) {}
async onModuleInit() {
LogService.setLogger(new RichConsoleLogger());
const homeserverUrl = this.config.get('MATRIX_HOMESERVER_URL');
const accessToken = this.config.get('MATRIX_ACCESS_TOKEN');
const storage = new SimpleFsStorageProvider('bot-storage.json');
this.client = new MatrixClient(homeserverUrl, accessToken, storage);
// Auto-join bei Einladungen
AutojoinRoomsMixin.setupOnClient(this.client);
// Message Handler
this.client.on('room.message', this.handleMessage.bind(this));
await this.client.start();
console.log('Matrix bot started!');
}
async onModuleDestroy() {
await this.client.stop();
}
private async handleMessage(roomId: string, event: any) {
// Eigene Nachrichten ignorieren
if (event.sender === await this.client.getUserId()) return;
// Nur Text-Nachrichten
if (event.content?.msgtype !== 'm.text') return;
const body = event.content.body;
// Command-Handler
if (body.startsWith('!')) {
await this.handleCommand(roomId, event, body);
} else {
// Normaler Chat → Ollama
await this.handleChat(roomId, event, body);
}
}
private async handleCommand(roomId: string, event: any, body: string) {
const [command, ...args] = body.slice(1).split(' ');
switch (command.toLowerCase()) {
case 'help':
await this.sendMessage(roomId, this.getHelpText());
break;
case 'models':
// Liste verfügbare Modelle
break;
case 'clear':
// Chat-History löschen
break;
// ... weitere Commands
}
}
private async handleChat(roomId: string, event: any, message: string) {
// Typing-Indikator senden
await this.client.setTyping(roomId, true);
// Ollama-Anfrage (wie bisher)
const response = await this.ollamaService.chat(message);
await this.client.setTyping(roomId, false);
await this.sendMessage(roomId, response);
}
async sendMessage(roomId: string, message: string) {
await this.client.sendMessage(roomId, {
msgtype: 'm.text',
body: message,
format: 'org.matrix.custom.html',
formatted_body: this.markdownToHtml(message),
});
}
private getHelpText(): string {
return `**Mana Ollama Bot**
Befehle:
- \`!help\` - Diese Hilfe
- \`!models\` - Verfügbare Modelle
- \`!model <name>\` - Modell wechseln
- \`!clear\` - Chat-Verlauf löschen
Einfach eine Nachricht schreiben für AI-Chat.`;
}
}
```
### 4.4 Environment Variables
**Datei:** `services/matrix-ollama-bot/.env`
```env
# Server
PORT=3311
# Matrix
MATRIX_HOMESERVER_URL=https://matrix.mana.how
MATRIX_ACCESS_TOKEN=syt_xxx
# Optional: Nur bestimmte Räume erlauben
MATRIX_ALLOWED_ROOMS=#ollama-bot:mana.how
# Ollama
OLLAMA_URL=http://host.docker.internal:11434
OLLAMA_MODEL=gemma3:4b
OLLAMA_TIMEOUT=120000
```
### 4.5 Docker Compose für Matrix Bots
```yaml
# ============================================
# Matrix Ollama Bot
# ============================================
matrix-ollama-bot:
image: ghcr.io/memo-2023/matrix-ollama-bot:latest
container_name: mana-matrix-ollama-bot
restart: always
depends_on:
synapse:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 3311
MATRIX_HOMESERVER_URL: http://synapse:8008
MATRIX_ACCESS_TOKEN: ${MATRIX_OLLAMA_BOT_TOKEN}
OLLAMA_URL: http://host.docker.internal:11434
OLLAMA_MODEL: gemma3:4b
volumes:
- matrix_ollama_bot_data:/app/data
ports:
- "3311:3311"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3311/health"]
interval: 30s
timeout: 10s
retries: 3
# Volume ergänzen:
volumes:
matrix_ollama_bot_data:
name: mana-matrix-ollama-bot
```
---
## Phase 5: Feature-Mapping Telegram → Matrix
### Commands
| Telegram | Matrix | Beschreibung |
|----------|--------|--------------|
| `/start` | `!help` | Hilfe anzeigen |
| `/help` | `!help` | Hilfe anzeigen |
| `/models` | `!models` | Modelle auflisten |
| `/model x` | `!model x` | Modell wechseln |
| `/clear` | `!clear` | Chat löschen |
| `/status` | `!status` | Bot-Status |
### Media-Handling
| Feature | Telegram | Matrix |
|---------|----------|--------|
| Foto senden | `ctx.message.photo` | `m.image` msgtype |
| Voice senden | `ctx.message.voice` | `m.audio` msgtype |
| Datei senden | `ctx.message.document` | `m.file` msgtype |
| Foto antworten | `ctx.replyWithPhoto()` | `sendMessage()` mit `m.image` |
### Beispiel: Media-Download in Matrix
```typescript
async downloadMedia(event: any): Promise<Buffer> {
const mxcUrl = event.content.url; // mxc://mana.how/abc123
const httpUrl = this.client.mxcToHttp(mxcUrl);
const response = await fetch(httpUrl);
return Buffer.from(await response.arrayBuffer());
}
```
---
## Phase 6: Health Check & Monitoring
### Health Checks ergänzen
**Datei:** `scripts/mac-mini/health-check.sh`
```bash
# Matrix Synapse
if curl -sf http://localhost:8008/health > /dev/null; then
echo "✅ Synapse: OK"
else
echo "❌ Synapse: FAILED"
FAILED_SERVICES="$FAILED_SERVICES synapse"
fi
# Element Web
if curl -sf http://localhost:8087/ > /dev/null; then
echo "✅ Element Web: OK"
else
echo "❌ Element Web: FAILED"
FAILED_SERVICES="$FAILED_SERVICES element-web"
fi
# Matrix Ollama Bot
if curl -sf http://localhost:3311/health > /dev/null; then
echo "✅ Matrix Ollama Bot: OK"
else
echo "❌ Matrix Ollama Bot: FAILED"
FAILED_SERVICES="$FAILED_SERVICES matrix-ollama-bot"
fi
```
### Prometheus Metrics (optional)
Synapse exportiert Metrics auf Port 9000 (kann aktiviert werden):
```yaml
# In homeserver.yaml ergänzen
enable_metrics: true
metrics_port: 9000
# prometheus.yml ergänzen
- job_name: 'synapse'
static_configs:
- targets: ['synapse:9000']
```
---
## Zeitplan
| Phase | Aufgabe | Aufwand |
|-------|---------|---------|
| **1** | Synapse + Element aufsetzen | 1-2h |
| **2** | Admin & Bot-User erstellen | 30min |
| **3** | Bot-Räume einrichten | 30min |
| **4** | Ersten Bot migrieren (Ollama) | 2-4h |
| **5** | Weitere Bots migrieren | je 1-2h |
| **6** | Monitoring & Alerts | 1h |
**Gesamt:** ~1 Tag für Grundsetup + Bot-Migration
---
## Nächste Schritte
1. [ ] `docker/matrix/` Verzeichnis erstellen
2. [ ] Synapse Config generieren
3. [ ] Docker Compose erweitern
4. [ ] Cloudflare Tunnel konfigurieren
5. [ ] Synapse starten & testen
6. [ ] Admin-Account erstellen
7. [ ] Bot-User erstellen
8. [ ] `matrix-ollama-bot` Service erstellen
9. [ ] Bot testen
10. [ ] Weitere Bots migrieren
11. [ ] Telegram Bots deaktivieren
---
## Ressourcen
- [Matrix Spec](https://spec.matrix.org/)
- [Synapse Docs](https://element-hq.github.io/synapse/latest/)
- [matrix-bot-sdk](https://github.com/turt2live/matrix-bot-sdk)
- [Element Web Config](https://github.com/element-hq/element-web/blob/develop/docs/config.md)

View file

@ -70,7 +70,7 @@
| 3010 | mana-sync | Go | Local-first data sync (WebSocket + HTTP) |
| 3011 | mana-media | NestJS | Content-addressable storage, thumbnails |
| 3012 | mana-search | Go | Web search via SearXNG |
| 3013 | mana-notify | Go | Notifications (email, push, Matrix) |
| 3013 | mana-notify | Go | Notifications (email, push, webhook) |
| 3014 | mana-crawler | Go | Web crawler, content extraction |
| 3015 | mana-landing-builder | NestJS | Org landing page builder |
| 3016 | mana-api-gateway | Go | API keys, rate limiting, usage tracking |
@ -107,14 +107,10 @@ Pure CRUD apps use mana-sync directly.
| 3040 | presi-server | Hono/Bun | Share links |
| 3041-3059 | *(reserved)* | | |
## 4000-4099: Matrix/Chat Stack
## 4000-4099: Misc
| Port | Service | Description |
|------|---------|-------------|
| 4000 | synapse | Matrix homeserver |
| 4001 | mana-matrix-bot | Go bot (health/metrics) |
| 4010 | element-web | Element web client |
| 4011 | matrix-web | SvelteKit Matrix client |
| 4400 | landings | Nginx static landing pages |
## 5000-5059: Web Frontends (SvelteKit)
@ -175,7 +171,6 @@ Pure CRUD apps use mana-sync directly.
| 9090 | victoriametrics | Metrics storage |
| 9091 | pushgateway | Deploy metrics |
| 9093 | alertmanager | Alert routing |
| 9095 | alert-notifier | Matrix alert bridge |
| 9100 | node-exporter | Host metrics |
| 9110 | cadvisor | Container metrics |
| 9121 | redis-exporter | Redis metrics |

View file

@ -39,15 +39,12 @@ This document defines the URL schema for all mana.how subdomains.
| **Media Service** | media.mana.how | Image/video processing |
| **LLM Service** | llm.mana.how | LLM abstraction layer |
| **LLM Playground** | playground.mana.how | LLM testing interface |
| **Link Shortener** | link.mana.how | URL shortener (uload) |
| **File Storage** | files.mana.how | MinIO/S3 file access |
### Matrix/Communication
### Automation
| Service | URL | Description |
|---------|-----|-------------|
| **Matrix Server** | matrix.mana.how | Synapse homeserver |
| **Element Web** | element.mana.how | Matrix web client |
| **N8N** | n.mana.how | Workflow automation |
### Monitoring & Admin