managarten/docs/DAILY_REPORT_2026-02-02.md
Till-JS d5e18c9c27 🔧 fix(mac-mini): update health checks and disable missing services
- Disable api-gateway and skilltree-web (no working images/Dockerfiles)
- Fix mana-search Dockerfile healthcheck port and endpoint
- Update health-check.sh to skip disabled services
- Fix search service health endpoint (/api/v1/health)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 13:28:55 +01:00

1081 lines
37 KiB
Markdown

# Daily Report - 2. Februar 2026
**Zeitraum:** 10:00 Uhr (1. Feb) - 05:00 Uhr (2. Feb)
**Commits:** 51
**Hauptthemen:** OIDC/Synapse Integration, Matrix Bot CI/CD, Credit System, Cross-Domain SSO, Monitoring
---
## Zusammenfassung
Eine epische 19-Stunden-Session mit Fokus auf **Auth-Infrastruktur** und **Production Readiness**:
- **OIDC Integration** - Vollständige Synapse-Kompatibilität mit EdDSA JWT Signing
- **Matrix Bot CI/CD** - Automatisierte GHCR Deployments für 10 Bots mit Watchtower
- **Credit System** - Neue Packages für Credit-Operationen und UI-Komponenten
- **Cross-Domain SSO** - Single Sign-On über alle *.mana.how Subdomains
- **Monitoring** - Node-Exporter, Grafana Dashboards, Prometheus Alerts
- **SSD Migration** - PostgreSQL und MinIO auf externe SSD verschoben
---
## 1. OIDC/Synapse Integration (Kritische Fixes)
### Problem
Matrix Synapse konnte sich nicht mit mana-core-auth verbinden. Token-Verifizierung schlug fehl.
### Ursachen & Lösungen
#### 1.1 EdDSA JWT Signing
**Commit:** `efb077b9` - fix(mana-core-auth): use EdDSA for OIDC id_token signing
```typescript
// better-auth.config.ts - VORHER
const auth = betterAuth({
jwt: {
issuer: config.get('JWT_ISSUER'),
}
});
// NACHHER - mit JWT Plugin für EdDSA
const auth = betterAuth({
plugins: [jwt()], // Aktiviert EdDSA Keys aus JWKS
advanced: {
useJWTPlugin: true, // id_tokens mit EdDSA statt HS256
}
});
```
**Warum:** Synapse verifiziert id_tokens via JWKS-Endpoint (`/.well-known/jwks.json`). HS256 verwendet ein Shared Secret, das Synapse nicht kennt. EdDSA verwendet Public/Private Keypaar aus JWKS.
#### 1.2 JWT Issuer = BASE_URL
**Commit:** `8cd5021b` - fix(mana-core-auth): use BASE_URL as JWT issuer
```typescript
// VORHER
jwt: { issuer: config.get('JWT_ISSUER') } // "manacore"
// NACHHER
jwt: { issuer: config.get('BASE_URL') } // "https://auth.mana.how"
```
**Warum:** OIDC Discovery Document (`/.well-known/openid-configuration`) enthält `issuer: "https://auth.mana.how"`. Der JWT `iss` Claim muss damit übereinstimmen.
#### 1.3 Body-Parser für Token Exchange
**Commit:** `f0cf1bc8` - fix(mana-core-auth): OIDC token exchange now works with body-parser
```typescript
// main.ts
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
// oidc.controller.ts
@Post('token')
async token(@Req() req: Request, @Res() res: Response) {
// req.body ist jetzt geparsed, nicht raw
const params = new URLSearchParams();
for (const [key, value] of Object.entries(req.body)) {
params.append(key, String(value));
}
return this.betterAuthService.handleOidcRequest(params, res);
}
```
**Warum:** Synapse sendet Token-Requests als `application/x-www-form-urlencoded`. Ohne body-parser kam `req.body` als leeres Objekt an.
#### 1.4 CORS Origins erweitert
**Commit:** `5a8e20e0` - fix(auth): add all apps to CORS_ORIGINS
```yaml
# docker-compose.macmini.yml
environment:
CORS_ORIGINS: "https://auth.mana.how,https://matrix.mana.how,https://chat.mana.how,https://calendar.mana.how,https://todo.mana.how,https://clock.mana.how,https://contacts.mana.how,https://picture.mana.how,https://zitare.mana.how,https://questions.mana.how,https://planta.mana.how,https://skilltree.mana.how,https://storage.mana.how,https://manadeck.mana.how,https://nutriphi.mana.how,https://presi.mana.how,https://link.mana.how,https://playground.mana.how"
```
---
## 2. Matrix Bot CI/CD Pipeline
### Übersicht
Vollautomatische Build- und Deployment-Pipeline für 10 Matrix-Bots via GitHub Actions → GHCR → Watchtower.
**Commit:** `45152ee9` - feat(matrix-bots): add CI/CD pipeline for automated GHCR deployment
### Pipeline-Architektur
```
┌─────────────────────────────────────────────────────────────────┐
│ GitHub Actions CI │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Push to main │───>│ Detect │───>│ Build Docker │ │
│ │ │ │ Changes │ │ Images (amd64) │ │
│ └──────────────┘ └──────────────┘ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Push to GHCR │ │
│ │ ghcr.io/till-js/ │ │
│ └────────┬─────────┘ │
└──────────────────────────────────────────────────┼───────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Mac Mini Server │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Watchtower │ │
│ │ (polls GHCR every 5 minutes) │ │
│ └────────────────────────────┬─────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ mana-bot │ │ todo-bot │ │ calendar │ │ ollama │ │
│ │ :latest │ │ :latest │ │ -bot │ │ -bot │ │
│ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### Bots mit CI/CD
| Bot | GHCR Image | Port |
|-----|------------|------|
| matrix-mana-bot | `ghcr.io/till-js/matrix-mana-bot` | 3310 |
| matrix-todo-bot | `ghcr.io/till-js/matrix-todo-bot` | 3311 |
| matrix-calendar-bot | `ghcr.io/till-js/matrix-calendar-bot` | 3312 |
| matrix-ollama-bot | `ghcr.io/till-js/matrix-ollama-bot` | 3313 |
| matrix-stats-bot | `ghcr.io/till-js/matrix-stats-bot` | 3314 |
| matrix-project-doc-bot | `ghcr.io/till-js/matrix-project-doc-bot` | 3315 |
| matrix-tts-bot | `ghcr.io/till-js/matrix-tts-bot` | 3316 |
| matrix-clock-bot | `ghcr.io/till-js/matrix-clock-bot` | 3317 |
| matrix-nutriphi-bot | `ghcr.io/till-js/matrix-nutriphi-bot` | 3318 |
| matrix-zitare-bot | `ghcr.io/till-js/matrix-zitare-bot` | 3319 |
### Docker-Build Challenges
#### Problem 1: QEMU Emulation Failure
**Commits:** `ab49be0b`, `a50d98c7`
```bash
# CI Build auf amd64 Runner für arm64 Target
Error: qemu: uncaught target signal 4 (Illegal instruction)
```
**Lösung:** Nur amd64 bauen. Mac Mini mit Apple Silicon läuft via Rosetta.
```yaml
# .github/workflows/ci.yml
platforms: linux/amd64 # Kein linux/arm64
```
```yaml
# docker-compose.macmini.yml
services:
matrix-mana-bot:
platform: linux/amd64 # Explizit für Apple Silicon
```
#### Problem 2: Alpine vs glibc
**Commit:** `a384bed1` - fix(matrix-bots): switch to node:20-slim
```dockerfile
# VORHER - Alpine (musl libc)
FROM node:20-alpine
# NACHHER - Debian slim (glibc)
FROM node:20-slim
```
**Warum:** `@matrix-org/matrix-sdk-crypto-nodejs` hat prebuilt Binaries nur für glibc.
#### Problem 3: E2EE Native Module
**Commit:** `a8521d7a` - fix(matrix-bots): disable E2EE crypto module
```json
// package.json (root)
{
"pnpm": {
"overrides": {
"@matrix-org/matrix-sdk-crypto-nodejs": "npm:empty-npm-package@1.0.0"
}
}
}
```
**Warum:** Das Crypto-Module erfordert plattformspezifische Native Binaries. In Docker-CI nicht verfügbar. E2EE wird serverseitig von Synapse gehandelt.
#### Problem 4: Health Check
**Commit:** `ea0198cc` - fix(bots): install wget for Docker health checks
```dockerfile
# node:20-slim hat weder wget noch curl
RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/*
```
---
## 3. Credit System
### Neue Packages
**Commit:** `8cd5021b` - feat: add credit-operations and shared-credit-ui packages
#### @manacore/credit-operations
Zentrale Definition aller Credit-Operationen:
```typescript
// packages/credit-operations/src/index.ts
export const CREDIT_OPERATIONS = {
// Chat
'chat:message': { cost: 1, description: 'Send chat message' },
'chat:ai-response': { cost: 5, description: 'AI response generation' },
// Picture
'picture:generate-sd': { cost: 10, description: 'Stable Diffusion image' },
'picture:generate-flux': { cost: 25, description: 'Flux image generation' },
// Calendar
'calendar:ai-scheduling': { cost: 3, description: 'AI scheduling assistant' },
// Todo
'todo:ai-breakdown': { cost: 5, description: 'AI task breakdown' },
// ... 50+ weitere Operationen
};
export function getCreditCost(operation: string): number {
return CREDIT_OPERATIONS[operation]?.cost ?? 0;
}
```
#### @manacore/shared-credit-ui
UI-Komponenten für React Native und Svelte:
**Mobile (React Native):**
```typescript
// CreditBalance.tsx
export function CreditBalance({ userId }: Props) {
const balance = useCreditBalance(userId);
return (
<View style={styles.container}>
<Text style={styles.label}>Credits</Text>
<Text style={styles.balance}>{balance}</Text>
</View>
);
}
// CreditToast.tsx
export function CreditToast({ operation, cost }: Props) {
return (
<Animated.View style={styles.toast}>
<Text>-{cost} Credits</Text>
<Text>{operation}</Text>
</Animated.View>
);
}
```
**Web (Svelte):**
```svelte
<!-- CreditBalance.svelte -->
<script lang="ts">
let { userId } = $props();
let balance = $state(0);
$effect(() => {
fetchBalance(userId).then(b => balance = b);
});
</script>
<div class="credit-balance">
<span class="label">Credits</span>
<span class="value">{balance}</span>
</div>
```
### NestJS Integration
**@UseCredits Decorator:**
```typescript
// packages/mana-core-nestjs-integration/src/decorators/use-credits.decorator.ts
export function UseCredits(operation: string, cost?: number) {
return applyDecorators(
SetMetadata('credit_operation', operation),
SetMetadata('credit_cost', cost ?? getCreditCost(operation)),
UseInterceptors(CreditInterceptor),
);
}
```
**CreditInterceptor:**
```typescript
// packages/mana-core-nestjs-integration/src/interceptors/credit.interceptor.ts
@Injectable()
export class CreditInterceptor implements NestInterceptor {
constructor(private creditService: CreditClientService) {}
async intercept(context: ExecutionContext, next: CallHandler) {
const operation = Reflector.get('credit_operation', context.getHandler());
const cost = Reflector.get('credit_cost', context.getHandler());
const user = context.switchToHttp().getRequest().user;
// Check balance before operation
const hasCredits = await this.creditService.hasCredits(user.sub, cost);
if (!hasCredits) {
throw new ForbiddenException('Insufficient credits');
}
return next.handle().pipe(
tap(async () => {
// Deduct credits after successful operation
await this.creditService.consumeCredits(user.sub, operation, cost);
}),
);
}
}
```
**Verwendung in Controller:**
```typescript
// apps/chat/apps/backend/src/chat/chat.controller.ts
@Controller('chat')
@UseGuards(AuthGuard)
export class ChatController {
@Post('message')
@UseCredits('chat:ai-response') // 5 Credits
async sendMessage(@Body() dto: SendMessageDto) {
return this.chatService.processMessage(dto);
}
}
```
---
## 4. Cross-Domain SSO
### Problem
Nutzer mussten sich auf jedem Subdomain separat einloggen:
- `auth.mana.how` → Login
- `chat.mana.how` → Login erneut
- `calendar.mana.how` → Login erneut
### Lösung
**Commit:** `f03c09ff` - feat(auth): enable cross-domain SSO via shared cookies
```typescript
// better-auth.config.ts
const auth = betterAuth({
advanced: {
crossSubDomainCookies: {
enabled: true,
domain: process.env.COOKIE_DOMAIN || undefined, // ".mana.how"
},
},
trustedOrigins: [
'https://auth.mana.how',
'https://chat.mana.how',
'https://calendar.mana.how',
'https://todo.mana.how',
// ... alle Apps
],
});
```
```yaml
# docker-compose.macmini.yml
mana-core-auth:
environment:
COOKIE_DOMAIN: ".mana.how"
```
### SSO Flow
```
┌─────────────────────────────────────────────────────────────────┐
│ User Flow │
│ │
│ 1. User besucht calendar.mana.how │
│ → Kein Cookie → Redirect zu auth.mana.how/login │
│ │
│ 2. Login auf auth.mana.how │
│ → Cookie gesetzt: domain=".mana.how" │
│ → Redirect zurück zu calendar.mana.how │
│ │
│ 3. calendar.mana.how │
│ → Cookie vorhanden → Eingeloggt! │
│ │
│ 4. User wechselt zu chat.mana.how │
│ → Cookie vorhanden → Automatisch eingeloggt! │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Matrix SSO Token Handling
**Commit:** `dc0d425f` - fix(matrix-web): handle Matrix SSO loginToken callback
```typescript
// apps/matrix/apps/web/src/lib/matrix/client.ts
export async function loginWithLoginToken(
homeserverUrl: string,
loginToken: string
): Promise<MatrixCredentials> {
const response = await fetch(`${homeserverUrl}/_matrix/client/v3/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'm.login.token',
token: loginToken,
}),
});
const data = await response.json();
return {
userId: data.user_id,
accessToken: data.access_token,
deviceId: data.device_id,
};
}
```
```svelte
<!-- src/routes/(app)/+layout.svelte -->
<script lang="ts">
import { page } from '$app/stores';
import { loginWithLoginToken } from '$lib/matrix';
$effect(() => {
const loginToken = $page.url.searchParams.get('loginToken');
if (loginToken) {
loginWithLoginToken(HOMESERVER_URL, loginToken)
.then(creds => saveCredentials(creds));
}
});
</script>
```
---
## 5. Monitoring & Grafana
### Node-Exporter für Host-Metriken
**Commit:** `7aa5115c` - feat(monitoring): add node-exporter for host system metrics
```yaml
# docker-compose.macmini.yml
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.cpu'
- '--collector.meminfo'
- '--collector.diskstats'
- '--collector.filesystem'
- '--collector.loadavg'
- '--collector.netdev'
ports:
- "9100:9100"
```
### Grafana Dashboards
#### Master Overview
**Commit:** `e7719eeb` - feat(grafana): enhance Master Overview with Key Metrics
Key Metrics Panel (oberste Zeile):
| Panel | Query |
|-------|-------|
| Services UP | `count(up{job=~".*"} == 1)` |
| Apps Running | `count(up{job=~".*-backend\|.*-web"} == 1)` |
| Matrix Bots | `count(up{job=~"matrix-.*"} == 1)` |
| Avg Response Time | `avg(rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m]))` |
| Total Requests | `sum(increase(http_requests_total[24h]))` |
| Requests/sec | `sum(rate(http_requests_total[5m]))` |
| Redis Keys | `redis_db_keys{db="db0"}` |
| Error Rate | `sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))` |
#### System Overview
**Commit:** `84e9f86d` - fix(grafana): rewrite System Overview with available metrics
Host System Section:
```promql
# CPU Usage
100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Memory Usage
(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100
# Disk Usage
(1 - node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100
```
#### Auth Service Dashboard
**Commit:** `fe33f4b3` - feat: add Grafana dashboard for Auth Service monitoring
Panels:
- Login Success/Failure Rate
- Token Validation Latency
- Active Sessions
- Registration Trend
- Password Reset Requests
- OIDC Token Exchange Rate
### Prometheus Alerts
**Commit:** `fe33f4b3` - feat: add 10 auth-specific Prometheus alert rules
```yaml
# docker/prometheus/alerts.yml
groups:
- name: auth_alerts
rules:
- alert: AuthServiceDown
expr: up{job="mana-core-auth"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Auth service is down"
- alert: HighLoginFailureRate
expr: rate(auth_login_failures_total[5m]) / rate(auth_login_attempts_total[5m]) > 0.3
for: 5m
labels:
severity: warning
annotations:
summary: "Login failure rate above 30%"
- alert: TokenValidationSlow
expr: histogram_quantile(0.95, rate(auth_token_validation_seconds_bucket[5m])) > 0.5
for: 5m
labels:
severity: warning
annotations:
summary: "Token validation p95 > 500ms"
```
---
## 6. Auth UI Improvements
### Resend Verification Email
**Commit:** `d703ccfd` - feat(auth): add resend verification email to registration screen
```svelte
<!-- packages/shared-auth-ui/src/pages/RegisterPage.svelte -->
<script lang="ts">
let { onResendVerification } = $props();
let showVerificationSent = $state(false);
let canResend = $state(true);
async function resendEmail() {
canResend = false;
await onResendVerification?.(email);
showVerificationSent = true;
setTimeout(() => canResend = true, 60000); // 1 Minute Cooldown
}
</script>
{#if showVerificationSent}
<div class="verification-success">
<CheckCircle class="icon" />
<h3>{t('auth.verificationEmailSent')}</h3>
<p>{t('auth.checkInbox')}</p>
<button onclick={resendEmail} disabled={!canResend}>
{t('auth.resendVerification')}
</button>
</div>
{/if}
```
### Multilingual Auth Pages
**Commit:** `ff22a297` - feat(i18n): make all auth pages multilingual
15 Apps aktualisiert für dynamische Locale:
```svelte
<!-- VORHER -->
<LoginPage locale="de" ... />
<!-- NACHHER -->
<script>
import { locale } from 'svelte-i18n';
</script>
<LoginPage locale={$locale || 'de'} ... />
```
**Translations erweitert:**
```json
// packages/shared-i18n/src/translations/auth/de.json
{
"verificationEmailSent": "Bestätigungs-E-Mail gesendet!",
"checkInbox": "Bitte überprüfe deinen Posteingang.",
"resendVerification": "Erneut senden",
"notVerifiedError": "E-Mail noch nicht bestätigt",
"resendVerificationPrompt": "Bestätigungs-E-Mail erneut senden?"
}
```
### Fehlende Auth Pages
**Commit:** `df2c518a` - feat(auth): add missing auth pages for zitare and planta
| App | Login | Register | Forgot Password |
|-----|-------|----------|-----------------|
| Zitare | ✅ Neu | ✅ Vorhanden | ✅ Neu |
| Planta | ✅ Vorhanden | ✅ Refactored | ✅ Neu |
---
## 7. SSD Migration
### Motivation
Docker volumes liegen auf der internen SSD des Mac Mini (begrenzt auf 256GB). Externe 4TB SSD bietet mehr Kapazität und bessere Backup-Möglichkeiten.
### Migrierte Services
**Commits:** `1c650589`, `6ca2d3b7`, `7d7e31e4`
```yaml
# docker-compose.macmini.yml
services:
postgres:
volumes:
- /Volumes/ManaData/postgres:/var/lib/postgresql/data
minio:
volumes:
- /Volumes/ManaData/minio:/data
```
### Vergleich
| Aspekt | Docker Volume | SSD Bind Mount |
|--------|---------------|----------------|
| Pfad | `/var/lib/docker/volumes/...` | `/Volumes/ManaData/...` |
| Backup | Docker Export erforderlich | Direkter rsync/cp |
| Größe | Begrenzt (256GB System SSD) | 4TB verfügbar |
| Geschwindigkeit | ~500 MB/s | ~500 MB/s (extern) |
| Portabilität | Docker-spezifisch | Standard-Dateisystem |
### Dokumentation
**Commit:** `9e9db590` - docs: update SSD documentation for ManaData volume
- Umbenennung: TillJakob-S04 → ManaData
- Docker Full Disk Access Requirement dokumentiert
- Backup-Skript-Pfade aktualisiert
---
## 8. mana-core-auth Production Readiness
### Übersicht
Vollständige Production-Readiness für den zentralen Auth-Service.
**Commit:** `efb077b9` - fix(mana-core-auth): use EdDSA for OIDC id_token signing
### Neue Features
#### LoggerService
```typescript
// src/common/logger/logger.service.ts
@Injectable()
export class LoggerService implements LoggerService {
private readonly context: string;
constructor(@Inject('LOGGER_CONTEXT') context?: string) {
this.context = context || 'Application';
}
log(message: string, context?: string) {
console.log(`[${context || this.context}] ${message}`);
}
error(message: string, trace?: string, context?: string) {
console.error(`[${context || this.context}] ${message}`, trace);
}
}
```
#### Environment Validation
```typescript
// src/config/env.validation.ts
import { plainToInstance, Type } from 'class-transformer';
import { IsString, IsNumber, IsUrl, validateSync } from 'class-validator';
export class EnvironmentVariables {
@IsUrl()
BASE_URL: string;
@IsString()
DATABASE_URL: string;
@IsNumber()
@Type(() => Number)
PORT: number = 3001;
@IsString()
JWT_SECRET: string;
// ... weitere Validierungen
}
export function validate(config: Record<string, unknown>) {
const validated = plainToInstance(EnvironmentVariables, config);
const errors = validateSync(validated);
if (errors.length > 0) {
throw new Error(`Config validation error: ${errors}`);
}
return validated;
}
```
#### Health Endpoints
```typescript
// src/health/health.controller.ts
@Controller('health')
export class HealthController {
@Get()
health() {
return { status: 'ok', timestamp: new Date().toISOString() };
}
@Get('live')
liveness() {
return { status: 'ok' };
}
@Get('ready')
async readiness() {
const dbOk = await this.checkDatabase();
const redisOk = await this.checkRedis();
if (!dbOk || !redisOk) {
throw new ServiceUnavailableException('Not ready');
}
return {
status: 'ok',
checks: { database: dbOk, redis: redisOk },
};
}
}
```
### E2E Tests
**Commits:** `ab49be0b`
| Test Suite | Tests | Beschreibung |
|------------|-------|--------------|
| `auth-flow.e2e-spec.ts` | 15 | Register, Login, Logout, Token Refresh |
| `oidc.e2e-spec.ts` | 12 | OIDC Discovery, Authorization, Token Exchange |
```typescript
// test/e2e/auth-flow.e2e-spec.ts
describe('Auth Flow', () => {
it('should register new user', async () => {
const response = await request(app.getHttpServer())
.post('/api/v1/auth/register')
.send({ email: 'test@example.com', password: 'secure123', name: 'Test' })
.expect(201);
expect(response.body).toHaveProperty('user');
expect(response.body.user.email).toBe('test@example.com');
});
it('should login and receive tokens', async () => {
const response = await request(app.getHttpServer())
.post('/api/v1/auth/login')
.send({ email: 'test@example.com', password: 'secure123' })
.expect(200);
expect(response.body).toHaveProperty('accessToken');
expect(response.body).toHaveProperty('refreshToken');
});
});
```
### Dokumentation
Neue Docs erstellt:
- `docs/PRODUCTION_DEPLOYMENT.md` - Deployment Guide
- `docs/DISASTER_RECOVERY.md` - Backup & Recovery Procedures
---
## 9. Matrix Bots Credit Integration
### CreditModule in bot-services
**Commit:** `dc0d425f` - feat(bot-services): add CreditModule
```typescript
// packages/bot-services/src/credit/credit.module.ts
@Module({
providers: [CreditService],
exports: [CreditService],
})
export class CreditModule {
static forRoot(options: CreditModuleOptions): DynamicModule {
return {
module: CreditModule,
providers: [
{ provide: 'CREDIT_OPTIONS', useValue: options },
CreditService,
],
exports: [CreditService],
};
}
}
```
```typescript
// packages/bot-services/src/credit/credit.service.ts
@Injectable()
export class CreditService {
constructor(
@Inject('CREDIT_OPTIONS') private options: CreditModuleOptions,
private httpService: HttpService,
) {}
async hasCredits(userId: string, amount: number): Promise<boolean> {
const balance = await this.getBalance(userId);
return balance >= amount;
}
async consumeCredits(
userId: string,
operation: string,
amount: number,
): Promise<void> {
await this.httpService.post(`${this.options.authUrl}/api/v1/credits/consume`, {
userId,
operation,
amount,
});
}
}
```
### Bot Integration
19 Bots mit Credit Support:
```typescript
// services/matrix-todo-bot/src/bot/matrix.service.ts
@Injectable()
export class MatrixService extends BaseMatrixService {
constructor(private creditService: CreditService) {
super();
}
protected async handleTextMessage(roomId: string, event: MatrixRoomEvent, message: string) {
const userId = event.sender;
// Check credits before AI operations
if (this.isAiCommand(message)) {
const hasCredits = await this.creditService.hasCredits(userId, 5);
if (!hasCredits) {
await this.sendMessage(roomId, '❌ Nicht genug Credits für AI-Features.');
return;
}
}
// Process command...
// Consume credits after success
if (this.isAiCommand(message)) {
await this.creditService.consumeCredits(userId, 'todo:ai-breakdown', 5);
}
}
}
```
---
## 10. Weitere Änderungen
### Calendar Cross-App API URLs
**Commit:** `9a22c898` - fix(calendar-web): inject cross-app API URLs for client-side
```typescript
// apps/calendar/apps/web/src/hooks.server.ts
export const handle: Handle = async ({ event, resolve }) => {
const response = await resolve(event, {
transformPageChunk: ({ html }) => {
return html.replace(
'</head>',
`<script>
window.__PUBLIC_TODO_BACKEND_URL__ = "${process.env.TODO_BACKEND_URL}";
window.__PUBLIC_CONTACTS_API_URL__ = "${process.env.CONTACTS_API_URL}";
</script></head>`
);
},
});
return response;
};
```
### Project-Doc-Bot tsconfig Fix
**Commit:** `a7c1908f` - fix(project-doc-bot): add include/exclude to tsconfig
```json
// services/matrix-project-doc-bot/tsconfig.json
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
```
**Problem:** Build output war `dist/src/main.js` statt `dist/main.js`.
---
## Architektur-Diagramm
### Auth & SSO Flow
```
┌─────────────────────────────────────────────────────────────────┐
│ User Request │
│ calendar.mana.how/events │
└─────────────────────────────────┬───────────────────────────────┘
┌─────────────────────────┐
│ Check Cookie │
│ (.mana.how domain) │
└────────────┬────────────┘
┌──────────────────┴──────────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Cookie Found │ │ No Cookie │
│ │ │ │
│ Validate JWT │ │ Redirect to │
│ via JWKS │ │ auth.mana.how │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ JWT Valid │ │ Login Page │
│ │ │ │
│ Return Data │ │ Set Cookie: │
└─────────────────┘ │ domain=.mana.how│
└────────┬────────┘
┌─────────────────┐
│ Redirect back │
│ to calendar │
└─────────────────┘
```
### Credit System Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Client Apps │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌──────────┐ │
│ │ Chat Web │ │ Picture │ │ Matrix Bot │ │ Mobile │ │
│ │ (Svelte) │ │ (Svelte) │ │ (NestJS) │ │ (Expo) │ │
│ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └────┬─────┘ │
│ │ │ │ │ │
│ └───────────────┴───────┬───────┴───────────────┘ │
│ │ │
│ @manacore/shared-credit-ui │
│ CreditBalance, CreditToast │
└────────────────────────────────┬─────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Backend Services │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ @manacore/credit-operations │ │
│ │ getCreditCost(), CREDIT_OPERATIONS │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ @mana-core/nestjs-integration │ │
│ │ @UseCredits(), CreditInterceptor, AuthGuard │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────┬─────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ mana-core-auth │
│ (Port 3001) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ /credits/ │ │ /credits/ │ │ PostgreSQL │ │
│ │ balance │ │ consume │ │ user_credits table │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
---
## Statistiken
| Metrik | Wert |
|--------|------|
| **Commits** | 51 |
| **Session-Dauer** | 19 Stunden |
| **Neue Packages** | 2 (credit-operations, shared-credit-ui) |
| **CI/CD Bots** | 10 |
| **Auth Pages aktualisiert** | 15 |
| **E2E Tests hinzugefügt** | 27 |
| **Grafana Dashboards** | 3 (Master, System, Auth) |
| **Prometheus Alerts** | 10 |
| **SSD-migrierte Services** | 2 (PostgreSQL, MinIO) |
---
## Bekannte Issues
1. **Matrix Bot arm64** - Keine arm64 Builds wegen QEMU/Native Module Issues
2. **E2EE deaktiviert** - Matrix Bots haben kein End-to-End Encryption (Server handles it)
3. **Docker Full Disk Access** - Muss manuell in Docker Desktop konfiguriert werden
---
## Nächste Schritte
1. **Credit System UI** - Balance-Anzeige in allen Web-Apps integrieren
2. **Rate Limiting** - Für Auth-Endpoints implementieren
3. **Backup Automation** - Scheduled Backups für PostgreSQL/MinIO auf SSD
4. **Bot Healthchecks** - Grafana Alerts für Bot-Ausfälle
5. **OIDC für weitere Apps** - Skilltree, Questions mit Matrix SSO
---
*Bericht erstellt am 2. Februar 2026, 15:00 Uhr*