# 🚀 Element Server Suite (ESS) Community – GitOps Deployment Guide

Dieses Repository enthält die Infrastruktur-as-Code (IaC) für den Matrix-Homeserver (basierend auf der Element Server Suite Community Edition), der per **FluxCD** nach GitOps-Prinzipien verwaltet wird.

## 📑 Inhaltsverzeichnis

1.  [Voraussetzungen & Lokale Tools](#1-voraussetzungen--lokale-tools)
2.  [Architektur & Logik des Stacks](#2-architektur--logik-des-stacks)
3.  [Aufbau des Repositories](#3-aufbau-des-repositories)
4.  [Das Deployment (Aktueller Stand)](#4-das-deployment-aktueller-stand)
5.  [Nützliche Befehle](#5-nützliche-befehle)
6.  [Troubleshooting & Known Issues](#6-troubleshooting--known-issues)
7.  [Weitere Ressourcen](#7-weitere-ressourcen)

-----

## 1\. Voraussetzungen & Lokale Tools

**Empfohlen: `.devcontainer/` nutzen** ("Reopen in Container" in VS Code, oder `docker build`
+ `docker run` manuell, siehe [`.devcontainer/README.md`](.devcontainer/README.md)) - bringt
alle unten genannten Tools bereits fertig eingerichtet mit, ohne sie lokal zu installieren.

Alternativ, um mit diesem Stack zu interagieren (Konfigurationen anzupassen, Secrets zu verschlüsseln, Fehler zu suchen), müssen folgende Tools lokal installiert sein:

### 🛠️ Benötigte CLI-Tools

  * **`kubectl`**: Für die direkte Kommunikation mit dem Kubernetes-Cluster.
  * **`flux`**: Für das manuelle Anstoßen von GitOps-Synchronisationen.
  * **`sops`** & **`age`** (oder GPG): Für die Ver- und Entschlüsselung von Secrets direkt im Git-Repo.
  * **`helm`**: (Optional) Zum Inspizieren von Chart-Values.

### 🍏 macOS (via Homebrew)

```bash
brew install kubectl fluxcd/tap/flux sops age helm
```

### 🐧 Linux

```bash
# kubectl & helm via Paketmanager (apt/dnf) oder curl
curl -sLS https://fluxcd.io/install.sh | sudo bash
# SOPS
wget https://github.com/getsops/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64
sudo mv sops-v3.8.1.linux.amd64 /usr/local/bin/sops && sudo chmod +x /usr/local/bin/sops
sudo apt install age
```

### 🪟 Windows (via Winget oder WSL2)

*Empfehlung: Nutze WSL2 (Ubuntu) und folge den Linux-Schritten.* Nativ via Winget:

```powershell
winget install Kubernetes.kubectl FluxCD.Flux Mozilla.sops age-encryption.age Helm.Helm
```

### ⚙️ Lokale Konfiguration

1.  **Kubeconfig:** Stelle sicher, dass die Datei `~/.kube/config` mit den Zugangsdaten zu deinem K3s-Cluster gefüllt ist. Test: `kubectl get nodes`.
2.  **SOPS Key:** Du benötigst den privaten `age`-Key (oder GPG-Key), der in der `.sops.yaml` des Repositories hinterlegt ist, um Secrets bearbeiten zu können.
3.  **Git Hooks installieren:** Nach dem Klonen dieses Repositories müssen Git Hooks installiert werden, um ConfigMap-Änderungen automatisch zu tracken:
    ```bash
    cd prod/gitops
    ./scripts/install-hooks.sh
    ```
    Siehe [📖 GitOps ConfigMap Auto-Sync](docs/ops-configmap-sync.md) für Details.

-----

## 2\. Architektur & Logik des Stacks

Das Setup basiert auf einer modernen, modularen GitOps-Architektur:

### Management-Komponenten

  * **K3s**: Die leichtgewichtige Kubernetes-Distribution, die als Fundament dient.
  * **FluxCD**: Der GitOps-Controller. Er überwacht dieses Git-Repository. Ändert sich hier eine Datei, wendet Flux die Änderung automatisch im Cluster an.
  * **SOPS + age**: Erlaubt es, Secrets verschlüsselt in Git zu speichern. Flux entschlüsselt diese "on the fly" im Cluster. Mehrere Secrets nutzen zusätzlich einen zweiten, eng gescopten age-Key für automatisierte Rotation (siehe coturn TURN-Secret unten).
  * **Traefik**: Der Ingress-Controller (Standard bei K3s). Er leitet Traffic von Port 80/443 an die richtigen internen Pods weiter.
  * **Cert-Manager**: Spricht mit Let's Encrypt und stellt automatisch gültige TLS-Zertifikate für alle Ingress-Routen aus.
  * **NetworkPolicies**: Default-Deny Ingress für die `matrix`- und `authentik`-Namespaces, mit expliziten Allow-Regeln pro Komponente (`apps/production/networkpolicy.yaml`, `apps/authentik/networkpolicy.yaml`).

### Matrix Stack (ESS Community v26.4.0)

Die Suite ist ein "Umbrella Chart", das aus mehreren Microservices besteht:

  * **Synapse (`matrix.`):** Das eigentliche Backend (Homeserver) für die Chat-Nachrichten.
  * **Matrix Authentication Service (MAS) (`account.`):** Der OIDC-basierte Login-Server. Zwingend erforderlich für moderne Matrix-Clients.
  * **Element Web (`domain.tld`):** Eigener Fork (`sorb/threadnet-web`) des Web-Clients für die Endnutzer - Custom Themes, Element Desktop Setup-Seiten, Element-Call-Anpassungen.
  * **Matrix RTC (`mrtc.`):** Die SFU (Selective Forwarding Unit) für Audio-/Video-Calls, mit eigenem Element-Call-Fork (`sorb/threadnet-call`) für höhere Video-Defaults (bis 1440p/60fps).
  * **coturn:** TURN/STUN-Server für WebRTC hinter NAT (`hostNetwork: true`, außerhalb der NetworkPolicy-Kontrolle, stattdessen über die Hetzner Cloud Firewall abgesichert). Shared Secret wird monatlich automatisiert rotiert.
  * **PostgreSQL:** Die relationale Datenbank für Synapse und MAS.

### Identity & Observability

  * **Authentik** (`auth.`, `account.`): OIDC-Identity-Provider für Matrix-Enrollment, Passwort-Recovery und optionales 2FA/Passkey. Flows/Provider/Application deklarativ als Authentik-Blueprints erfasst (`apps/authentik/authentik-blueprints.yaml`), nicht nur in der UI geklickt.
  * **Monitoring**: Grafana Alloy sammelt Metriken/Logs, Remote-Write zu einem externen Prometheus/Loki-Stack.
  * **Backups**: Nächtliche, verschlüsselte & deduplizierte Borg-Backups (Postgres-Dumps + Synapse-`media_store`) zu einer Hetzner Storage Box, getrennt nach Namespace, mit eigenen Repos/Passphrasen.

### Moderation & Content Scanning

  * **Draupnir**: Moderationsbot (Community-Nachfolger von Mjolnir) für Ban-Listen/Policy-Rooms.
  * **ClamAV**: Zwei Bausteine für unterschiedliche Räume - ein eigenes Synapse-Modul (`clamav_spam_checker.py`) scannt Uploads in unverschlüsselten Räumen; ein zusätzlicher, eigenständiger `clamav-http-scanner`-Dienst wird vom gepatchten Element-Web-Client (`sorb/threadnet-web`) sowohl beim Senden als auch beim Empfangen aufgerufen und deckt damit auch verschlüsselte Räume/DMs ab. Details: `docs/deployment-guides/06-moderation-content-scanning.md`.

### Host-Level (nicht-GitOps) Änderungen

  * `host-config/` ist bewusst der einzige Teil dieses Repos, den Flux **nicht** verwaltet - Skripte/systemd-Units, die direkt auf dem nackten Hetzner-Host laufen (z.B. `unattended-upgrades`-Vorab-Benachrichtigungen), für Dinge, die strukturell außerhalb der Reichweite von Flux liegen. Deployment erfolgt manuell per SSH, instanzspezifische Werte liegen in einer Config-Datei auf dem Host, nicht im versionierten Skript. Details: `docs/deployment-guides/07-host-maintenance-notifications.md`.

-----

## 3\. Aufbau des Repositories

Das Repository ist strikt nach "Infrastruktur" und "Applikation" getrennt, um Abhängigkeiten korrekt zu laden.

```text
gitops/
├── .sops.yaml                 # Definiert, wie Secrets verschlüsselt werden
├── clusters/matrix/           # Der Einstiegspunkt für FluxCD
├── apps/
│   ├── base/                  
│   │   ├── infra/             # Core-Dienste (Cert-Manager, Namespaces)
│   │   └── matrix/            # Die OCI Helm-Repository Definition für ESS
│   └── production/            # Das eigentliche Matrix-Deployment
│       ├── kustomization.yaml           # Inhaltsverzeichnis
│       ├── element-server-suite.yaml    # Das HelmRelease (Bestellung an Flux)
│       ├── cert-issuer.yaml             # Let's Encrypt Konfiguration
│       ├── matrix-postgres-auth.yaml    # DB-Passwörter
│       └── custom-configs/              # Eigene Anpassungen (Themes, Logging)
│           ├── synapse-values.yaml      # Als ConfigMap
│           ├── element-values.yaml      # Als ConfigMap
│           └── mas-secret.yaml          # Als verschlüsseltes SOPS-Secret
```

Weitere Secret-Dateien liegen direkt unter `apps/production/` bzw. `apps/authentik/`
(z.B. `coturn-secret.yaml`, `synapse-turn-secret.yaml`, `synapse-backup-secret.yaml`,
`authentik-backup-secret.yaml`) - jede einzeln SOPS-verschlüsselt, nicht in `custom-configs/`
gebündelt.

### Repo-Topologie (seit 2026-07-31)

**Kanonisch ist `git.lab/axion1337.chat/axion1337.chat-gitops`** (Homelab-GitLab, nur im
Lab auflösbar) — dort wird gepusht und läuft der CI-Verifikations-Job (`.gitlab-ci.yml`).
Die Kopie auf `rohana.axion1337.de` ist ein automatischer **Push-Mirror** und bleibt die
**Flux-Quelle**: der Cluster zieht unverändert von Gitea, der Mirror liefert. **Niemals
direkt nach rohana pushen** — der Mirror überschreibt divergente Stände.

**Issues und Wiki liegen seit 2026-08-01/02 ebenfalls auf git.lab**
([Issues](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues) ·
[Wiki](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/wikis/home) — der
Wiki-Reiter oben im Projekt). ⚠️ Die Issue-Nummern haben sich beim Umzug verschoben;
ein alter Verweis „gitops#N" meint die Gitea-Nummer, verbindlich ist der
Migrations-Fußtext im jeweiligen Issue. **Releases bleiben auf Gitea** (öffentlicher
Download-Pfad). Der `wiki`-Branch in diesem Repo ist ein **überholter Abzug von
`docs/` aus dem Mai** und nicht die gepflegte Fassung.

Alle Dokumentationsquellen zusammen (Plattform, Homelab, Arbeitsweise) gibt es unter
**[wiki.lab](https://wiki.lab)** — Konfiguration im Repo
[`homelab/wiki`](https://git.lab/homelab/wiki).

**Abhängigkeits-Logik:** Flux installiert erst `infra-apps` (damit Namespaces und Repositories existieren) und danach `production-apps` (das eigentliche ESS-Chart).

-----

## 4\. Das Deployment (Aktueller Stand)

### Das HelmRepository (OCI)

Element verteilt die Community-Edition modern über die GitHub Container Registry (`ghcr.io`). Klassische HTTP-Helm-Repos werfen hier oft 404-Fehler.

```yaml
# apps/base/matrix/ess-repo.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
  name: element-ess-oci
spec:
  type: oci
  url: oci://ghcr.io/element-hq/ess-helm
```

### Das HelmRelease (Das Herzstück)

Das ESS-Chart (`v26.4.0`) hat ein extrem striktes JSON-Schema. Konfigurationen müssen exakt sitzen:

  * `serverName` muss an der Wurzel stehen.
  * Komponenten werden in `camelCase` geschrieben (`elementWeb`, `synapseAdmin`).
  * Zertifikate werden durch `certManager: true` automatisch gemanaged. **Keine manuellen TLS-Einträge im Ingress-Block\!**

<!-- end list -->

```yaml
# apps/production/element-server-suite.yaml (Auszug)
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: matrix-stack
spec:
  chart:
    spec:
      chart: matrix-stack
      version: "26.4.0"
  valuesFrom:
    - kind: ConfigMap
      name: ess-synapse-custom
      valuesKey: values.yaml
    - kind: Secret
      name: ess-mas-values-secret
      valuesKey: values.yaml
    - kind: Secret
      name: synapse-turn-secret
      valuesKey: values.yaml
  values:
    serverName: axion1337.chat
    certManager: true
    postgres:
      enabled: true
    synapse:
      enabled: true
      ingress: { host: matrix.axion1337.chat }
    matrixAuthenticationService:
      enabled: true
      ingress: { host: account.axion1337.chat }
    elementWeb:
      enabled: true
      ingress: { host: axion1337.chat }
    wellKnownDelegation:
      enabled: false # ! WICHTIG (Siehe Troubleshooting)
```

-----

## 5\. Nützliche Befehle

### 🔄 Flux / GitOps Sync erzwingen

Wenn man nicht auf den automatischen 10-Minuten-Timer von Flux warten will:

```bash
flux reconcile kustomization flux-system --with-source
flux reconcile kustomization production-apps --with-source
```

### 🔍 Status des Deployments prüfen

```bash
# Zeigt, ob Flux das Chart akzeptiert und angewendet hat
flux get helmreleases -A

# Zeigt an, ob die Pods erfolgreich starten
kubectl get pods -n matrix
```

### 🔐 Zertifikate (Let's Encrypt) debuggen

```bash
# Sind die Zertifikate da und gültig?
kubectl get certificate -n matrix

# Wo hängt der Request? (403 Fehler etc.)
kubectl get certificaterequest -n matrix
kubectl get challenges -n matrix
kubectl describe challenge <name> -n matrix
```

### 🛡️ Secrets mit SOPS bearbeiten

Um ein Passwort im GitOps-Repo zu ändern, editiert man die verschlüsselte Datei direkt via SOPS (sie wird transparent entschlüsselt und beim Speichern wieder verschlüsselt):

```bash
sops apps/production/custom-configs/mas-secret.yaml
```

-----

## 6\. Troubleshooting & Known Issues

### Issue 1: `HelmChart is not ready: stat ... no such file or directory`

  * **Ursache:** Falscher Versuch, das Chart direkt aus dem GitHub-Repo-Code (als GitRepository) zu lesen. Das Chart erfordert Sub-Charts, die so nicht gerendert werden können.
  * **Lösung:** Immer das OCI-Repository (`oci://ghcr.io/...`) und den Chartnamen `matrix-stack` verwenden.

### Issue 2: `values don't meet the specifications of the schema(s)`

  * **Ursache:** Ab Version 26.x hat ESS ein sehr rigides JSON-Schema.
  * **Lösung:** Logs genau lesen.
      * `tls` darf nicht in den Ingress-Block der Komponenten.
      * `serverName` muss ins Top-Level, nicht unter `synapse`.
      * Keine `config:` Blöcke für Core-Komponenten.

### Issue 3: Let's Encrypt Error `403 Order's status is processing` auf der Hauptdomain

  * **Ursache (Die ACME Race Condition):** Wenn `elementWeb` (auf `axion1337.chat`) und `wellKnownDelegation` (ebenfalls auf `axion1337.chat`) gleichzeitig aktiviert sind, fordert `cert-manager` zeitgleich zwei Zertifikate für dieselbe Domain an. Let's Encrypt blockt den zweiten Versuch und das Ingress-Setup hängt sich auf.
  * **Lösung:** `wellKnownDelegation: enabled: false` im Helm-Chart setzen. Das `.well-known/matrix/server` File muss stattdessen entweder als statische JSON-Datei auf dem Webserver der Hauptdomain hinterlegt oder per Ingress-Route (Traefik Middleware) direkt auf den Synapse-Dienst umgebogen werden.

### Issue 4: Fehlende Zertifikate (`No resources found`)

  * **Ursache:** Manuelle Kustomize-Patches kollidieren mit dem Helm-Chart.
  * **Lösung:** Manuelle Patches löschen und das native Feature des Charts nutzen: `certManager: true` auf der obersten (Root-)Ebene der `values` setzen. Das Chart erstellt daraufhin die korrekten Ingress-Annotations und Secrets von selbst.

-----

## 7\. Weitere Ressourcen

  * **`CLAUDE.md`** (Repo-Root): Technische Referenz für KI-gestützte Arbeit an diesem Repo - Architektur, bekannte Chart-Quirks, Troubleshooting-Checkliste.
  * **`docs/TASKS.md`**: Backlog-Pointer zu den [Gitea Issues](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues) - Details werden nicht mehr doppelt gepflegt.
  * **[Gitea Releases](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/releases)**: Versionshistorie (SemVer, `vMAJOR.MINOR.PATCH` als Änderungsgrößen-Konvention, kein Kompatibilitätsvertrag - siehe [[00-TASKS]] Wiki für die Konvention).
  * **[Wiki](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/wikis/home)** (auf git.lab, Reiter *Wiki*): Ausführliche Historie, Incident-Notizen, Setup-Guides pro Komponente. Zusammen mit Homelab- und Verfahrensdoku auch unter **[wiki.lab](https://wiki.lab)**.
  * **`docs/deployment-guides/`**: Detaillierte Guides für TURN-Server, Authentik, Monitoring, Element-Customization, Room-Policies, Moderation & Content-Scanning, Host-Wartungsbenachrichtigungen.