This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/docker-compose.yml
BreakPilot Dev 557305db5d
Some checks failed
ci/woodpecker/push/integration Pipeline failed
ci/woodpecker/push/main Pipeline failed
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
feat: Add Academy, Whistleblower, Incidents SDK modules, pitch-deck, blog and CI/CD config
- Academy, Whistleblower, Incidents frontend pages with API proxies and types
- Vendor compliance API proxy route
- Go backend handlers and models for all new SDK modules
- Investor pitch-deck app with interactive slides
- Blog section with DSGVO, AI Act, NIS2, glossary articles
- MkDocs documentation site
- CI/CD pipelines (Woodpecker, GitHub Actions), security scanning config
- Planning and implementation documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:12:16 +01:00

1856 lines
61 KiB
YAML

services:
# ============================================
# Nginx HTTPS Reverse Proxy
# Enables secure context for microphone/crypto
# Access via HTTPS on same ports as HTTP was before
# ============================================
nginx:
image: nginx:alpine
container_name: breakpilot-pwa-nginx
ports:
- "443:443" # HTTPS Studio v2 (https://macmini/)
- "80:80" # HTTP -> HTTPS redirect
- "3000:3000" # HTTPS Admin Website (https://macmini:3000/)
- "3002:3002" # HTTPS Admin v2 (https://macmini:3002/)
- "8091:8091" # HTTPS Voice Service (wss://macmini:8091/)
- "8000:8000" # HTTPS Backend API
- "8086:8086" # HTTPS Klausur Service
- "8089:8089" # HTTPS Edu-Search proxy (edu-search runs on 8088)
- "8093:8093" # HTTPS AI Compliance SDK
- "8443:8443" # HTTPS Jitsi Meet (https://macmini:8443/)
- "3006:3006" # HTTPS Developer Portal (https://macmini:3006/)
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- vault_certs:/etc/nginx/certs:ro
depends_on:
vault-agent:
condition: service_started
studio-v2:
condition: service_started
voice-service:
condition: service_started
backend:
condition: service_started
klausur-service:
condition: service_started
website:
condition: service_started
ai-compliance-sdk:
condition: service_started
admin-v2:
condition: service_started
jitsi-web:
condition: service_started
developer-portal:
condition: service_started
extra_hosts:
- "breakpilot-edu-search:host-gateway"
networks:
- breakpilot-pwa-network
restart: unless-stopped
# ============================================
# HashiCorp Vault - Secrets Management
# Web UI: http://localhost:8200/ui
# ============================================
vault:
image: hashicorp/vault:1.15
container_name: breakpilot-pwa-vault
cap_add:
- IPC_LOCK
ports:
- "8200:8200"
environment:
- VAULT_DEV_ROOT_TOKEN_ID=${VAULT_DEV_TOKEN:-breakpilot-dev-token}
- VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200
- VAULT_ADDR=http://127.0.0.1:8200
volumes:
- vault_data:/vault/data
healthcheck:
test: ["CMD", "vault", "status"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s
networks:
- breakpilot-pwa-network
restart: unless-stopped
# Vault PKI Initialization - runs once to set up PKI and issue initial certs
vault-init:
image: hashicorp/vault:1.15
container_name: breakpilot-pwa-vault-init
environment:
- VAULT_ADDR=http://vault:8200
- VAULT_TOKEN=${VAULT_DEV_TOKEN:-breakpilot-dev-token}
volumes:
- ./vault/init-pki.sh:/init-pki.sh:ro
- vault_agent_config:/vault/agent/data
- vault_certs:/vault/certs
entrypoint: ["/bin/sh", "-c"]
command:
- |
echo "Waiting for Vault to be ready..."
until vault status > /dev/null 2>&1; do sleep 1; done
echo "Vault is ready. Running PKI initialization..."
chmod +x /init-pki.sh
/init-pki.sh
echo "PKI initialization complete. Exiting."
depends_on:
vault:
condition: service_healthy
networks:
- breakpilot-pwa-network
restart: "no"
# Vault Agent - manages certificate renewal for nginx
vault-agent:
image: hashicorp/vault:1.15
container_name: breakpilot-pwa-vault-agent
environment:
- VAULT_ADDR=http://vault:8200
volumes:
- ./vault/agent/config.hcl:/vault/agent/config.hcl:ro
- ./vault/agent/templates:/vault/agent/templates:ro
- ./vault/agent/split-certs.sh:/vault/agent/split-certs.sh:ro
- vault_agent_config:/vault/agent/data
- vault_certs:/vault/certs
entrypoint: ["vault", "agent", "-config=/vault/agent/config.hcl"]
depends_on:
vault:
condition: service_healthy
vault-init:
condition: service_completed_successfully
networks:
- breakpilot-pwa-network
restart: unless-stopped
# PostgreSQL Database with PostGIS Extension
# PostGIS is required for geo-service (OSM/Terrain features)
postgres:
image: postgis/postgis:16-3.4-alpine
container_name: breakpilot-pwa-postgres
ports:
- "5432:5432"
environment:
POSTGRES_USER: breakpilot
POSTGRES_PASSWORD: breakpilot123
POSTGRES_DB: breakpilot_db
volumes:
- breakpilot_pwa_data:/var/lib/postgresql/data
- ./geo-service/scripts/init_postgis.sql:/docker-entrypoint-initdb.d/10-init-postgis.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U breakpilot -d breakpilot_db"]
interval: 5s
timeout: 5s
retries: 5
networks:
- breakpilot-pwa-network
restart: unless-stopped
# ============================================
# Valkey - Session Cache (Redis-Fork, BSD-3)
# Redis-compatible, 100% Open Source
# ============================================
valkey:
image: valkey/valkey:8-alpine
container_name: breakpilot-pwa-valkey
ports:
- "6379:6379"
volumes:
- valkey_data:/data
command: valkey-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- breakpilot-pwa-network
restart: unless-stopped
# Go Consent Service
consent-service:
build:
context: ./consent-service
dockerfile: Dockerfile
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-consent-service
ports:
- "8081:8081"
environment:
- DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
- JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
- JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:-your-refresh-secret-key-change-in-production}
- PORT=8081
- ENVIRONMENT=${ENVIRONMENT:-development}
- ALLOWED_ORIGINS=http://localhost:8000,http://backend:8000
# Valkey Session Cache
- VALKEY_URL=${VALKEY_URL:-redis://valkey:6379}
- SESSION_TTL_HOURS=${SESSION_TTL_HOURS:-24}
# E-Mail Konfiguration (Mailpit für Entwicklung)
- SMTP_HOST=${SMTP_HOST:-mailpit}
- SMTP_PORT=${SMTP_PORT:-1025}
- SMTP_USERNAME=${SMTP_USERNAME:-}
- SMTP_PASSWORD=${SMTP_PASSWORD:-}
- SMTP_FROM_NAME=${SMTP_FROM_NAME:-BreakPilot}
- SMTP_FROM_ADDR=${SMTP_FROM_ADDR:-noreply@breakpilot.app}
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:8000}
depends_on:
postgres:
condition: service_healthy
valkey:
condition: service_healthy
mailpit:
condition: service_started
networks:
- breakpilot-pwa-network
restart: unless-stopped
# Python Backend
backend:
build:
context: ./backend
dockerfile: Dockerfile
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-backend
expose:
- "8000"
environment:
- CONSENT_SERVICE_URL=http://consent-service:8081
- KLAUSUR_SERVICE_URL=http://klausur-service:8086
- TROCR_SERVICE_URL=${TROCR_SERVICE_URL:-http://host.docker.internal:18084}
- CAMUNDA_URL=http://camunda:8080/engine-rest
- DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
- JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
- ENVIRONMENT=${ENVIRONMENT:-development}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
- DEBUG=${DEBUG:-false}
# Alerts Agent (Google Alerts Monitoring)
- ALERTS_AGENT_ENABLED=${ALERTS_AGENT_ENABLED:-true}
# HashiCorp Vault - Secrets Management
- VAULT_ADDR=${VAULT_ADDR:-http://vault:8200}
- VAULT_TOKEN=${VAULT_TOKEN:-breakpilot-dev-token}
- VAULT_SECRETS_PATH=${VAULT_SECRETS_PATH:-breakpilot}
- USE_VAULT_SECRETS=${USE_VAULT_SECRETS:-true}
# Keycloak Authentication (optional - wenn nicht gesetzt wird lokales JWT verwendet)
- KEYCLOAK_SERVER_URL=${KEYCLOAK_SERVER_URL:-}
- KEYCLOAK_REALM=${KEYCLOAK_REALM:-}
- KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID:-}
- KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET:-}
- KEYCLOAK_VERIFY_SSL=${KEYCLOAK_VERIFY_SSL:-true}
# Valkey Session Cache
- VALKEY_URL=${VALKEY_URL:-redis://valkey:6379}
- SESSION_TTL_HOURS=${SESSION_TTL_HOURS:-24}
# vast.ai GPU Infrastructure
- VAST_API_KEY=${VAST_API_KEY:-}
- VAST_INSTANCE_ID=${VAST_INSTANCE_ID:-}
- CONTROL_API_KEY=${CONTROL_API_KEY:-}
- VAST_AUTO_SHUTDOWN=${VAST_AUTO_SHUTDOWN:-true}
- VAST_AUTO_SHUTDOWN_MINUTES=${VAST_AUTO_SHUTDOWN_MINUTES:-30}
# vLLM Backend
- VLLM_BASE_URL=${VLLM_BASE_URL:-}
- VLLM_ENABLED=${VLLM_ENABLED:-false}
# Ollama Backend (lokal auf Mac Mini - DSGVO-konform)
- OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
- OLLAMA_ENABLED=${OLLAMA_ENABLED:-true}
- OLLAMA_DEFAULT_MODEL=${OLLAMA_DEFAULT_MODEL:-qwen2.5:14b}
- OLLAMA_VISION_MODEL=${OLLAMA_VISION_MODEL:-qwen2.5vl:32b}
- OLLAMA_CORRECTION_MODEL=${OLLAMA_CORRECTION_MODEL:-qwen2.5:14b}
- OLLAMA_TIMEOUT=${OLLAMA_TIMEOUT:-180}
# Breakpilot Drive Game API
- GAME_USE_DATABASE=${GAME_USE_DATABASE:-true}
- GAME_REQUIRE_AUTH=${GAME_REQUIRE_AUTH:-false}
- GAME_REQUIRE_BILLING=${GAME_REQUIRE_BILLING:-false}
- GAME_LLM_MODEL=${GAME_LLM_MODEL:-}
# Compliance LLM Provider Configuration
# Options: "anthropic" (cloud) or "self_hosted" (Ollama/local)
- COMPLIANCE_LLM_PROVIDER=${COMPLIANCE_LLM_PROVIDER:-self_hosted}
- SELF_HOSTED_LLM_URL=${SELF_HOSTED_LLM_URL:-http://host.docker.internal:11434}
- SELF_HOSTED_LLM_MODEL=${SELF_HOSTED_LLM_MODEL:-llama3.1:70b}
- COMPLIANCE_LLM_MAX_TOKENS=${COMPLIANCE_LLM_MAX_TOKENS:-4096}
- COMPLIANCE_LLM_TEMPERATURE=${COMPLIANCE_LLM_TEMPERATURE:-0.3}
- COMPLIANCE_LLM_TIMEOUT=${COMPLIANCE_LLM_TIMEOUT:-120}
# E-Mail Konfiguration (Mailpit fuer Entwicklung)
- SMTP_HOST=${SMTP_HOST:-mailpit}
- SMTP_PORT=${SMTP_PORT:-1025}
- SMTP_USERNAME=${SMTP_USERNAME:-}
- SMTP_PASSWORD=${SMTP_PASSWORD:-}
- SMTP_FROM_NAME=${SMTP_FROM_NAME:-BreakPilot}
- SMTP_FROM_ADDR=${SMTP_FROM_ADDR:-noreply@breakpilot.app}
extra_hosts:
- "host.docker.internal:host-gateway"
- "mac-mini:192.168.178.163"
volumes:
# Mount Docker socket for container monitoring (Mac Mini Control)
- /var/run/docker.sock:/var/run/docker.sock:ro
# Mount SBOM files for Security Dashboard
- ./docs/sbom:/app/docs/sbom:ro
# Mount Projekt-Verzeichnis fuer Test-Dashboard (echte Test-Discovery)
- /Users/benjaminadmin/Projekte/breakpilot-pwa:/app/project:ro
depends_on:
- consent-service
- valkey
- mailpit
networks:
- breakpilot-pwa-network
restart: unless-stopped
# Automatic Database Backup (runs daily at 2 AM)
backup:
image: postgres:16-alpine
container_name: breakpilot-pwa-backup
volumes:
- ./backups:/backups
- postgres_data:/var/lib/postgresql/data:ro
environment:
- PGHOST=postgres
- PGUSER=breakpilot
- PGPASSWORD=breakpilot123
- PGDATABASE=breakpilot_db
entrypoint: ["/bin/sh", "-c"]
command:
- |
echo "Backup service started. Running backup every day at 2 AM..."
while true; do
# Berechne Sekunden bis 2 Uhr
current_hour=$$(date +%H)
current_min=$$(date +%M)
current_sec=$$(date +%S)
if [ $$current_hour -lt 2 ]; then
sleep_hours=$$((2 - current_hour - 1))
else
sleep_hours=$$((26 - current_hour - 1))
fi
sleep_mins=$$((60 - current_min - 1))
sleep_secs=$$((60 - current_sec))
total_sleep=$$((sleep_hours * 3600 + sleep_mins * 60 + sleep_secs))
echo "Next backup in $$total_sleep seconds (at 2:00 AM)"
sleep $$total_sleep
# Backup erstellen
TIMESTAMP=$$(date +"%Y%m%d_%H%M%S")
BACKUP_FILE="/backups/breakpilot_$${TIMESTAMP}.sql.gz"
echo "Creating backup: $$BACKUP_FILE"
pg_dump | gzip > "$$BACKUP_FILE"
if [ $$? -eq 0 ]; then
echo "✓ Backup completed: $$BACKUP_FILE"
# Alte Backups löschen (älter als 30 Tage)
find /backups -name "breakpilot_*.sql.gz" -mtime +30 -delete
echo "✓ Old backups cleaned up"
else
echo "✗ Backup failed!"
fi
done
depends_on:
postgres:
condition: service_healthy
networks:
- breakpilot-pwa-network
restart: unless-stopped
profiles:
- backup
# Mailpit - Lokaler E-Mail-Server für Entwicklung
# Web UI: http://localhost:8025
# SMTP: localhost:1025
mailpit:
image: axllent/mailpit:latest
container_name: breakpilot-pwa-mailpit
ports:
- "8025:8025" # Web UI
- "1025:1025" # SMTP Server
environment:
- MP_SMTP_AUTH_ACCEPT_ANY=true
- MP_SMTP_AUTH_ALLOW_INSECURE=true
networks:
- breakpilot-pwa-network
restart: unless-stopped
# Matrix Synapse - Schulkommunikation (E2EE Messenger)
# Admin: http://localhost:8008/_synapse/admin
synapse:
image: matrixdotorg/synapse:latest
container_name: breakpilot-pwa-synapse
ports:
- "8008:8008" # Client-Server API
- "8448:8448" # Federation (optional für später)
volumes:
- synapse_data:/data
environment:
- SYNAPSE_SERVER_NAME=${MATRIX_SERVER_NAME:-breakpilot.local}
- SYNAPSE_REPORT_STATS=no
- SYNAPSE_NO_TLS=1
- SYNAPSE_ENABLE_REGISTRATION=no
- SYNAPSE_LOG_LEVEL=INFO
- UID=1000
- GID=1000
healthcheck:
test: ["CMD-SHELL", "curl -fSs http://localhost:8008/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
networks:
- breakpilot-pwa-network
restart: unless-stopped
# PostgreSQL für Matrix Synapse (separate DB)
synapse-db:
image: postgres:16-alpine
container_name: breakpilot-pwa-synapse-db
environment:
POSTGRES_USER: synapse
POSTGRES_PASSWORD: ${SYNAPSE_DB_PASSWORD:-synapse_secret_123}
POSTGRES_DB: synapse
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C"
volumes:
- synapse_db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U synapse -d synapse"]
interval: 5s
timeout: 5s
retries: 5
networks:
- breakpilot-pwa-network
restart: unless-stopped
# Go School Service (Klausuren, Noten, Zeugnisse)
school-service:
build:
context: ./school-service
dockerfile: Dockerfile
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-school-service
ports:
- "8084:8084"
environment:
- DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
- JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
- PORT=8084
- ENVIRONMENT=${ENVIRONMENT:-development}
- ALLOWED_ORIGINS=http://localhost:8000,http://backend:8000
- LLM_GATEWAY_URL=http://backend:8000/llm
depends_on:
postgres:
condition: service_healthy
networks:
- breakpilot-pwa-network
restart: unless-stopped
# Embedding Service (ML-heavy operations)
# Handles: embeddings, re-ranking, PDF extraction
# Separated for faster klausur-service builds
embedding-service:
build:
context: ./klausur-service/embedding-service
dockerfile: Dockerfile
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-embedding-service
ports:
- "8087:8087"
environment:
- EMBEDDING_BACKEND=${EMBEDDING_BACKEND:-local}
- LOCAL_EMBEDDING_MODEL=${LOCAL_EMBEDDING_MODEL:-BAAI/bge-m3}
- LOCAL_RERANKER_MODEL=${LOCAL_RERANKER_MODEL:-BAAI/bge-reranker-v2-m3}
- PDF_EXTRACTION_BACKEND=${PDF_EXTRACTION_BACKEND:-auto}
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- COHERE_API_KEY=${COHERE_API_KEY:-}
- LOG_LEVEL=${LOG_LEVEL:-INFO}
volumes:
- embedding_models:/root/.cache/huggingface
deploy:
resources:
limits:
memory: 4G
healthcheck:
test: ["CMD", "python", "-c", "import httpx; httpx.get('http://localhost:8087/health').raise_for_status()"]
interval: 30s
timeout: 10s
start_period: 120s
retries: 3
networks:
- breakpilot-pwa-network
restart: unless-stopped
# Klausur-Service (Abitur/Vorabitur Klausurkorrektur)
# React + FastAPI Microservice
# Web UI: http://localhost:8086
klausur-service:
build:
context: ./klausur-service
dockerfile: Dockerfile
platform: linux/arm64 # Native ARM64 - PaddlePaddle 3.3.0 unterstützt ARM64
container_name: breakpilot-pwa-klausur-service
expose:
- "8086"
environment:
- JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
- BACKEND_URL=http://backend:8000
- SCHOOL_SERVICE_URL=http://school-service:8084
- ENVIRONMENT=${ENVIRONMENT:-development}
# PostgreSQL for OCR Labeling & Metrics
- DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db
# Embedding Service (ML operations)
- EMBEDDING_SERVICE_URL=http://embedding-service:8087
# BYOEH Configuration
- QDRANT_URL=http://qdrant:6333
- BYOEH_ENCRYPTION_ENABLED=true
- BYOEH_CHUNK_SIZE=1000
- BYOEH_CHUNK_OVERLAP=200
# MinIO Configuration (RAG Document Storage)
- MINIO_ENDPOINT=minio:9000
- MINIO_ACCESS_KEY=${MINIO_ROOT_USER:-breakpilot}
- MINIO_SECRET_KEY=${MINIO_ROOT_PASSWORD:-breakpilot123}
- MINIO_BUCKET=breakpilot-rag
- MINIO_SECURE=false
# Ollama LLM Configuration
- OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
- OLLAMA_ENABLED=${OLLAMA_ENABLED:-true}
- OLLAMA_DEFAULT_MODEL=${OLLAMA_DEFAULT_MODEL:-qwen2.5:14b}
- OLLAMA_VISION_MODEL=${OLLAMA_VISION_MODEL:-qwen2.5vl:32b}
- OLLAMA_CORRECTION_MODEL=${OLLAMA_CORRECTION_MODEL:-qwen2.5:14b}
# PaddleOCR Service (x86_64 via Rosetta)
- PADDLEOCR_SERVICE_URL=http://paddleocr-service:8095
# HashiCorp Vault - Anthropic API Key for Loesung E
- VAULT_ADDR=http://vault:8200
- VAULT_TOKEN=${VAULT_DEV_TOKEN:-breakpilot-dev-token}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
volumes:
- klausur_uploads:/app/uploads
- eh_uploads:/app/eh-uploads
- ocr_labeling:/app/ocr-labeling
- paddle_models:/root/.paddlex # Persist PaddleOCR models across restarts
- ./docs:/app/docs # NIBIS Abitur-Dateien
depends_on:
- backend
- school-service
- embedding-service
- postgres
- qdrant
- minio
- paddleocr-service
- vault
networks:
- breakpilot-pwa-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8086/health"]
interval: 30s
timeout: 30s
retries: 3
start_period: 10s
restart: unless-stopped
# ============================================
# PaddleOCR Service - x86_64 via Rosetta
# Runs in emulation to avoid ARM64 crashes
# ============================================
paddleocr-service:
build:
context: ./paddleocr-service
dockerfile: Dockerfile
platform: linux/amd64 # Force x86_64 emulation via Rosetta
container_name: breakpilot-pwa-paddleocr
expose:
- "8095"
environment:
- PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK=True
volumes:
- paddleocr_models:/root/.paddlex # Cache PaddleX models
- paddleocr_models:/root/.paddleocr # Cache PaddleOCR 3.x models
networks:
- breakpilot-pwa-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8095/health"]
interval: 30s
timeout: 60s
retries: 5
start_period: 180s # Models need time to load in emulation
restart: unless-stopped
# Qdrant Vector Database (BYOEH - Erwartungshorizont RAG)
# REST API: http://localhost:6333
# gRPC: localhost:6334
qdrant:
image: qdrant/qdrant:v1.12.1
container_name: breakpilot-pwa-qdrant
ports:
- "6333:6333"
- "6334:6334"
volumes:
- qdrant_data:/qdrant/storage
environment:
- QDRANT__SERVICE__GRPC_PORT=6334
healthcheck:
test: ["CMD-SHELL", "bash -c '</dev/tcp/localhost/6333'"]
interval: 10s
timeout: 5s
retries: 3
networks:
- breakpilot-pwa-network
restart: unless-stopped
# MinIO Object Storage (RAG Document Storage)
# Web Console: http://localhost:9001
# S3 API: http://localhost:9000
minio:
image: minio/minio:latest
container_name: breakpilot-pwa-minio
ports:
- "9000:9000" # S3 API
- "9001:9001" # Web Console
environment:
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-breakpilot}
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-breakpilot123}
volumes:
- minio_data:/data
command: server /data --console-address ":9001"
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 10s
timeout: 5s
retries: 3
networks:
- breakpilot-pwa-network
restart: unless-stopped
# Go Billing Service
billing-service:
build:
context: ./billing-service
dockerfile: Dockerfile
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-billing-service
ports:
- "8083:8083"
environment:
- DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
- JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
- PORT=8083
- ENVIRONMENT=${ENVIRONMENT:-development}
- ALLOWED_ORIGINS=http://localhost:8000,http://backend:8000
# Stripe Configuration
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY:-}
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET:-}
- STRIPE_PUBLISHABLE_KEY=${STRIPE_PUBLISHABLE_KEY:-}
# URLs
- BILLING_SUCCESS_URL=${BILLING_SUCCESS_URL:-http://localhost:3000/success}
- BILLING_CANCEL_URL=${BILLING_CANCEL_URL:-http://localhost:3000/cancel}
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:8000}
# Trial
- TRIAL_PERIOD_DAYS=${TRIAL_PERIOD_DAYS:-7}
# Internal API
- INTERNAL_API_KEY=${INTERNAL_API_KEY:-internal-service-key-change-in-production}
depends_on:
postgres:
condition: service_healthy
networks:
- breakpilot-pwa-network
restart: unless-stopped
# Website - BreakPilot Landing/Pricing Page (Next.js)
# HTTPS via nginx on port 3000
website:
build:
context: ./website
dockerfile: Dockerfile
args:
- NEXT_PUBLIC_BILLING_API_URL=${NEXT_PUBLIC_BILLING_API_URL:-https://macmini:8083}
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-https://macmini:8000}
- NEXT_PUBLIC_KLAUSUR_SERVICE_URL=${NEXT_PUBLIC_KLAUSUR_SERVICE_URL:-https://macmini:8086}
- NEXT_PUBLIC_VOICE_SERVICE_URL=${NEXT_PUBLIC_VOICE_SERVICE_URL:-https://macmini:8091}
- NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${STRIPE_PUBLISHABLE_KEY:-}
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-website
expose:
- "3000"
environment:
- NODE_ENV=production
- VAST_API_KEY=${VAST_API_KEY:-}
- CONTROL_API_KEY=${CONTROL_API_KEY:-}
- BACKEND_URL=http://backend:8000
- CONSENT_SERVICE_URL=http://consent-service:8081
# Edu-Search Service (for uni-crawler admin)
- EDU_SEARCH_URL=${EDU_SEARCH_URL:-http://breakpilot-edu-search:8086}
- EDU_SEARCH_API_KEY=${EDU_SEARCH_API_KEY:-dev-key}
# Unity AI Bridge (runs on host machine in Unity Editor)
- UNITY_BRIDGE_URL=http://host.docker.internal:8090
# Woodpecker CI/CD Status Widget
- WOODPECKER_URL=${WOODPECKER_URL:-http://woodpecker-server:8000}
- WOODPECKER_TOKEN=${WOODPECKER_TOKEN:-}
depends_on:
- billing-service
- backend
- consent-service
networks:
- breakpilot-pwa-network
restart: unless-stopped
# ============================================
# Studio v2 - Neue Lehrer-Oberfläche
# Next.js auf Port 3001
# ============================================
studio-v2:
build:
context: ./studio-v2
dockerfile: Dockerfile
args:
- NEXT_PUBLIC_VOICE_SERVICE_URL=${NEXT_PUBLIC_VOICE_SERVICE_URL:-https://macmini:8091}
- NEXT_PUBLIC_KLAUSUR_SERVICE_URL=${NEXT_PUBLIC_KLAUSUR_SERVICE_URL:-https://macmini:8086}
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-studio-v2
# Port 443 wird von nginx HTTPS bereitgestellt (studio-v2 intern auf 3001)
# Port 3004 ist HTTP-only für Mobile Uploads (HSTS-Umgehung)
ports:
- "3004:3001" # HTTP-only port for mobile QR uploads
expose:
- "3001"
environment:
- NODE_ENV=production
- BACKEND_URL=http://backend:8000
depends_on:
- backend
networks:
- breakpilot-pwa-network
restart: unless-stopped
# ============================================
# Admin v2 - Neues Admin-Frontend
# Next.js auf Port 3002
# ============================================
admin-v2:
build:
context: ./admin-v2
dockerfile: Dockerfile
args:
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-https://macmini:8000}
- NEXT_PUBLIC_OLD_ADMIN_URL=${NEXT_PUBLIC_OLD_ADMIN_URL:-https://macmini:3000/admin}
- NEXT_PUBLIC_SDK_URL=${NEXT_PUBLIC_SDK_URL:-https://macmini:8093}
- NEXT_PUBLIC_KLAUSUR_SERVICE_URL=${NEXT_PUBLIC_KLAUSUR_SERVICE_URL:-/klausur-api}
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-admin-v2
# Port 3002 wird über nginx proxied (admin-v2:3000 intern)
# Port 3005 ist HTTP-only für Mobile Uploads (HSTS-Umgehung)
ports:
- "3005:3000" # HTTP-only port for mobile QR uploads
expose:
- "3000"
environment:
- NODE_ENV=production
- BACKEND_URL=http://backend:8000
- CONSENT_SERVICE_URL=http://consent-service:8081
- KLAUSUR_SERVICE_URL=http://klausur-service:8086
- SDK_URL=http://ai-compliance-sdk:8090
# Woodpecker CI Status
- WOODPECKER_URL=${WOODPECKER_URL:-http://woodpecker-server:8000}
- WOODPECKER_TOKEN=${WOODPECKER_TOKEN:-}
# Compliance Advisor LLM
- OLLAMA_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
- COMPLIANCE_LLM_MODEL=${COMPLIANCE_LLM_MODEL:-qwen2.5vl:32b}
volumes:
# Mount Docker socket for Mac Mini monitoring dashboard
- /var/run/docker.sock:/var/run/docker.sock:ro
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
- backend
- consent-service
- ai-compliance-sdk
networks:
- breakpilot-pwa-network
restart: unless-stopped
# ============================================
# Developer Portal - Oeffentliches SDK-Dokumentationsportal
# Access: https://macmini:3006/
# ============================================
developer-portal:
build:
context: ./developer-portal
dockerfile: Dockerfile
platform: linux/arm64
container_name: breakpilot-pwa-developer-portal
expose:
- "3000"
environment:
- NODE_ENV=production
networks:
- breakpilot-pwa-network
restart: unless-stopped
# ============================================
# Pitch Deck - Interactive Investor Presentation
# Next.js auf Port 3012
# ============================================
pitch-deck:
build:
context: ./pitch-deck
dockerfile: Dockerfile
platform: linux/arm64
container_name: breakpilot-pwa-pitch-deck
ports:
- "3012:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://breakpilot:breakpilot123@host.docker.internal:5432/breakpilot_db
- OLLAMA_URL=http://host.docker.internal:11434
- OLLAMA_MODEL=qwen2.5:32b
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
- breakpilot-pwa-network
restart: unless-stopped
# ============================================
# AI Compliance SDK - Multi-Tenant RBAC & LLM Gateway
# Go auf Port 8090 (intern), 8093 (extern)
# CFO Use-Case: Namespace-isolierte KI-Nutzung
# ============================================
ai-compliance-sdk:
build:
context: ./ai-compliance-sdk
dockerfile: Dockerfile
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-ai-compliance-sdk
# Port 8093 wird über nginx proxied (ai-compliance-sdk:8090 intern)
environment:
- PORT=8090
- ENVIRONMENT=${ENVIRONMENT:-development}
# PostgreSQL for RBAC, Policies, Audit
- DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
- JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
# LLM Provider Configuration
- LLM_PROVIDER=${SDK_LLM_PROVIDER:-ollama}
- LLM_FALLBACK_PROVIDER=${SDK_LLM_FALLBACK_PROVIDER:-anthropic}
# Ollama (Mac Mini lokal - DSGVO-konform)
- OLLAMA_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
- OLLAMA_DEFAULT_MODEL=${OLLAMA_DEFAULT_MODEL:-qwen2.5:14b}
# Anthropic (Cloud-Fallback via Syseleven BSI-Cloud)
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
- ANTHROPIC_DEFAULT_MODEL=${ANTHROPIC_DEFAULT_MODEL:-claude-sonnet-4-20250514}
# PII Detection & Redaction
- PII_REDACTION_ENABLED=${PII_REDACTION_ENABLED:-true}
- PII_REDACTION_LEVEL=${PII_REDACTION_LEVEL:-strict}
# Audit Trail
- AUDIT_RETENTION_DAYS=${AUDIT_RETENTION_DAYS:-365}
- AUDIT_LOG_PROMPTS=${AUDIT_LOG_PROMPTS:-false}
# CORS
- ALLOWED_ORIGINS=http://localhost:3002,https://macmini:3002,http://admin-v2:3000
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
postgres:
condition: service_healthy
networks:
- breakpilot-pwa-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8090/health"]
interval: 30s
timeout: 3s
start_period: 10s
retries: 3
restart: unless-stopped
# DSMS Node - Dezentrales Speichersystem (Private IPFS)
dsms-node:
build:
context: ./dsms-node
dockerfile: Dockerfile
container_name: breakpilot-pwa-dsms-node
ports:
- "4001:4001" # Swarm P2P
- "5001:5001" # IPFS API
- "8085:8080" # IPFS Gateway (8085 um Konflikt mit Backend zu vermeiden)
volumes:
- dsms_data:/data/ipfs
environment:
- IPFS_PROFILE=server
networks:
- breakpilot-pwa-network
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "ipfs id > /dev/null 2>&1 || exit 1"]
interval: 30s
timeout: 10s
start_period: 30s
retries: 3
# DSMS Gateway - REST API für DSMS
dsms-gateway:
build:
context: ./dsms-gateway
dockerfile: Dockerfile
container_name: breakpilot-pwa-dsms-gateway
ports:
- "8082:8082"
environment:
- IPFS_API_URL=http://dsms-node:5001
- IPFS_GATEWAY_URL=http://dsms-node:8080
- JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
depends_on:
dsms-node:
condition: service_healthy
networks:
- breakpilot-pwa-network
restart: unless-stopped
# ============================================
# Jitsi Meet - Videokonferenzen für Schulungen
# Web UI: http://localhost:8443
# ============================================
# Jitsi Web Frontend
jitsi-web:
image: jitsi/web:stable-9823
container_name: breakpilot-pwa-jitsi-web
expose:
- "80"
environment:
- ENABLE_XMPP_WEBSOCKET=1
- ENABLE_COLIBRI_WEBSOCKET=1
- XMPP_DOMAIN=meet.jitsi
- XMPP_BOSH_URL_BASE=http://jitsi-xmpp:5280
- XMPP_MUC_DOMAIN=muc.meet.jitsi
- XMPP_GUEST_DOMAIN=guest.meet.jitsi
- TZ=Europe/Berlin
- PUBLIC_URL=${JITSI_PUBLIC_URL:-https://macmini}
- JICOFO_AUTH_USER=focus
- ENABLE_AUTH=${JITSI_ENABLE_AUTH:-0}
- ENABLE_GUESTS=${JITSI_ENABLE_GUESTS:-1}
- ENABLE_RECORDING=${JITSI_ENABLE_RECORDING:-1}
- ENABLE_LIVESTREAMING=0
- DISABLE_HTTPS=1
# Branding
- APP_NAME=BreakPilot Meet
- NATIVE_APP_NAME=BreakPilot Meet
- PROVIDER_NAME=BreakPilot
volumes:
- jitsi_web_config:/config
- jitsi_web_crontabs:/var/spool/cron/crontabs
- jitsi_transcripts:/usr/share/jitsi-meet/transcripts
networks:
breakpilot-pwa-network:
aliases:
- meet.jitsi
restart: unless-stopped
depends_on:
- jitsi-xmpp
# Prosody XMPP Server
jitsi-xmpp:
image: jitsi/prosody:stable-9823
container_name: breakpilot-pwa-jitsi-xmpp
environment:
- XMPP_DOMAIN=meet.jitsi
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- XMPP_MUC_DOMAIN=muc.meet.jitsi
- XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi
- XMPP_GUEST_DOMAIN=guest.meet.jitsi
- XMPP_RECORDER_DOMAIN=recorder.meet.jitsi
- XMPP_CROSS_DOMAIN=true
- TZ=Europe/Berlin
- JICOFO_AUTH_USER=focus
- JICOFO_AUTH_PASSWORD=${JITSI_JICOFO_AUTH_PASSWORD:-jicofo_secret_123}
- JVB_AUTH_USER=jvb
- JVB_AUTH_PASSWORD=${JITSI_JVB_AUTH_PASSWORD:-jvb_secret_123}
- JIGASI_XMPP_USER=jigasi
- JIGASI_XMPP_PASSWORD=${JITSI_JIGASI_XMPP_PASSWORD:-jigasi_secret_123}
- JIBRI_XMPP_USER=jibri
- JIBRI_XMPP_PASSWORD=${JITSI_JIBRI_XMPP_PASSWORD:-jibri_secret_123}
- JIBRI_RECORDER_USER=recorder
- JIBRI_RECORDER_PASSWORD=${JITSI_JIBRI_RECORDER_PASSWORD:-recorder_secret_123}
- LOG_LEVEL=info
- PUBLIC_URL=${JITSI_PUBLIC_URL:-https://macmini}
- ENABLE_AUTH=${JITSI_ENABLE_AUTH:-0}
- ENABLE_GUESTS=${JITSI_ENABLE_GUESTS:-1}
volumes:
- jitsi_prosody_config:/config
- jitsi_prosody_plugins:/prosody-plugins-custom
networks:
breakpilot-pwa-network:
aliases:
- xmpp.meet.jitsi
restart: unless-stopped
# Jicofo - Jitsi Conference Focus
jitsi-jicofo:
image: jitsi/jicofo:stable-9823
container_name: breakpilot-pwa-jitsi-jicofo
environment:
- XMPP_DOMAIN=meet.jitsi
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- XMPP_MUC_DOMAIN=muc.meet.jitsi
- XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi
- XMPP_SERVER=jitsi-xmpp
- JICOFO_AUTH_USER=focus
- JICOFO_AUTH_PASSWORD=${JITSI_JICOFO_AUTH_PASSWORD:-jicofo_secret_123}
- TZ=Europe/Berlin
- ENABLE_AUTH=${JITSI_ENABLE_AUTH:-0}
- AUTH_TYPE=internal
- ENABLE_AUTO_OWNER=${JITSI_ENABLE_AUTO_OWNER:-1}
volumes:
- jitsi_jicofo_config:/config
networks:
- breakpilot-pwa-network
restart: unless-stopped
depends_on:
- jitsi-xmpp
# JVB - Jitsi Videobridge (WebRTC SFU)
jitsi-jvb:
image: jitsi/jvb:stable-9823
container_name: breakpilot-pwa-jitsi-jvb
ports:
- "10000:10000/udp" # Video/Audio RTP
- "8080:8080" # Colibri REST API (internal)
environment:
- XMPP_DOMAIN=meet.jitsi
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi
- XMPP_SERVER=jitsi-xmpp
- JVB_AUTH_USER=jvb
- JVB_AUTH_PASSWORD=${JITSI_JVB_AUTH_PASSWORD:-jvb_secret_123}
- JVB_PORT=10000
- JVB_STUN_SERVERS=meet-jit-si-turnrelay.jitsi.net:443
- TZ=Europe/Berlin
- PUBLIC_URL=${JITSI_PUBLIC_URL:-https://macmini}
- COLIBRI_REST_ENABLED=true
- ENABLE_COLIBRI_WEBSOCKET=1
volumes:
- jitsi_jvb_config:/config
networks:
- breakpilot-pwa-network
restart: unless-stopped
depends_on:
- jitsi-xmpp
# ============================================
# Jibri - Jitsi Recording Service
# Recordings werden zu MinIO hochgeladen
# ============================================
jibri:
build:
context: ./docker/jibri
dockerfile: Dockerfile
container_name: breakpilot-pwa-jibri
environment:
- XMPP_DOMAIN=meet.jitsi
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi
- XMPP_RECORDER_DOMAIN=recorder.meet.jitsi
- XMPP_SERVER=jitsi-xmpp
- XMPP_MUC_DOMAIN=muc.meet.jitsi
- JIBRI_XMPP_USER=jibri
- JIBRI_XMPP_PASSWORD=${JITSI_JIBRI_XMPP_PASSWORD:-jibri_secret_123}
- JIBRI_RECORDER_USER=recorder
- JIBRI_RECORDER_PASSWORD=${JITSI_JIBRI_RECORDER_PASSWORD:-recorder_secret_123}
- JIBRI_BREWERY_MUC=jibribrewery
- JIBRI_RECORDING_DIR=/recordings
- JIBRI_FINALIZE_SCRIPT=/config/finalize.sh
- TZ=Europe/Berlin
# X11 Display Konfiguration (Xvfb)
- DISPLAY=:0
- RESOLUTION=1920x1080x24
# Optional: VNC fuer Debugging (Port 5900)
# - VNC_PASSWORD=debug123
# MinIO Upload Konfiguration
- MINIO_ENDPOINT=minio:9000
- MINIO_ACCESS_KEY=${MINIO_ROOT_USER}
- MINIO_SECRET_KEY=${MINIO_ROOT_PASSWORD}
- MINIO_BUCKET=breakpilot-recordings
# Backend Webhook (wird nach Upload aufgerufen)
- BACKEND_WEBHOOK_URL=http://backend:8000/api/recordings/webhook
volumes:
- jibri_recordings:/recordings
- /dev/shm:/dev/shm
shm_size: '2gb'
cap_add:
- SYS_ADMIN
- NET_BIND_SERVICE
networks:
- breakpilot-pwa-network
restart: unless-stopped
depends_on:
- jitsi-xmpp
- minio
profiles:
- recording
# ============================================
# Transcription Worker - Whisper + pyannote
# Verarbeitet Recordings asynchron
# ============================================
transcription-worker:
build:
context: ./backend
dockerfile: Dockerfile.worker
container_name: breakpilot-pwa-transcription-worker
environment:
- DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
- REDIS_URL=redis://valkey:6379/1
- WHISPER_MODEL=${WHISPER_MODEL:-large-v3}
- WHISPER_DEVICE=${WHISPER_DEVICE:-cpu}
- WHISPER_COMPUTE_TYPE=${WHISPER_COMPUTE_TYPE:-int8}
# pyannote.audio Token (HuggingFace)
- PYANNOTE_AUTH_TOKEN=${PYANNOTE_AUTH_TOKEN:-}
# MinIO Storage
- MINIO_ENDPOINT=${MINIO_ENDPOINT:-minio:9000}
- MINIO_ACCESS_KEY=${MINIO_ROOT_USER:-breakpilot}
- MINIO_SECRET_KEY=${MINIO_ROOT_PASSWORD:-breakpilot123}
- MINIO_BUCKET=breakpilot-recordings
- MINIO_SECURE=false
- TZ=Europe/Berlin
volumes:
- transcription_models:/root/.cache/huggingface
- transcription_temp:/tmp/transcriptions
deploy:
resources:
limits:
memory: 8G
reservations:
memory: 4G
networks:
- breakpilot-pwa-network
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
valkey:
condition: service_healthy
minio:
condition: service_started
profiles:
- recording
# ============================================
# ERPNext - Open Source ERP System
# Web UI: http://localhost:8090
# Default: Administrator / admin
# ============================================
# MariaDB for ERPNext
erpnext-db:
image: mariadb:10.6
container_name: breakpilot-pwa-erpnext-db
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --skip-character-set-client-handshake --skip-innodb-read-only-compressed
environment:
- MYSQL_ROOT_PASSWORD=${ERPNEXT_DB_ROOT_PASSWORD:-changeit123}
- MYSQL_DATABASE=erpnext
- MYSQL_USER=erpnext
- MYSQL_PASSWORD=${ERPNEXT_DB_PASSWORD:-erpnext123}
volumes:
- erpnext_db_data:/var/lib/mysql
networks:
- breakpilot-pwa-network
restart: unless-stopped
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${ERPNEXT_DB_ROOT_PASSWORD:-changeit123}"]
interval: 5s
timeout: 5s
retries: 10
# Redis Queue for ERPNext
erpnext-redis-queue:
image: redis:alpine
container_name: breakpilot-pwa-erpnext-redis-queue
volumes:
- erpnext_redis_queue_data:/data
networks:
- breakpilot-pwa-network
restart: unless-stopped
# Redis Cache for ERPNext
erpnext-redis-cache:
image: redis:alpine
container_name: breakpilot-pwa-erpnext-redis-cache
volumes:
- erpnext_redis_cache_data:/data
networks:
- breakpilot-pwa-network
restart: unless-stopped
# ERPNext Site Creator (runs once)
erpnext-create-site:
image: frappe/erpnext:latest
container_name: breakpilot-pwa-erpnext-create-site
command:
- bash
- -c
- |
wait-for-it -t 120 erpnext-db:3306;
wait-for-it -t 120 erpnext-redis-cache:6379;
wait-for-it -t 120 erpnext-redis-queue:6379;
if [[ ! -f sites/erpnext.local/site_config.json ]]; then
echo "Creating ERPNext site...";
bench new-site erpnext.local --db-host=erpnext-db --db-port=3306 --admin-password=admin --db-root-password=${ERPNEXT_DB_ROOT_PASSWORD:-changeit123} --install-app erpnext --set-default;
echo "Site created successfully!";
else
echo "Site already exists, skipping creation.";
fi;
environment:
- DB_HOST=erpnext-db
- DB_PORT=3306
- REDIS_CACHE=redis://erpnext-redis-cache:6379
- REDIS_QUEUE=redis://erpnext-redis-queue:6379
- SOCKETIO_PORT=9000
volumes:
- erpnext_sites:/home/frappe/frappe-bench/sites
- erpnext_logs:/home/frappe/frappe-bench/logs
networks:
- breakpilot-pwa-network
depends_on:
erpnext-db:
condition: service_healthy
erpnext-redis-cache:
condition: service_started
erpnext-redis-queue:
condition: service_started
# ERPNext Backend
erpnext-backend:
image: frappe/erpnext:latest
container_name: breakpilot-pwa-erpnext-backend
command:
- bash
- -c
- |
wait-for-it -t 120 erpnext-db:3306;
wait-for-it -t 120 erpnext-redis-cache:6379;
wait-for-it -t 120 erpnext-redis-queue:6379;
bench serve --port 8000
environment:
- DB_HOST=erpnext-db
- DB_PORT=3306
- REDIS_CACHE=redis://erpnext-redis-cache:6379
- REDIS_QUEUE=redis://erpnext-redis-queue:6379
- SOCKETIO_PORT=9000
volumes:
- erpnext_sites:/home/frappe/frappe-bench/sites
- erpnext_logs:/home/frappe/frappe-bench/logs
networks:
- breakpilot-pwa-network
restart: unless-stopped
depends_on:
erpnext-db:
condition: service_healthy
erpnext-create-site:
condition: service_completed_successfully
# ERPNext WebSocket
erpnext-websocket:
image: frappe/erpnext:latest
container_name: breakpilot-pwa-erpnext-websocket
command:
- bash
- -c
- |
wait-for-it -t 120 erpnext-db:3306;
wait-for-it -t 120 erpnext-redis-cache:6379;
wait-for-it -t 120 erpnext-redis-queue:6379;
bench watch
environment:
- DB_HOST=erpnext-db
- DB_PORT=3306
- REDIS_CACHE=redis://erpnext-redis-cache:6379
- REDIS_QUEUE=redis://erpnext-redis-queue:6379
- SOCKETIO_PORT=9000
volumes:
- erpnext_sites:/home/frappe/frappe-bench/sites
- erpnext_logs:/home/frappe/frappe-bench/logs
networks:
- breakpilot-pwa-network
restart: unless-stopped
depends_on:
erpnext-db:
condition: service_healthy
erpnext-create-site:
condition: service_completed_successfully
# ERPNext Scheduler
erpnext-scheduler:
image: frappe/erpnext:latest
container_name: breakpilot-pwa-erpnext-scheduler
command:
- bash
- -c
- |
wait-for-it -t 120 erpnext-db:3306;
wait-for-it -t 120 erpnext-redis-cache:6379;
wait-for-it -t 120 erpnext-redis-queue:6379;
bench schedule
environment:
- DB_HOST=erpnext-db
- DB_PORT=3306
- REDIS_CACHE=redis://erpnext-redis-cache:6379
- REDIS_QUEUE=redis://erpnext-redis-queue:6379
- SOCKETIO_PORT=9000
volumes:
- erpnext_sites:/home/frappe/frappe-bench/sites
- erpnext_logs:/home/frappe/frappe-bench/logs
networks:
- breakpilot-pwa-network
restart: unless-stopped
depends_on:
erpnext-db:
condition: service_healthy
erpnext-create-site:
condition: service_completed_successfully
# ERPNext Worker (Long)
erpnext-worker-long:
image: frappe/erpnext:latest
container_name: breakpilot-pwa-erpnext-worker-long
command:
- bash
- -c
- |
wait-for-it -t 120 erpnext-db:3306;
wait-for-it -t 120 erpnext-redis-cache:6379;
wait-for-it -t 120 erpnext-redis-queue:6379;
bench worker --queue long,default,short
environment:
- DB_HOST=erpnext-db
- DB_PORT=3306
- REDIS_CACHE=redis://erpnext-redis-cache:6379
- REDIS_QUEUE=redis://erpnext-redis-queue:6379
- SOCKETIO_PORT=9000
volumes:
- erpnext_sites:/home/frappe/frappe-bench/sites
- erpnext_logs:/home/frappe/frappe-bench/logs
networks:
- breakpilot-pwa-network
restart: unless-stopped
depends_on:
erpnext-db:
condition: service_healthy
erpnext-create-site:
condition: service_completed_successfully
# ERPNext Worker (Short)
erpnext-worker-short:
image: frappe/erpnext:latest
container_name: breakpilot-pwa-erpnext-worker-short
command:
- bash
- -c
- |
wait-for-it -t 120 erpnext-db:3306;
wait-for-it -t 120 erpnext-redis-cache:6379;
wait-for-it -t 120 erpnext-redis-queue:6379;
bench worker --queue short,default
environment:
- DB_HOST=erpnext-db
- DB_PORT=3306
- REDIS_CACHE=redis://erpnext-redis-cache:6379
- REDIS_QUEUE=redis://erpnext-redis-queue:6379
- SOCKETIO_PORT=9000
volumes:
- erpnext_sites:/home/frappe/frappe-bench/sites
- erpnext_logs:/home/frappe/frappe-bench/logs
networks:
- breakpilot-pwa-network
restart: unless-stopped
depends_on:
erpnext-db:
condition: service_healthy
erpnext-create-site:
condition: service_completed_successfully
# ERPNext Frontend (Nginx)
erpnext-frontend:
image: frappe/erpnext:latest
container_name: breakpilot-pwa-erpnext-frontend
command:
- nginx-entrypoint.sh
ports:
- "8090:8080"
environment:
- BACKEND=erpnext-backend:8000
- SOCKETIO=erpnext-websocket:9000
- UPSTREAM_REAL_IP_ADDRESS=127.0.0.1
- UPSTREAM_REAL_IP_HEADER=X-Forwarded-For
- UPSTREAM_REAL_IP_RECURSIVE=off
- FRAPPE_SITE_NAME_HEADER=erpnext.local
volumes:
- erpnext_sites:/home/frappe/frappe-bench/sites
- erpnext_logs:/home/frappe/frappe-bench/logs
networks:
- breakpilot-pwa-network
restart: unless-stopped
depends_on:
erpnext-backend:
condition: service_started
erpnext-websocket:
condition: service_started
# ============================================
# Breakpilot Drive - Lernspiel (Unity WebGL)
# Web UI: http://localhost:3001
# ============================================
breakpilot-drive:
build:
context: ./breakpilot-drive
dockerfile: Dockerfile
container_name: breakpilot-pwa-drive
ports:
- "3001:80"
environment:
# API Configuration (injected into Unity WebGL)
- API_BASE_URL=${GAME_API_URL:-http://localhost:8000/api/game}
# Feature Flags
- GAME_REQUIRE_AUTH=${GAME_REQUIRE_AUTH:-false}
- GAME_ENABLE_LEADERBOARDS=${GAME_ENABLE_LEADERBOARDS:-true}
networks:
- breakpilot-pwa-network
depends_on:
- backend
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:80/health.json"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
profiles:
- game
# ============================================
# Camunda 7 - BPMN Workflow Engine
# Web UI: http://localhost:8089/camunda
# REST API: http://localhost:8089/engine-rest
# License: Apache 2.0 (kommerziell nutzbar)
# ============================================
camunda:
image: camunda/camunda-bpm-platform:7.21.0
container_name: breakpilot-pwa-camunda
ports:
- "8089:8080"
environment:
- DB_DRIVER=org.postgresql.Driver
- DB_URL=jdbc:postgresql://postgres:5432/breakpilot_db
- DB_USERNAME=breakpilot
- DB_PASSWORD=${POSTGRES_PASSWORD:-breakpilot123}
- DB_VALIDATE_ON_BORROW=true
- WAIT_FOR=postgres:5432
- CAMUNDA_BPM_ADMIN_USER_ID=admin
- CAMUNDA_BPM_ADMIN_USER_PASSWORD=${CAMUNDA_ADMIN_PASSWORD:-admin123}
depends_on:
postgres:
condition: service_healthy
networks:
- breakpilot-pwa-network
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/camunda/api/engine || exit 1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
restart: unless-stopped
profiles:
- bpmn
# ============================================
# GeoEdu Service - Self-Hosted OSM + Terrain
# DSGVO-konforme Erdkunde-Lernplattform
# Web UI: http://localhost:8088
# ============================================
geo-service:
build:
context: ./geo-service
dockerfile: Dockerfile
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-geo-service
ports:
- "8088:8088"
environment:
- PORT=8088
- ENVIRONMENT=${ENVIRONMENT:-development}
- JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key}
# PostgreSQL (PostGIS fuer OSM-Daten)
- DATABASE_URL=postgresql+asyncpg://breakpilot:breakpilot123@postgres:5432/breakpilot_db
# MinIO (AOI Bundles, generierte Assets)
- MINIO_ENDPOINT=minio:9000
- MINIO_ACCESS_KEY=${MINIO_ROOT_USER:-breakpilot}
- MINIO_SECRET_KEY=${MINIO_ROOT_PASSWORD:-breakpilot123}
- MINIO_BUCKET=breakpilot-geo
- MINIO_SECURE=false
# Ollama (Lernstationen generieren)
- OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
- OLLAMA_MODEL=${OLLAMA_DEFAULT_MODEL:-qwen2.5:14b}
# Tile Server Config
- TILE_CACHE_DIR=/app/cache/tiles
- DEM_CACHE_DIR=/app/cache/dem
- MAX_AOI_SIZE_KM2=4
volumes:
- geo_osm_data:/app/data/osm
- geo_dem_data:/app/data/dem
- geo_tile_cache:/app/cache/tiles
- geo_aoi_bundles:/app/bundles
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
postgres:
condition: service_healthy
minio:
condition: service_started
networks:
- breakpilot-pwa-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8088/health"]
interval: 30s
timeout: 10s
start_period: 60s
retries: 3
restart: unless-stopped
# ============================================
# Voice Service - PersonaPlex + TaskOrchestrator
# Voice-First Interface fuer Breakpilot
# DSGVO-konform: Keine Audio-Persistenz
# Web UI: http://localhost:8091
# ============================================
voice-service:
build:
context: ./voice-service
dockerfile: Dockerfile
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-voice-service
# Port 8091 wird von nginx HTTPS bereitgestellt (wss://macmini:8091)
expose:
- "8091"
environment:
- PORT=8091
- DATABASE_URL=postgresql+asyncpg://breakpilot:breakpilot123@postgres:5432/breakpilot_db
- VALKEY_URL=redis://valkey:6379/2
- PERSONAPLEX_ENABLED=${PERSONAPLEX_ENABLED:-false}
- PERSONAPLEX_WS_URL=${PERSONAPLEX_WS_URL:-ws://host.docker.internal:8998}
- ORCHESTRATOR_ENABLED=true
- FALLBACK_LLM_PROVIDER=${FALLBACK_LLM_PROVIDER:-ollama}
- OLLAMA_BASE_URL=http://host.docker.internal:11434
- OLLAMA_VOICE_MODEL=qwen2.5:32b
- BQAS_JUDGE_MODEL=qwen2.5:14b
- KLAUSUR_SERVICE_URL=http://klausur-service:8086
- ENCRYPTION_ENABLED=true
- AUDIO_PERSISTENCE=false
- AUDIO_SAMPLE_RATE=24000
- ENVIRONMENT=${ENVIRONMENT:-development}
- JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
volumes:
- voice_session_data:/app/data/sessions
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
postgres:
condition: service_healthy
valkey:
condition: service_healthy
networks:
- breakpilot-pwa-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8091/health"]
interval: 30s
timeout: 10s
start_period: 60s
retries: 3
restart: unless-stopped
# ============================================
# MkDocs Documentation
# Web UI: http://localhost:8009
# Material Theme with German language support
# ============================================
docs:
build:
context: .
dockerfile: docs-src/Dockerfile
platform: linux/arm64 # Mac Mini Apple Silicon
container_name: breakpilot-pwa-docs
ports:
- "8009:80"
networks:
- breakpilot-pwa-network
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:80/"]
interval: 30s
timeout: 10s
retries: 3
# ============================================
# Gitea - Self-hosted Git Server
# Web UI: http://localhost:3003
# SSH: localhost:2222
# Gitea Actions enabled for CI/CD
# ============================================
gitea:
image: gitea/gitea:1.22-rootless
container_name: breakpilot-pwa-gitea
extra_hosts:
- "macmini:192.168.178.100"
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=postgres:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=breakpilot
- GITEA__database__PASSWD=breakpilot123
- GITEA__server__DOMAIN=macmini
- GITEA__server__SSH_DOMAIN=macmini
- GITEA__server__ROOT_URL=http://macmini:3003/
- GITEA__server__HTTP_PORT=3003
- GITEA__server__SSH_PORT=2222
- GITEA__server__SSH_LISTEN_PORT=2222
- GITEA__actions__ENABLED=true
- GITEA__actions__DEFAULT_ACTIONS_URL=https://gitea.com
- GITEA__service__DISABLE_REGISTRATION=true
- GITEA__service__REQUIRE_SIGNIN_VIEW=false
- GITEA__repository__DEFAULT_BRANCH=main
- GITEA__log__LEVEL=Info
- GITEA__webhook__ALLOWED_HOST_LIST=macmini,192.168.178.100,woodpecker-server,localhost,external
volumes:
- gitea_data:/var/lib/gitea
- gitea_config:/etc/gitea
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3003:3003"
- "2222:2222"
depends_on:
postgres:
condition: service_healthy
networks:
- breakpilot-pwa-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3003/api/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
restart: unless-stopped
# ============================================
# Gitea Actions Runner
# Executes CI/CD workflows defined in .gitea/workflows/
# Includes Syft, Grype, Trivy for SBOM generation
# ============================================
gitea-runner:
image: gitea/act_runner:latest
container_name: breakpilot-pwa-gitea-runner
environment:
- CONFIG_FILE=/config/config.yaml
- GITEA_INSTANCE_URL=http://gitea:3003
- GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_TOKEN:-}
- GITEA_RUNNER_NAME=breakpilot-runner
- GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:20-bookworm,ubuntu-22.04:docker://ubuntu:22.04,self-hosted:host
volumes:
- gitea_runner_data:/data
- ./gitea/runner-config.yaml:/config/config.yaml:ro
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
gitea:
condition: service_healthy
networks:
- breakpilot-pwa-network
restart: unless-stopped
# ============================================
# Woodpecker CI - Server
# Modern CI/CD with native container support
# Web UI: http://localhost:8085
# ============================================
woodpecker-server:
image: woodpeckerci/woodpecker-server:v3
container_name: breakpilot-pwa-woodpecker-server
ports:
- "8090:8000"
extra_hosts:
- "macmini:192.168.178.100"
environment:
- WOODPECKER_OPEN=true
- WOODPECKER_HOST=http://macmini:8090
- WOODPECKER_ADMIN=pilotadmin,breakpilot_admin
# Gitea OAuth Integration
- WOODPECKER_GITEA=true
- WOODPECKER_GITEA_URL=http://macmini:3003
- WOODPECKER_GITEA_CLIENT=${WOODPECKER_GITEA_CLIENT:-}
- WOODPECKER_GITEA_SECRET=${WOODPECKER_GITEA_SECRET:-}
# Agent Secret (fuer Agent-Authentifizierung)
- WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET:-woodpecker-agent-secret-key}
# Database (SQLite fuer einfache Einrichtung)
- WOODPECKER_DATABASE_DRIVER=sqlite3
- WOODPECKER_DATABASE_DATASOURCE=/var/lib/woodpecker/woodpecker.sqlite
# Logging
- WOODPECKER_LOG_LEVEL=info
# Trust all repos (allows privileged containers)
- WOODPECKER_PLUGINS_PRIVILEGED=true
- WOODPECKER_PLUGINS_TRUSTED_CLONE=true
volumes:
- woodpecker_data:/var/lib/woodpecker
depends_on:
gitea:
condition: service_healthy
networks:
- breakpilot-pwa-network
restart: unless-stopped
# ============================================
# Night Scheduler - Nachtabschaltung
# Stoppt Services nachts, startet sie morgens
# API: http://localhost:8096
# ============================================
night-scheduler:
build: ./night-scheduler
container_name: breakpilot-pwa-night-scheduler
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./night-scheduler/config:/config
- ./docker-compose.yml:/app/docker-compose.yml:ro
environment:
- COMPOSE_PROJECT_NAME=breakpilot-pwa
ports:
- "8096:8096"
networks:
- breakpilot-pwa-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8096/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# ============================================
# Woodpecker CI - Agent
# Executes pipeline steps in containers
# ============================================
woodpecker-agent:
image: woodpeckerci/woodpecker-agent:v3
container_name: breakpilot-pwa-woodpecker-agent
command: agent
environment:
- WOODPECKER_SERVER=woodpecker-server:9000
- WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET:-woodpecker-agent-secret-key}
- WOODPECKER_MAX_WORKFLOWS=4
- WOODPECKER_LOG_LEVEL=info
# Backend für Container-Ausführung
- WOODPECKER_BACKEND=docker
- DOCKER_HOST=unix:///var/run/docker.sock
# Extra hosts für Pipeline-Container (damit sie macmini erreichen)
- WOODPECKER_BACKEND_DOCKER_EXTRA_HOSTS=macmini:192.168.178.100,gitea:192.168.178.100
# Nutze das gleiche Netzwerk für Pipeline-Container
- WOODPECKER_BACKEND_DOCKER_NETWORK=breakpilot-dev_breakpilot-pwa-network
# Docker-Socket für Build-Steps (Host-Docker statt DinD)
- WOODPECKER_BACKEND_DOCKER_VOLUMES=/var/run/docker.sock:/var/run/docker.sock
volumes:
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- woodpecker-server
networks:
- breakpilot-pwa-network
restart: unless-stopped
volumes:
# Woodpecker CI Data
woodpecker_data:
driver: local
# Vault Data
vault_data:
driver: local
# Vault Agent Config (role-id, secret-id, token)
vault_agent_config:
driver: local
# Vault-managed SSL Certificates
vault_certs:
driver: local
breakpilot_pwa_data:
driver: local
# Embedding Service Model Cache
embedding_models:
driver: local
# Valkey Session Cache
valkey_data:
driver: local
dsms_data:
driver: local
klausur_uploads:
driver: local
eh_uploads:
driver: local
ocr_labeling:
driver: local
# PaddleOCR Model Cache (persist across container restarts)
paddle_models:
driver: local
# PaddleOCR Service Model Cache (x86_64 emulation)
paddleocr_models:
driver: local
qdrant_data:
driver: local
minio_data:
driver: local
synapse_data:
driver: local
synapse_db_data:
driver: local
# Jitsi Volumes
jitsi_web_config:
driver: local
jitsi_web_crontabs:
driver: local
jitsi_transcripts:
driver: local
jitsi_prosody_config:
driver: local
jitsi_prosody_plugins:
driver: local
jitsi_jicofo_config:
driver: local
jitsi_jvb_config:
driver: local
# Jibri Recording Volumes
jibri_recordings:
driver: local
# Transcription Worker Volumes
transcription_models:
driver: local
transcription_temp:
driver: local
# ERPNext Volumes
erpnext_db_data:
driver: local
erpnext_redis_queue_data:
driver: local
erpnext_redis_cache_data:
driver: local
erpnext_sites:
driver: local
erpnext_logs:
driver: local
# GeoEdu Service Volumes
geo_osm_data:
driver: local
geo_dem_data:
driver: local
geo_tile_cache:
driver: local
geo_aoi_bundles:
driver: local
# Voice Service Volumes (transient sessions only)
voice_session_data:
driver: local
# Gitea Volumes
gitea_data:
driver: local
gitea_config:
driver: local
gitea_runner_data:
driver: local
networks:
breakpilot-pwa-network:
driver: bridge