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

37 KiB

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

// 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

// 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

// 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

# 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

# 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.

# .github/workflows/ci.yml
platforms: linux/amd64  # Kein linux/arm64
# 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

# 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

// 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

# 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:

// 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):

// 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):

<!-- 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:

// 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:

// 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:

// 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

// 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
  ],
});
# 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

// 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,
  };
}
<!-- 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

# 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:

# 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

# 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

<!-- 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:

<!-- VORHER -->
<LoginPage locale="de" ... />

<!-- NACHHER -->
<script>
  import { locale } from 'svelte-i18n';
</script>
<LoginPage locale={$locale || 'de'} ... />

Translations erweitert:

// 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

# 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

// 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

// 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

// 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
// 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

// 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],
    };
  }
}
// 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:

// 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

// 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

// 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