## 🚀 Step-by-Step Installationsanleitung (From Scratch)

Diese Anleitung geht davon aus, dass du einen frischen Server (Ubuntu/Debian) mit öffentlicher IP hast und deine lokalen Tools (Flux CLI, kubectl, sops, age) installiert sind.

### Schritt 1: Kubernetes (K3s) auf dem Server installieren
Logge dich per SSH auf deinem Server ein und installiere ein frisches K3s. Wir nutzen K3s mit dem Standard-Traefik-Ingress.

```bash
# Auf dem Server ausführen:
curl -sfL https://get.k3s.io | sh -

# Kubeconfig kopieren und Berechtigungen setzen (für den lokalen Zugriff)
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER ~/.kube/config
```
*Kopiere dir den Inhalt der `~/.kube/config` auf deinen lokalen Rechner, damit dein lokales `kubectl` den Server steuern kann. Vergiss nicht, die IP `127.0.0.1` in der Datei durch die öffentliche IP deines Servers zu ersetzen.*

### Schritt 2: Den SOPS-Verschlüsselungs-Key generieren
Damit Flux deine verschlüsselten Passwörter (SMTP, Datenbank) im Cluster lesen kann, braucht es einen privaten Schlüssel. Wir nutzen `age`.

```bash
# Auf deinem lokalen Rechner ausführen:
# 1. Key generieren
age-keygen -o age.agekey

# 2. Den Public Key (steht in der Datei) in die .sops.yaml des Repos eintragen!

# 3. Den Private Key als Secret in den Cluster laden (in den flux-system Namespace)
cat age.agekey | kubectl create secret generic sops-age \
--namespace=flux-system \
--from-file=age.agekey=/dev/stdin
```

### Schritt 3: Das Git-Repository vorbereiten
Stelle sicher, dass deine GitOps-Struktur gepusht ist und du ein Personal Access Token (PAT) für dein Git-Repository hast (bei GitHub/GitLab). 
* Das Token benötigt Lese- und Schreibrechte auf das Repository.

### Schritt 4: Flux Bootstrap (Der Startschuss)
Das ist der magische Befehl. Er installiert den Flux-Controller in deinem Cluster und verbindet ihn mit deinem Repository. Ab diesem Moment übernimmt Flux das Steuer.

**Für GitHub:**
```bash
export GITHUB_TOKEN="dein-personal-access-token"
export GITHUB_USER="dein-github-username"

flux bootstrap github \
  --owner=$GITHUB_USER \
  --repository=dein-repo-name \
  --branch=main \
  --path=prod/gitops/clusters/matrix \
  --personal
```

**Für Gitea/GitLab/Generisches Git (wie in deinem Setup scheinbar genutzt):**
```bash
flux bootstrap git \
  --url=https://rohana.axion1337.de/sorb/axion1337.chat-gitops.git \
  --branch=main \
  --path=prod/gitops/clusters/matrix \
  --username=dein-git-user \
  --password=dein-git-token
```

### Schritt 5: Zurücklehnen und beobachten
Flux klont jetzt dein Repo, liest die Kustomizations und wendet sie in der richtigen Reihenfolge an (`infra-apps` -> `production-apps`).

Du kannst den Fortschritt live verfolgen:
```bash
# Zeigt den GitOps-Sync-Status:
flux get kustomizations --watch

# Zeigt das Helm-Deployment der Element Server Suite:
flux get helmreleases -n matrix --watch

# Zeigt, wie die Pods hochfahren:
kubectl get pods -n matrix -w
```
Sobald alle Pods auf `Running` stehen und die Zertifikate über Let's Encrypt validiert wurden (`kubectl get certificate -n matrix`), ist dein Matrix-Stack unter `https://axion1337.chat` erreichbar.

---

## 🔁 Recovery: lokalen age-Key wiederherstellen (Server läuft bereits)

Anders als Schritt 2 oben (neuen Key **erzeugen**) — falls der Server bereits läuft und nur der
lokale Rechner den age-Key verloren hat (z.B. nach einer Neuinstallation), lässt sich der
**bestehende** Private Key direkt aus dem Cluster zurückholen, ohne einen neuen zu generieren
(das würde `.sops.yaml` und alle bereits verschlüsselten Secrets ungültig machen):

```bash
mkdir -p ~/.age
kubectl get secret sops-age -n flux-system -o jsonpath='{.data.age\.agekey}' | base64 -d > ~/.age/keys.txt
chmod 600 ~/.age/keys.txt

# Public Key zur Kontrolle gegen .sops.yaml abgleichen:
grep 'public key:' ~/.age/keys.txt
grep 'age:' .sops.yaml
```

Voraussetzung: laufender Kubeconfig-Zugriff auf den Cluster (siehe Schritt 1 oben — auch das
ist reines Zurückkopieren, kein Neu-Erzeugen).

**Bekannte Schwachstelle**: Dieser Key existiert aktuell nur an zwei Orten — im
`sops-age`-Secret selbst (auf demselben Server) und lokal bei wem auch immer ihn zuletzt
zurückgeholt hat. Es gibt kein separates, offsite Backup. Fällt der Server komplett aus
(nicht nur der lokale Rechner), sind alle SOPS-verschlüsselten Secrets im Repo unlesbar.
Siehe Issue-Backlog für die Entscheidung, ob/wie das abgesichert wird.

---

## 🌐 Node-Konfiguration: `/etc/hosts`-Eintrag für den Gitea-Host

Der K3s-Node und der Gitea-Host (`rohana.axion1337.de`, Container-Registry + Git-Remote)
teilen sich ein privates Hetzner-Netzwerk (Node `10.0.0.2`, Gitea-Host `10.0.0.3`). Seit
2026-07-28 hat der Node dafür einen manuellen `/etc/hosts`-Eintrag:

```
10.0.0.3 rohana.axion1337.de
```

**Warum**: eine Firewall-Fehlkonfiguration hatte den Node zeitweise komplett von
`rohana.axion1337.de` über die öffentliche IP abgeschnitten, was Image-Pulls (z.B. für
Custom-Images wie `sorb/axion-backup`) mit Timeout scheitern ließ. Der Eintrag macht
Image-Pulls unabhängig vom Zustand der öffentlichen Firewall.

**Wichtig**: Das ist unmanaged Node-Konfiguration (kein GitOps, kein Kubernetes-Objekt) —
überlebt einen Node-Neuaufbau **nicht** und muss dann erneut gesetzt werden:

```bash
echo "10.0.0.3 rohana.axion1337.de" | sudo tee -a /etc/hosts
```

Ein sauberer, clusterweiter Ersatz (z.B. CoreDNS-Rewrite, damit auch Pods selbst intern
auflösen) ist als Issue #41 nachgehalten.