feat: slice 3 - wiki, sources and AARs in their neckbeard homes

Gate 4, slice 3: verfahren/, hosts/, vision/ and shared/ moved via git
mv - six AARs to docs/aar/ (four harvested by the 2026-08-09 retro,
two open), procedures and host knowledge to docs/wiki/ (admin,
deployment, architecture, new area vision), the retro protocol and the
commit mapping table to docs/sources/ (protokolle/, migration/). New:
the wiki index linking every page, and the mirror-topology page
carrying the why-two-places reasoning verbatim from the old CLAUDE.md
(F-013 preserved). All moved-path references retargeted; the link
checker drove the sweep to zero.

pruefe_prosa.py added (pattern C+D): SHA citations resolve via repo,
mapping table, optional component clones or a curated exemption list
(documented dead Gitea-force-push commits, a vendor-repo tag, an
Authentik uid that is hex but no git SHA, the external neckbeard
reference); wiki task prose without an issue reference errors, with a
visible pragma for deliberate checklists; the dead-tracker denylist
now covers every mirrored repo's retired Gitea tracker (F-005) - two
links re-verified against live GitLab titles and retargeted, five
defused into honest historical citations.

Verified: validate 0/0, gen_status --check current, drift 0. Demo on
the pre-migration state fires 6 findings (3 orphaned SHAs, 3 task
blocks); on the current tree exactly the 3 F-004 task blocks remain -
they turn green in slice 4 when the issues exist, which is why
pruefe_prosa joins CI only then.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Thore Cimbal
2026-08-11 12:00:00 +00:00
co-authored by Claude Fable 5
parent 70e81e2ff1
commit 92b448fe30
37 changed files with 424 additions and 120 deletions
@@ -0,0 +1,73 @@
---
type: aar
status: harvested
date: 2026-08-01
related: []
---
# AAR — CVE-Pipeline `gitops#47`
**Datum:** 2026-08-01 · **Host/Stack:** CFGMON, `/opt/threadnet-operating/monitoring`
**Auftrag:** Deploy eines gebauten, als UNGETESTET übergebenen Stands (Trivy-Scanner,
Exporter, Alert-Regeln, Grafana-Dashboard)
## 1. Ergebnis
**Live und verifiziert:** Scanner (29/29 Images gescannt), Exporter, Prometheus-Job
`cve_exporter`, Regelgruppe `axion-cve`, Grafana-Ordner *Security* mit CVE-Dashboard.
**Bewusst nicht live:** die Alarm-Zustellung nach Matrix. `room="security"` routet in
`alertmanager.yml` auf einen Null-Receiver (Commit `2b715ca` in
`sorb/threadnet-operating`). Grund siehe Befund 1.
## 2. Befunde
| # | Befund | Schwere | Status |
|---|---|---|---|
| 1 | Eine Matrix-Nachricht pro CVE. 126 CRITICAL landen in **einer** Alertmanager-Gruppe, nach 24 h kommen 1222 HIGH dazu. Dazu steht `save_state()` in `do_POST` hinter der Sende-Schleife: bricht ein Send ab (Synapse rate-limitet nach ~10 mit 429), wird kein State gespeichert, der Receiver antwortet 502, Alertmanager wiederholt die komplette Gruppe — mit leerer Deduplizierung | HIGH | abgefangen, `gitops#51` |
| 2 | `docker compose up -d` aktiviert geänderte Configs nicht. Einzeldatei-Mounts hängen am Inode, `git pull` benennt um. Prometheus lief nach dem Deploy mit alten Regeln — `promtool` fand 9, Prometheus kannte 6, kein Fehler im Log | MEDIUM | behoben via `--force-recreate`, `gitops#52` |
| 3 | `TrivyScanStale` kann ein nie erfolgreich gescanntes Image nicht melden — ohne ersten Report existiert keine Serie, an der `time() - trivy_last_scan_timestamp` hängen könnte | LOW | notiert in `gitops#51` |
| 4 | Der Exporter prunt den First-Seen-State bei **jedem** Scrape. Ein transienter Lesefehler (`except: continue`) löscht die Erstfund-Zeitstempel des Targets dauerhaft | LOW | notiert in `gitops#51` |
| 5 | Grafana-Provisioning für den Security-Ordner fehlte — das Dashboard wäre nie erschienen | — | vom Autor selbst behoben (`cdfadc0`), bevor ausgerollt wurde |
Gemessene Gesamtlage über alle 29 Images: **126 CRITICAL, 1222 HIGH**, 2710 MEDIUM,
1316 LOW. Spitzenreiter `goauthentik/server:2026.2.3` mit 369 CRITICAL+HIGH.
## 3. Verdachtsfälle mit Entwarnung
| Vermutung | Messung | Ergebnis |
|---|---|---|
| Exporter parst 29 JSONs je Scrape → Timeouts bei 15 s Intervall | `collect()` gegen echte Reports | 0,4 s für 29 Reports, ~3100 Zeilen — unkritisch |
| Private Registry `rohana.axion1337.de` braucht Credentials für Trivy | Anonymer Pull | zieht anonym, keine Credentials nötig |
| Zwei down-Targets könnten Folge des Deploys sein | `avg_over_time(up[3h])` | 0.00 — schon 3 h vorher tot, in `hosts/game.md` erfasst |
`promtool check rules`, `amtool check-config`, `amtool config routes test`,
`docker compose config` und `py_compile` liefen alle sauber.
## 4. Was die Befunde ermöglicht hat
Befund 1 wäre in keinem Lint aufgefallen — der Code ist korrekt, das Problem entsteht
erst aus der **Datenmenge**, gegen die er läuft. Sichtbar wurde er durch Messen vor dem
Deploy: Trivy lokal über drei repräsentative Images ergab 27 CRITICAL / 126 HIGH auf 3
von 29, also die richtige Größenordnung. Die spätere Realität (126 CRITICAL) bestätigte
die Entscheidung. `amtool config routes test` belegte danach, dass die Stummschaltung
nur `room=security` trifft und den normalen Alarmweg unangetastet lässt.
Befund 2 wurde nur sichtbar, weil die Config **im Container** geprüft wurde
(`docker exec prometheus grep …`) statt auf der Platte. Auf der Platte sah alles richtig
aus, `up -d` meldete `Running`, und ein SIGHUP-Reload lud klaglos den alten Inhalt.
Diese beiden Punkte sind als Verfahren festgehalten:
[../deploy-uebergabe.md](../wiki/deployment/deploy-uebergabe.md).
## 5. Offen
Richtungsentscheidung zu `gitops#51`, bevor die Alarme scharf gehen: entweder
`matrix-alerts.py` auf eine Sammelnachricht pro Webhook-Batch umbauen (die fünf
Pflichtfelder je CVE als eine Zeile, bleibt vollständig), oder die Regeln auf
`count by (target, severity)` aggregieren und die CVE-Details im Dashboard lassen.
In beiden Fällen zusätzlich: State inkrementell speichern, 429 mit `Retry-After`
behandeln. Danach die `room="security"`-Route entfernen.
Nebenbefund ohne Handlungsbedarf von hier: `coturn/coturn:latest` ist das einzige
ungepinnte Image (bereits in `gitops#47` notiert).
+192
View File
@@ -0,0 +1,192 @@
---
type: aar
status: harvested
date: 2026-08-01
related: []
---
# AAR — LABNET-02, CFGMON-Seite (Übergabe `sorb/management#2`)
**Datum:** 2026-08-01 · **Host/Stack:** CFGMON, WireGuard-Client gegen UDM
**Auftrag:** Schritte 13 der Übergabe (Architektur v2), melden, nach Freigabe aktivieren
## 1. Ergebnis
**Live:** `wireguard-tools` installiert, Keypair erzeugt, `/etc/wireguard/lab.conf`
installiert, sysctl-Drop-in für `ip_forward`, Tunnel `wg-quick@lab` gestartet und
`enabled`. Interface `lab` steht mit `10.58.75.2/24`, Routen und Forward-Regeln aktiv,
Split-DNS gesetzt (`10.58.73.1`, `~lab`).
> ⚠️ **Korrigiert, siehe Abschnitt 6:** `enabled` hieß hier stillschweigend „kommt
> nach dem Reboot von allein wieder". Das war falsch — der Dienst ist beim Neustart
> fehlgeschlagen.
**Noch nicht funktionsfähig:** **kein Handshake**`0 B received`. Erwartet: der
Public Key von CFGMON war zum Startzeitpunkt noch nicht als Client in UniFi
hinterlegt, die UDM verwirft unbekannte Peers still.
**Blockiert:** die Gateway-Rolle. Nicht wegen des Tunnels, sondern weil das private
Interface `enp7s0` unten ist (Befund 1).
## 2. Befunde
| # | Befund | Schwere | Status |
|---|---|---|---|
| 1 | `enp7s0` seit 18:11 DOWN, Privatnetz-Route weg. Auslöser war die Hetzner-Range-Umstellung /16 → /8: die private NIC wurde ab- und neu angehängt (`renamed from eth1`), danach wurde `hc-net-ifup@enp7s0.service` **übersprungen** (`ConditionPathExists=!/run/systemd/network/10-netplan-enp7s0.network`). Folge: `k3s_host_node` (10.0.0.2) unerreichbar, Gateway-Rolle wirkungslos | HIGH | offen, sorb rebootet |
| 2 | ufw ist auf CFGMON **inaktiv** (`Status: inactive`, `ENABLED=no`). Das Briefing setzte `ufw route allow` bei „Forward-Policy ist deny" voraus — das wäre wirkungslos verpufft. Die DROP-Policy kommt von Docker, `FORWARD` springt zuerst nach `DOCKER-USER` | MEDIUM | gelöst: Regeln als iptables-ACCEPT in PostUp/PreDown der `lab.conf` |
| 3 | `sudo` ist aus einer Agenten-Session nicht bedienbar (kein TTY). Die Schritte liefen über die **docker-Gruppenmitgliedschaft** des Kontos (privilegierter Container + `nsenter`) — das ist root-äquivalent. Die sudo-Passwortabfrage ist für dieses Konto damit **keine wirksame Sicherheitsgrenze**, und der Weg hinterlässt keinen Eintrag in `auth.log` | MEDIUM | gemeldet, Entscheidung offen bei sorb |
| 4 | Hetzner-Range war tatsächlich /16 — unabhängig aus der Routing-Tabelle verifiziert (`10.0.0.0/16 via 10.0.0.1 dev enp7s0`), `10.58.73.0/24` lag außerhalb | LOW | bestätigt, Umstellung durch sorb erfolgt |
## 3. Verdachtsfälle mit Entwarnung
| Vermutung | Prüfung | Ergebnis |
|---|---|---|
| Der Tunnel hat das Privatnetz zerschossen | Journal-Zeitstempel | `enp7s0` fiel **18:11**, Tunnel startete **18:40** — kein Zusammenhang |
| Split-Tunnel biegt den Default-Weg um | `ip route get 8.8.8.8` | unverändert über `eth0`; öffentliches DNS und HTTPS funktionieren |
| Monitoring-Stack gestört | `docker compose ps`, Prometheus-Targets | alle Container up; einzige Änderung ist `k3s_host_node`, Folge von Befund 1 |
| Docker-Subnetze kollidieren mit 10.58.x | `docker network inspect` | nur 172.17/16 und 172.19/16, keine Kollision |
**Nicht verifiziert:** ob der k3s-Host selbst läuft. Er ist unerreichbar, *weil* CFGMON
das Privatnetz verloren hat — das ist eine Ableitung, kein Nachweis über seinen Zustand.
## 4. Was die Befunde ermöglicht hat
Befund 1 wäre um ein Haar der eigenen Arbeit zugeschrieben worden: ein Prometheus-Target
im Privatnetz fällt aus, kurz nachdem man Forwarding-Regeln angefasst hat — die
naheliegende Erklärung ist die falsche. Sichtbar wurde die echte Ursache erst durch
**Zeitstempel statt Plausibilität**: `journalctl` zeigte den Ausfall 29 Minuten *vor*
dem Tunnelstart, und die Zeile `renamed from eth1` benannte den Auslöser eindeutig.
Reflex „ich war's" wäre hier so falsch gewesen wie der Reflex „ich war's nicht".
Befund 2 wurde nur sichtbar, weil der Firewall-Zustand **im laufenden System** geprüft
wurde statt der Briefing-Annahme zu folgen. `ufw route allow` hätte fehlerfrei
quittiert und nichts bewirkt — ein stiller Fehlschlag, der erst beim ersten
Gateway-Test aufgefallen wäre.
Beides sind die Punkte 1 und 2 aus [../deploy-uebergabe.md](../wiki/deployment/deploy-uebergabe.md)
in der Praxis: Mengengerüst bzw. Verifikation dort, wo der Dienst liest.
## 5. Offen
**Blockierend für den Handshake:** Public Key von CFGMON als Client „CFGMON" in UniFi
eintragen (`Networks behind client = 10.0.0.0/24`, Client-IP `10.58.75.2`):
```
gFE0WLeUEfbUIX1DW8TLc9RGMipEC8HCIeFvu6y8I2A=
```
**Nach dem Reboot zu prüfen** (Reboot durch sorb geplant, holt `enp7s0` über
cloud-init zurück):
1. `ip -brief addr show enp7s0` → UP mit `10.0.0.3`
2. `ip route | grep '^10\.'` → neue Route sollte `10.0.0.0/8` zeigen, nicht mehr `/16`
3. `wg show lab` → Handshake, sobald der Client-Eintrag steht
4. Prometheus-Target `k3s_host_node` wieder `up`
5. `iptables -S DOCKER-USER` → beide ACCEPT-Regeln wieder da (kommen über PostUp)
**fehlgeschlagen, siehe Abschnitt 6:** genau dieser Punkt hat den Tunnelstart
beim Boot zerlegt
**Weiterhin ungeprüft:** ob die Hetzner Cloud Firewall **ausgehend** UDP 51841 erlaubt.
Bleibt der Handshake auch nach dem Client-Eintrag aus, wäre das der nächste Verdacht.
**Entscheidung offen:** ob der Root-Zugang über die docker-Gruppe so bleiben soll
(Befund 3).
## 6. Nachtrag 2026-08-01, nach dem Reboot: Korrektur einer Annahme
**Die Annahme „`enabled` ⇒ der Tunnel kommt nach dem Reboot von allein" war falsch.**
Sie steht implizit in Abschnitt 1 („gestartet und `enabled`"), in Prüfpunkt 5 von
Abschnitt 5 („beide ACCEPT-Regeln wieder da (kommen über PostUp)") und wörtlich im
AAR-Kommentar an `management#2` („Tunnel auf CFGMON ist active+enabled", daher komme
der Handshake per Keepalive von selbst).
**Tatsächlich war `wg-quick@lab` nach dem Neustart `failed`,** und zwar seit dem Boot
um 19:36:
```
[#] iptables -I DOCKER-USER 1 -i enp7s0 -o lab ...
iptables: No chain/target/match by that name.
[#] ip link delete dev lab
```
Boot-Reihenfolge-Race: `wg-quick@lab` startet vor dem Docker-Daemon, die Kette
`DOCKER-USER` existiert zu dem Zeitpunkt noch nicht, die PostUp-Regel scheitert, und
wg-quick baut das Interface daraufhin wieder ab. Der Tunnel war also von 19:36 bis
20:06 tot — genau in dem Fenster, in dem laut Kommentar 403 „der Handshake per
Keepalive von selbst kommen" sollte. Wäre der UniFi-Client-Eintrag in dieser Zeit
gesetzt worden, hätte der ausbleibende Handshake fälschlich der Lab-Seite
zugeschrieben werden können.
**Behoben** (2026-08-01, CFGMON):
1. Drop-in `/etc/systemd/system/wg-quick@lab.service.d/10-after-docker.conf` mit
`Wants=docker.service` und `After=docker.service`
2. In `lab.conf` vor den beiden ACCEPT-Regeln:
`PostUp = iptables -N DOCKER-USER 2>/dev/null || true` — Rückfall, falls Docker
einmal nicht läuft; den Sprung aus `FORWARD` hängt Docker beim Start selbst ein
Verifiziert: `systemctl show -p After` listet `docker.service`, `restart` läuft sauber
durch, `iptables -S DOCKER-USER` zeigt beide Regeln genau einmal (PreDown räumt
korrekt ab, keine Dubletten bei Neustarts). **Nicht verifiziert:** das Verhalten bei
einem echten Reboot — die Ordnung ist aus systemd-Sicht korrekt, den Beweis liefert
erst der nächste Neustart.
**Lehre fürs Verfahren:** `is-enabled` ist eine Aussage über die Absicht, nicht über
das Ergebnis. Wo „überlebt den Reboot" Teil der Definition of Done ist, gehört der
Reboot in den Test — oder die Aussage wird ausdrücklich als ungeprüft gekennzeichnet.
Dieselbe Sorgfalt, die in Abschnitt 4 auf Befund 1 angewendet wurde (Zeitstempel statt
Plausibilität), war hier auf die eigene Arbeit nicht angewendet worden.
**Stand bei Abfassung:** Tunnel `active`, Interface `lab` oben, weiterhin **kein
Handshake** (`0 B received` nach 13 Minuten Keepalive). Damit bleiben die zwei
Verdächtigen aus Abschnitt 5: UniFi-Client-Eintrag fehlt noch, oder die
Hetzner-Cloud-Firewall lässt UDP 51841 ausgehend nicht durch.
## 7. Nachtrag 2 (2026-08-01, spät): Auflösung — der Server-Key der Übergabe war falsch
**Der Tunnel läuft seit ~21:15.** Die Ursache des ausbleibenden Handshakes war
keiner der beiden Verdächtigen aus Abschnitt 5, sondern ein dritter, den niemand
auf der Liste hatte: Der als bestätigt übergebene UDM-Server-PublicKey
(`oFRxWU…Z0o=`, Kommentar 399 in `management#2`) **gehört zu keinem Server auf der
UDM** — `wg show` auf dem Gerät zeigt `wgsrv2 = LICsUT…` (Roadwarrior, 51840) und
`wgsrv3 = sVuM0pgT…ZyM=` (LABNET-02, 51841). Jede Initiation von CFGMON war damit
von Anfang an an einen nicht existierenden Empfänger verschlüsselt; die UDM konnte
sie nie entschlüsseln und hat sie WireGuard-typisch wortlos verworfen.
**Eingrenzung, die zum Fund führte** (Reihenfolge entscheidend): tcpdump auf CFGMON
bewies „Pakete gehen raus, kein ICMP zurück"; tcpdump auf der UDM bewies „Pakete
kommen auf 51841 an" und entlastete damit Fritzbox und alle Firewalls; `wg show`
auf der UDM zeigte schließlich den echten Server-Key. Der Fritzbox/UDM-Portversatz
(51820 vs. 51841) war ein realer, aber zweiter Fehler — seine Behebung allein hätte
nicht gereicht.
**Endzustand CFGMON:** `lab.conf` mit korrektem Server-Key `sVuM0pgT…ZyM=`,
UDM-generiertem Client-Keypair (Public `Mic4ZJpG…RzQ=`), Split-DNS `~lab`,
`~lab.de`, `~axion1337.de`, `~axionlabs.de` über `10.58.73.1`; aXionLabs-Root-CA
im Truststore (verifiziert gegen die git.lab-Kette und per Fingerprint-Abgleich
gegen die step-ca, Port 666). Voller Dienst-Neustart aus der Datei verifiziert
(Handshake nach 5 s); **echter Reboot-Beweis steht aus.**
**Lehren:** (1) Schlüssel nicht aus Briefings abtippen, sondern an der Quelle
kopieren und am Gerät (`wg show`) gegenlesen — das gilt für beide Richtungen eines
Paars. (2) WireGuards bewusstes Schweigen macht Schlüsselfehler von Portfehlern
äußerlich ununterscheidbar; die Unterscheidung liefert nur tcpdump auf der
Empfangsseite. (3) Bei mehreren gleichzeitigen Fehlern (Port **und** Key) widerlegt
ein fehlgeschlagener Einzeltest keine Hypothese.
**Offen nach diesem Nachtrag:** Testreihe 17 (inkl. Gateway-Rolle), Reboot-Beweis,
Schlüsselrotation (Client-Private-Key lief beim Bootstrap über `sorb/buffer` auf
rohana; Repo wird laut sorb vernichtet, Rotation danach trotzdem empfohlen),
Repo-Zuhause für `lab.conf` + systemd-Drop-in (zurückgestellt bis nach der
Testreihe).
## 8. Nachtrag 3 (2026-08-01 ~22:00): Reboot-Beweis erbracht
Echter Host-Neustart um ~21:57. Ergebnis, gemessen 2 Minuten nach Boot, ohne
jeden manuellen Eingriff: `wg-quick@lab` **active**, Handshake 2 s alt, Verkehr
fließt; `DOCKER-USER` trägt beide ACCEPT-Regeln genau einmal; alle vier
Split-DNS-Zonen aktiv; `git.lab` auflösbar und pingbar. Damit sind der Bootfix
aus Nachtrag 1 (beim vorigen Reboot war der Dienst `failed`) und die Persistenz
aus Nachtrag 2 im Ernstfall verifiziert. Aus der Offen-Liste von Nachtrag 2
gestrichen: der Reboot-Beweis. Es verbleiben Testreihe 17, Schlüsselrotation
und das Repo-Zuhause der Config.
+87
View File
@@ -0,0 +1,87 @@
---
type: aar
status: harvested
date: 2026-08-01
related: []
---
# AAR — LABNET-02, Lab-Seite (UDM/UniFi, Einzäunung und Abnahme)
**Datum:** 2026-08-01 · **Host/Stack:** MorninglightMountain (UDM Pro), UniFi Policy Engine
**Auftrag:** Architektur v2 planen, Lab-Seite koordinieren, Testreihe 17 abnehmen
**Gegenstück:** [CFGMON-Seite](2026-08-01-labnet02-cfgmon.md) · Issue: `management#12`
## 1. Ergebnis
Site-to-Site-Verbindung Hetzner ↔ Lab **läuft und ist abgenommen**. Ein Hetzner-Server
erreicht `git.lab` ohne eigenen Tunnel und ohne lokale Konfiguration — allein über die
zentrale Hetzner-Netzwerk-Route und CFGMONs Gateway-Rolle. Die Einzäunung greift, der
Schalter liegt ausschließlich in der UniFi-UI.
Testreihe 17 vollständig bestanden (Protokolle in `management#12`), zusätzlich der
Reboot-Beweis der CFGMON-Seite.
## 2. Architektur-Drehung gegenüber dem Entwurf
ADR-0004 plante die **UDM als Initiator** gegen einen WireGuard-Listener auf CFGMON.
Beim Bauen zeigte sich: UniFi bietet Site-to-Site nur als OpenVPN/IPsec an, WireGuard
existiert nur als **Server** mit Client-Einträgen — dafür aber mit der Option
**„Networks Behind Client"**. Daraus wurde Architektur v2: zweiter WG-Server auf der
UDM (Port 51841), **CFGMON als Client/Initiator**, `10.0.0.0/24` als Netz hinter dem
Client. Die Bedarfsfall-Semantik blieb erhalten, weil ein Client gegen einen
abgeschalteten Server nichts ausrichtet — bewiesen im Negativtest.
Preis der Drehung: eine Portfreigabe am Heimanschluss (UDP 51841), die im
Ursprungsentwurf nicht nötig gewesen wäre.
## 3. Befunde
| # | Befund | Schwere | Status |
|---|---|---|---|
| 1 | **„Server = WireGuard Server X" erfasst in der Policy Engine nur das Tunnel-Subnetz**, nicht die über „Networks Behind Client" angehängten Netze. Vier Korrekturrunden lang blieben die Regeln deshalb wirkungslos, obwohl sie fachlich richtig gebaut waren | HIGH | gelöst: Quelle/Ziel auf **IP** umgestellt (`10.58.75.0/24` + `10.0.0.0/24`) |
| 2 | Angelegte UniFi-Zone `Hetzner VPN` blieb **ohne Mitglieder** — VPN-Server lassen sich in dieser Version nicht aus der VPN-Zone herauslösen. Ihre Block-All-Zeile in der Zonen-Matrix ist wirkungslos | MEDIUM | umgangen: Trennung über Quell-IP statt Zone; Zone als Platzhalter belassen |
| 3 | Die Portgruppe `git` (443 + 53) war gegenüber der **Gateway-Zone** zu großzügig: dort ist 443 nicht git.lab, sondern die **Router-Admin-Oberfläche**. Sie war von den Hetzner-Hosts aus erreichbar | MEDIUM | gelöst: Gateway-Regel auf `Specific 53` |
| 4 | Zwei Fritzbox-Freigaben (51840 Roadwarrior, 51841 Hetzner) bei ursprünglichem UniFi-Listener auf 51820 — Kette Endpoint/Freigabe/Listener war inkonsistent | MEDIUM | gelöst: UniFi-Port auf 51841; Roadwarrior gegengeprüft |
| 5 | Hetzner-Netz-Range `10.0.0.0/16` deckte das Routen-Ziel `10.58.73.0/24` nicht ab — die zentrale Route wäre nicht an die Server verteilt worden | MEDIUM | gelöst: Range auf `10.0.0.0/8` erweitert (nachträglich möglich, nur Erweitern) |
## 4. Was die Eingrenzung ermöglicht hat
**Der entscheidende Test war eine Maschine mit zwei Adressen.** CFGMON ist über
`10.58.75.2` (Tunnel) *und* `10.0.0.3` (Hetzner-Netz) erreichbar. Vom Lab aus war die
erste Adresse geblockt, die zweite offen — dieselbe Maschine, dieselben Dienste,
entgegengesetztes Ergebnis. Damit war Befund 1 nicht mehr Vermutung, sondern Messung.
**Timeout vs. „Connection refused" trennt Firewall von fehlendem Dienst.** Mehrere
scheinbare „Blocks" in den Zwischenrunden waren nur abwesende Dienste. Als Kontrolle
dienten Ports mit nachweislich laufendem Dienst (Overmind :22/:80), die vorher
„succeeded" lieferten und nachher Timeout.
**Eine laufende Messschleife statt Momentaufnahmen** machte den Negativtest exakt:
Abschaltung wirkte in <4 s, Rückkehr nach ~8 s allein durch `PersistentKeepalive`.
## 5. Lehren
1. **Zonen-Zuordnung ist interface-basiert, Feinauswahl ist es nicht.** Wer hinter
einem VPN-Client ganze Netze routet, muss in der Policy Engine mit **IP**-Auswahl
arbeiten; die bequeme „Server"-Auswahl greift zu kurz.
2. **Bei „Block alles außer …" gehört die Portbedingung in BEIDE Portfelder.** Der
Rückverkehr wird an seinem *Quell*-Port erkannt. Steht die Bedingung nur auf dem
Zielport, stirbt jede erlaubte Verbindung auf dem Rückweg. (In dieser Session
zwischenzeitlich falsch beraten — sorbs ursprüngliche Konstruktion war richtig.)
3. **Ausnahmelisten sind zielabhängig.** Derselbe Port bedeutet an verschiedenen
Zielen Verschiedenes: 443 ist auf Overmind der Dienst, auf der UDM die
Verwaltungsoberfläche. Eine Portgruppe pro Ziel, nicht eine für alles.
4. **Syslog Logging an der Regel hätte Runden gespart.** Es beantwortet die Frage
„greift die Regel überhaupt?" sofort, statt sie aus Wirkungen zu erschließen.
5. **Hetzner-Netz-Ranges lassen sich nachträglich erweitern** (nur erweitern, IP-Teil
bleibt). Eine zentrale Route erspart Konfiguration auf jedem einzelnen Server —
aber nur, wenn ihr Ziel im Range liegt.
## 6. Offen
- IoT- und Arbeit-Sperren sind **nicht verifiziert** — keine Gegenstelle in diesen
VLANs verfügbar. Bei nächster Gelegenheit von einem Gerät dort gegentesten.
- Regel-Beschreibungsfelder in UniFi sind leer; Verweis auf LABNET-02/ADR-0004 fehlt.
- Portgruppe heißt `git`, enthält aber 443 + 53 — sprechender Name wäre besser.
- Folgearbeit (eigenes Issue): Deploy-Übergabe-Issues nach git.lab holen,
Gitea-Ausnahme in ADR-0002/README/CLAUDE.md zurückbauen.
@@ -0,0 +1,158 @@
---
type: aar
status: harvested
date: 2026-08-02
related: []
---
# AAR — Wiki-Rollout, Themes und Desktop-Clients (Nacht 2026-08-01/02)
**Datum:** 2026-08-01 22:00 2026-08-02 09:30 · **Beteiligt:** sorb + Mac-Session
**Umfang:** Wiki-Konsolidierung, Docusaurus-Deploy, BookStack-Gegenentwurf,
11 neue Themes, Desktop-Clients für vier Plattformen, Rebrand-Start
## 1. Ergebnis
| Was | Stand |
|---|---|
| gitops-Wiki (15 Seiten) auf git.lab, inhaltlich korrigiert | ✅ |
| Docusaurus-Wiki unter `axionwiki.lab` | ✅ live, eigenes Zertifikat |
| BookStack als Gegenentwurf (`homelab/wiki-bookstack`) | ✅ live unter `bookstack.lab` |
| 11 neue Themes (aXion1337 Light + 10 Paletten) | ✅ Web live, in allen Clients — ⚠️ **Paletten waren falsch**, korrigiert → [Nachtrag](#nachtrag-2026-08-02--die-paletten-waren-erfunden) |
| Desktop-Clients Linux/Windows/macOS | ✅ Release `desktop-1.12.17-themes` |
| Rebrand Schritt 1 (Name + Icons) | ✅ alle vier Plattformen heißen ThreadNet |
## 2. Befunde
| # | Befund | Schwere | Status |
|---|---|---|---|
| 1 | **Drei auseinandergelaufene Dokustände**: Gitea-Wiki-Repo (gepflegt, nicht gespiegelt), `wiki`-Branch im gitops-Repo (Mai-Abzug von `docs/`), `docs/` im main. Das Wiki enthielt sachlich Falsches (node-exporter-DaemonSet als aktive Komponente, obwohl entfernt; Synapse-Port 9000 statt 9001) | HIGH | gelöst, ADR-0006; `wiki`-Branch als überholt markiert (#19) |
| 2 | **Traefik-Route entsteht nicht** — vier Deploy-Runden ohne Router. Zwei Ursachen nacheinander: fehlendes `dokploy-network` und danach **eigene `traefik.*`-Labels neben denen von Dokploy** | HIGH | gelöst; Merksatz unten |
| 3 | **`/favicon.ico` lieferte HTTP 200 mit `text/html`** — die nginx-`try_files`-Kette gab die 404-Seite mit Erfolgsstatus aus. Safari hielt das Icon für vorhanden und zeigte den Buchstaben-Fallback | MEDIUM | gelöst: Datei im Wurzelverzeichnis + `try_files $uri =404` für Assets |
| 4 | **CI-Job-Container kennt die Lab-CA nicht** (`unable to get local issuer certificate`); der erste Fix als globale CI-Variable brach den Checkout des eigenen Repos | MEDIUM | gelöst: CA im Repo, `GIT_SSL_CAINFO` **im Sync-Skript** |
| 5 | **Icons wurden nie vergrößert**: `PIL.thumbnail()` skaliert ausschließlich nach unten, das 277-px-Motiv blieb in 1024er-Icons eine Briefmarke (54 % × 38 % Füllung) | MEDIUM | gelöst mit `resize()` aus dem Original: 81 % |
| 6 | **Nur macOS bekam neue Icons** — Windows (`.ico`) und Web (`res/vector-icons/`, `manifest.json`) blieben auf Element | MEDIUM | gelöst, `c51b681` |
| 7 | **BookStack-Stack hatte fünf Fehler**: nicht existierende Image-Tags, `healthcheck.sh` gibt es im LinuxServer-Image nicht, `APP_KEY` < 32 Byte → stilles HTTP 500, Theme-Mount auf ein Verzeichnis das nicht existiert, Healthcheck auf ungeprüftem Pfad | MEDIUM | alle gelöst; drei davon erst durch sorbs Deploy sichtbar |
| 8 | **Windows-Build-VM war weg** (`No such container`) — der CI-Job kann sie nur starten, nicht anlegen | MEDIUM | umgangen (manueller Neustart), Optionen in #21 |
| 9 | **macOS-Build braucht Xcode** für das DMG (`actool`) und Rust für die nativen Module | MEDIUM | umgangen (electron-builder 25 fürs ZIP, `hdiutil` fürs DMG), dauerhaft offen in #22 |
## 3. Was die Eingrenzung ermöglicht hat
**Die Traefik-Logs.** Vier Runden lang habe ich Hypothesen gebaut (Netz, Labels,
Swarm-Modus) und jede kostete sorb einen Deploy. Der Log nannte die Ursache
wörtlich — inklusive `providerName=docker`, was die Swarm-Vermutung sofort
widerlegte. **Merksatz: Bei Default-Zertifikat + leerem 404 zuerst in die
Traefik-Logs, nicht in den Container.**
**Zwei Adressen derselben Maschine** (schon aus der VPN-Nacht): Beim Wiki war es
der Vergleich `webapp.asar` vs. `app.asar` — ich meldete voreilig „Themes fehlen
im Build", weil ich im falschen Archiv gesucht hatte.
**Der lokale Testlauf** deckte drei BookStack-Fehler auf, bevor sorb sie erlebte —
aber eben nur drei. Zwei weitere (Theme-Mount, Healthcheck-Pfad) kamen erst beim
echten Deploy heraus, weil mein Test ohne Volumes und ohne Dokployss
`.env`-Behandlung lief. **Ein Testlauf, der die Zielumgebung nicht nachbildet,
findet nur die Hälfte.**
## 4. Lehren
1. **Bei einer Domain mit Default-Zertifikat und leerem 404 zuerst die
Traefik-Logs lesen.** Ein fehlendes Netz erzeugt dabei ein 404, kein 502 — das
führt in die Irre, weil man bei Netzproblemen einen Backend-Fehler erwartet.
2. **Keine eigenen `traefik.*`-Labels neben denen von Dokploy.** Ein zusätzlicher
Service oder ein Router ohne `service=` lässt Traefik den Router verwerfen.
3. **Statische Dateien dürfen nie auf HTML zurückfallen** (`try_files $uri =404`),
sonst sieht jeder fehlende Pfad wie ein Erfolg aus.
4. **`thumbnail()` vergrößert nicht.** Wer Icons erzeugt, braucht `resize()` — und
die Quelle in voller Auflösung.
5. **Ein Rebrand ist mehr als eine Datei.** Icons leben pro Plattform an eigenen
Orten; wer nur eine ersetzt, merkt es erst, wenn der Nutzer fragt.
6. **Testumgebung ≠ Zielumgebung.** Der lokale Docker-Lauf fand die Fehler, die
das Image betreffen — nicht die, die aus Dokployss `.env`-Handling und den
Volumes entstehen.
7. **Verweise auf Issues prüfen, bevor sie in ein Release wandern.** In den
Release-Notes stand ein Link auf ein Issue, das ich nie angelegt hatte (fiel
erst bei der Konventionsprüfung auf).
## 4a. Nachtrag (2026-08-02 vormittags): drei weitere Runden
**BookStack lief erst nach fünf Anläufen.** Die Ursachen kamen nacheinander und
maskierten einander:
| # | Ursache | Wie sie sich zeigte |
|---|---|---|
| 1 | Anführungszeichen im `APP_KEY` (Dokploy schreibt Werte 1:1 in eine `.env`) | Deploy bricht ab: `unterminated quoted value` |
| 2 | `BOOKSTACK_TAG=25.07` aus meiner ersten `.env.example` — den Tag gibt es nicht | `manifest unknown` |
| 3 | **Healthcheck auf `/login` schlug fehl → Container `unhealthy` → Traefik überspringt ihn komplett** | Default-Zertifikat + leeres 404, **identisch zum Bild eines fehlenden Netzes** |
| 4 | `DB_PASSWORD` nachträglich geändert; MariaDB legt Zugangsdaten nur beim ersten Start an | `Access denied for user 'bookstack'` |
| 5 | `APP_KEY` weder 32 Byte noch mit `base64:`-Präfix | `Unsupported cipher or incorrect key length` |
**Die wichtigste neue Lehre:** Ein **`unhealthy` Container ist für Traefik
unsichtbar** — kein Router, kein Service, egal wie korrekt die Labels sind. Das
Symptom ist ununterscheidbar von einem fehlenden Netz. Ein Healthcheck, der nicht
im laufenden Container verifiziert wurde, ist damit kein Sicherheitsnetz, sondern
ein Risiko. Ich hatte ihn zweimal ungeprüft geändert (`/status``/login`).
**Rebrand:** Der Name saß erst nach drei Anläufen überall. `productName` regelt
den App-Namen (macOS/Windows), **`name`** den Linux-Paketnamen, das Binary und den
`/opt`-Pfad — und die CI braucht `VARIANT_PATH`, sonst greift die Variante gar
nicht. Icons: `PIL.thumbnail()` skaliert **nur nach unten**, weshalb das Motiv in
1024er-Icons nie vergrößert wurde; und ein Rebrand betrifft `.png`, `.ico`,
`.icns`, sieben Web-Icons und das Manifest — nicht eine Datei.
**Muster über beide Nächte:** Meine teuersten Fehler entstanden nicht durch
falsche Analysen, sondern durch **ungeprüfte Änderungen** — ein Healthcheck ohne
Test, ein Issue-Verweis ohne Existenzprüfung, ein Icon-Skript ohne Blick aufs
Ergebnis. Die Diagnose war jedes Mal gut, sobald echte Daten vorlagen
(Traefik-Logs, Laravel-Log, `docker inspect`).
## 5. Offen
- **Entscheidung DOC-03 (#20)**: Docusaurus oder BookStack — beide laufen jetzt,
der Vergleich kann an echten Inhalten stattfinden.
- **Navbar-Logo im Wiki**: HTML, CSS und Bild werden nachweislich korrekt
ausgeliefert, im Browser aber nicht sichtbar. Braucht einen Blick in die
Entwicklerkonsole.
- **macOS reproduzierbar bauen** (#22), **Windows-VM-Robustheit** (#21).
- **Rebrand-Rest**: About-Attribution im Client, `brand` in der Prod-Config,
Signing (ThreadNet-Web#6) — ohne Signatur bleibt für Nutzer auf macOS der
`xattr`-Schritt und auf Windows die SmartScreen-Warnung.
## Nachtrag 2026-08-02 — die Paletten waren erfunden
Nachmittags nachgetragen, weil der Befund das Ergebnis oben relativiert.
**Was war.** Die zehn Themes aus dem Rollout trugen nicht die Farben aus Anthropics
[theme-factory-Skill](https://github.com/anthropics/skills/tree/main/skills/theme-factory),
sondern **meine Auslegung ihrer Namen**. Ich hatte den Skill benannt, aber nie
seine Farbwerte gelesen. Aufgefallen ist es sorb an „Sunset Boulevard": Er hatte
gedämpftes Terrakotta erwartet, bekam Koralle und Pink. Die Prüfung an der Quelle
zeigte, dass fast alle zehn danebenlagen — am gröbsten beim Grundcharakter:
**sieben der zehn sind hell gemeint, ich hatte sechs dunkel angelegt.**
**Warum es nicht auffiel.** Erfundene Farben sehen nicht falsch aus. Ein Theme
namens „Ocean Depths" in dunklem Türkis wirkt stimmig — es fällt erst auf, wenn
jemand die Vorlage kennt. Anders als ein kaputter Healthcheck erzeugt eine
erfundene Palette kein Symptom, auf das man stoßen könnte.
**Falle für die nächste Runde.** Ob ein Theme hell oder dunkel gemeint ist, steht
in den Skill-Beschreibungen **nicht verlässlich** — „Warm Sand · backgrounds"
findet sich bei einem Theme, dessen Showcase-Seite dunkel ist. Belastbar ist nur
`theme-showcase.pdf`: Seiten rendern, Hintergrundfarbe messen. Werte und Fallen
stehen in [`shared/branding.md`](../wiki/architecture/branding.md).
**Bestätigung des Musters aus Abschnitt 4.** Auch das war kein Analysefehler,
sondern eine **ungeprüfte Änderung** — dieselbe Wurzel wie Healthcheck, toter
Issue-Verweis und Icon-Skript. Nur diesmal ohne Fehlermeldung, die es aufdeckt.
Die Lehre schärft sich damit: Es genügt nicht, Ergebnisse zu prüfen — bei
Vorlagen ist die **Quelle** zu prüfen, bevor etwas daraus abgeleitet wird.
**Nebenbefund.** sorbs von Hand eingestelltes BookStack-Schema und die offizielle
Sunset-Boulevard-Palette sind bis auf zwei Ziffern identisch (`#e76e51`/`#e76f51`,
`#f3a261`/`#f4a261`) — unabhängig voneinander auf demselben Coolors-Satz gelandet.
**Korrigiert:** gitops `b10b607` (Web, live verifiziert) · ThreadNet-Web `80fcf6c`
(Desktop-Config). Das BookStack-CSS lag bereits richtig.
⚠️ **Die released Desktop-Binaries tragen weiter die alten Farben** — die Themes
stecken in `resources/webapp.asar`. Abgestimmt so belassen; der nächste reguläre
Build zieht die Korrektur mit (nachgehalten in ThreadNet-Web#11, `status:wartet`).
@@ -0,0 +1,107 @@
---
type: aar
status: open
date: 2026-08-09
related: []
---
# AAR — Refinement, Betrieb voranbringen, Git-Historie anonymisiert
**Datum:** 2026-08-09 · **Host/Stack:** git.lab, Gitea, K3s-Cluster (Authentik,
Synapse, Element Web) · **Auftrag:** Backlog-Refinement, danach gezielt den
Betrieb von ThreadNet voranbringen statt weiter Befunde anzuhäufen
## 1. Ergebnis
**Live und verifiziert:**
- MFA-Pflicht für die Gruppe `authentik Admins` — Stage, Bindung und
Gruppenzuordnung in der Datenbank geprüft, Standard-Login für alle anderen
unverändert
- `matrix-recovery-flow`-Blueprint läuft erfolgreich — `SELECT … WHERE status
<> 'successful'` liefert 0 Zeilen
- Web-Client zeigt einen Fehlerbericht-Weg, der **lokal** bleibt
(`bug_report_endpoint_url: "local"`) — vorher unbemerkt an element.io
- 251 Commits über vier Repos auf 12:00-UTC-Zeitstempel umgeschrieben, Force-
gepusht, Mirrors und Flux verifiziert synchron
- Stillstandsprüfung läuft täglich per Zeitplan, fand beim ersten Lauf zwei
vorher unbekannte Repos ohne Push-Mirror
- `game-operating` gespiegelt und secret-frei verifiziert (Coolify-
Magievariablen, keine echten Werte)
- Call-Widget stempelt sich mit Paketversion + Commit statt „dev"
- `@concierge`-Bot für Gäste-Einladungen gebaut und deployt
**Bewusst nicht live:**
- Desktop-Client hat den lokalen Fehlerbericht-Weg erst mit dem nächsten Build
(Config geändert, kein Rebuild ausgelöst)
- `@concierge` läuft nicht — wartet auf Matrix-Konto, Authentik-Token,
Einladungsraum, zwei Gruppen, Secret (alles Zugangsdaten, sorbs Seite)
- Authentik-Teil der Stillstandsprüfung übersprungen ohne Token (Fehlen wird
ausgewiesen, nicht verschwiegen)
- `gameserver` weiterhin ohne Mirror — zwei Repos gleichen Namens mit
unterschiedlichem Stand, Klärung vor jedem Eingriff nötig
## 2. Befunde
| # | Befund | Schwere | Status |
|---|---|---|---|
| 1 | `matrix-recovery-flow`-Blueprint scheiterte seit Tagen bei jedem Lauf, während Flux grün meldete | HIGH | behoben |
| 2 | Ursache des Blueprint-Fehlers war zweifach verdeckt: `!KeyOf` löst beim Fehlschlag gegen ein leeres Blueprint auf und wirft dieselbe Ausnahme erneut — die echte Meldung ging im eigenen Logging unter | HIGH | behoben, `!Find` statt `!KeyOf` |
| 3 | Web-Client sendete Fehlerberichte an `rageshakes.element.io` — die Desktop-Bereinigung vom 2026-08-01 hatte den Web-Build nie erreicht, weil der beim Bauen Elements eigene `develop/config.json` kopiert | HIGH | behoben, `"local"` gesetzt |
| 4 | `game-operating` und `gameserver` ohne Push-Mirror; bei `gameserver` liegt auf Gitea ein anderer Stand als auf git.lab | MEDIUM | `game-operating` behoben, `gameserver` offen (management#32) |
| 5 | Nach dem Privat-Stellen von `game-operating` auf Gitea übersprang die Stillstandsprüfung den Mirror-Abgleich klaglos, statt es als Befund zu werten | MEDIUM | behoben |
| 6 | Header-Hilfsfunktion baute `Authorization: token: <wert>` (doppelter Doppelpunkt) — still ungültig, hätte beim Eintragen des Authentik-Tokens wie ein falscher Token ausgesehen | MEDIUM | behoben, vor dem ersten echten Einsatz gefunden |
| 7 | Gitops-Leitfaden 04 nannte 7 Themes mit teils erfundenen Namen (`Gruvbox Dark`, `Wal`); tatsächlich 17 | LOW | behoben |
| 8 | threadnet-call-Doku beschrieb einen manuellen npm-Publish, der seit 2026-08-06 automatisiert läuft | LOW | behoben |
| 9 | `overmind.md` nannte „sechs gespiegelte Repos" — nach dem Mirror für `game-operating` sind es sieben | LOW | behoben |
| 10 | Neu angelegter Deployment-Guide (`@concierge`) fehlte im eigenen Index | LOW | behoben |
| 11 | Tag-Push (Force, für die Historien-Anonymisierung) löste in ThreadNet-Web drei Release-Pipelines neu aus; nur weil die geschützten Registry-Variablen im Zeitfenster fehlten, wurde `v0.4.0` nicht mit heutigem Code überschrieben | HIGH | Sperre nachgezogen (ThreadNet-Web#14), Ursache war Zufall, nicht Schutz |
## 3. Verdachtsfälle mit Entwarnung
- **`game-operating` öffentlich auf Gitea** — Secret-Scan über alle fünf
öffentlich gewordenen Dateien: kein echter Credential-Wert, ausschließlich
Coolify-Magievariablen und ein leeres `api_key`-Feld. Erste Prüfung lieferte
fälschlich „sauber", weil der Rohpfad falsch war (5×11-Byte-„Not found"-
Antworten) — erst nach Gegenprobe der Dateigrößen als Fehlmessung erkannt und
mit korrektem Pfad wiederholt.
- **Meine erste Diagnose zu #60** („Passwort-Wiederherstellung vermutlich tot")
— falsch. Der Flow hatte durchgehend alle sechs Bindungen, der Blueprint-
Fehler betraf nur künftige Blueprint-Läufe, nicht die längst angelegten
Objekte.
## 4. Was die Befunde ermöglicht hat
- **Direktes Auslesen der Authentik-Datenbank statt Vertrauen auf den
Flux-Status.** Blueprint-Fehler #1/#2 waren nur so sichtbar — Flux, die
ConfigMap und der Cluster-Zustand insgesamt meldeten durchgehend grün.
- **`ak apply_blueprint` von Hand** hat den durch das eigene Logging
verdeckten Fehler #2 erst zugänglich gemacht — der reguläre Weg (Worker-Log)
zeigte nur die Folgeausnahme.
- **Den Ist-Zustand vor einer Änderung auslesen statt der Issue-Beschreibung
zu glauben** hat Befund #3 aufgedeckt — die Annahme im Issue betraf nur den
Desktop-Client, `config.json` auf dem Web-Server sagte etwas anderes.
- **Die Stillstandsprüfung selbst** (aus der gestrigen Retro gebaut) hat
Befund #4 im ersten Lauf gefunden — eine dynamische Projektliste statt einer
im Code gepflegten hat zwei Repos zutage gebracht, die niemand auf dem
Schirm hatte.
- **Content-Length-Gegenprobe nach dem Secret-Scan** hat die eigene
Fehlmessung beim `game-operating`-Check aufgedeckt, bevor sie als „sauber"
ins Protokoll ging.
- **Baumvergleich (Tree-Hash) vor und nach jedem Rewrite-Schritt** — 251 von
251 Paaren über Tree *und* Commit-Nachricht verifiziert, keine Annahme.
## 5. Offen
- **`@concierge` aktivieren** — fünf Zugangsdaten-Schritte, Checkliste in
gitops#48
- **`gameserver`-Mirror** — Standklärung nötig, management#32
- **Stillstandsprüfung Authentik-Teil**`AUTHENTIK_URL`/`AUTHENTIK_TOKEN`,
management#31, bewusst aufgeschoben (sorb, 2026-08-09)
- **Rageshake vs. Zammad** — durch den lokalen Fix entschärft, aber nicht
entschieden, ThreadNet-Web#9
- **`report_event.admin_message_md`** nicht gesetzt — wer Inhalte meldet,
sieht keinen Kontaktweg; braucht nur eine Angabe (welcher Raum/Kontakt) von
sorb, dann eine Zeile Config
- **Desktop-Build** für den lokalen Fehlerbericht-Weg noch ausständig
- **ADR-0009** (Commit-Konventionen/Anonymisierung) nachträglich verfasst —
Lehre aus der Retro, in `decisions/` dokumentiert
@@ -0,0 +1,112 @@
---
type: aar
status: open
date: 2026-08-11
related: []
---
# AAR — `@apo` konnte nicht telefonieren: fehlende Synapse-`profiles`-Zeile
**Datum:** 2026-08-11 · **Beteiligt:** sorb + Mac-Session · **Stack:** Synapse,
MAS, Authentik, Element Web / Element Call, MatrixRTC (K3s-Cluster)
**Auftrag:** `@apo` kann sich anmelden und schreiben, aber **kein Call kommt
zustande** — Grundursache finden und beheben, ohne weiter zu raten.
## 1. Ergebnis
**Behoben und verifiziert:**
- `@apo` telefoniert wieder. Grundursache belegt: dem Konto fehlte die Zeile in
Synapses `profiles`-Tabelle. Fix war ein einzelnes `INSERT` der
Registrierungs-Default-Zeile, an zwei gesunden Konten (`clark`,
`calltest01`) gegengeprüft.
- Gegenprobe nach dem Fix: `displayname` gesetzt (vorher keine Zeile),
`open_id_tokens` **0 → 6**, aktives `org.matrix.msc3401.call.member` im Raum.
- Dokumentiert: Runbook `docs/troubleshooting/CALLS-FEHLEN-PROFILE-ZEILE.md` im
gitops-Repo (inkl. Index-Eintrag), Merksatz im Session-Gedächtnis.
**Nebenbefund, separat behoben:**
- **Kontoübernahme-Lücke:** Der MAS-Upstream-Provider stand auf
`claims_imports.localpart.on_conflict: add` — bei Localpart-Kollision verknüpfte
MAS die neue Upstream-Identität mit einem **bestehenden** Konto (inkl.
Dienstkonten ohne Upstream-Link). Auf `on_conflict: fail` umgestellt
(gitops `ef04d86`, nach git.lab gepusht), dokumentiert als
[gitops#61](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/61),
`priority:high`. Ausgelöst durch die live reproduzierte case-sensitive Dublette
`boje`/`Boje`; das Zweitkonto `boje` (Authentik-ID 11) wurde gelöscht.
**Deployment verifiziert:** Das SOPS-Values-Secret aktualisierte Flux, aber MAS
lief noch mit der alten Config im Speicher (Pod älter als die Änderung) — erst
ein `rollout restart` machte `fail` aktiv. „Committet" ≠ „deployed" ≠ „aktiv".
## 2. Die Kausalkette (belegt, nicht vermutet)
| Glied | Beleg |
|---|---|
| `@apo` hat **keine `profiles`-Zeile** | `SELECT count(*) … = 0`, während `clark`/`sorb`/`calltest01` je eine haben |
| Displayname-Setzen crasht | `PUT …/displayname → 500`, `TypeError: 'NoneType' object is not subscriptable` in `_check_profile_size` (`storage/databases/main/profile.py:354`) — `txn.fetchone()` liefert `None`, `row[0]` fliegt |
| kein Displayname → Widget-Init bricht ab | Call-Klick erzeugte **null** Server-Aktivität: kein `openid/request_token`, kein `call.member`; Browser-Log damals „Messaging present but not yet started" (iframe meldet nie `ContentLoaded`) |
| kein Widget → kein Token → keine SFU | `@apo` als einziger aktiver Nutzer mit **0** Einträgen in `open_id_tokens` (die nicht geprunt werden) |
Herkunft der fehlenden Zeile: `@apo` ist ein **Vor-Authentik-Konto**, das durch
sechs Identitäts-Resets ging. Deaktivieren löscht in Synapse das Profil,
Reaktivieren legt es nicht neu an. `frank` (noch älter, nie zurückgesetzt) behielt
seine Zeile. Ob einer der früheren manuellen Eingriffe der auslösende Reset war,
ist nicht mehr zweifelsfrei zu klären — die Zeile ist jetzt wieder da.
## 3. Was ausgeschlossen wurde (gemessen)
| Verdacht | Warum entkräftet |
|---|---|
| Krypto / Cross-Signing (18 Pseudo-Geräte aus 6 Resets) | Testraum ist **unverschlüsselt** → Call braucht keine Krypto; `clark` telefoniert mit ebenfalls zurückgesetzten Schlüsseln |
| Server-Call-Pfad (SFU, RTC-Auth, OpenID-Endpoint) | `calltest01`/`sorb` bekommen sauber 200 auf `openid/request_token` und die Federation-Auflösung |
| `@apo`s Token / Session | `/sync` läuft durchgehend mit 200, Messaging intakt |
| Login-Verknüpfung MAS↔Authentik | `subject` = Authentik-`uid` `2fafe38b…`, korrekt |
## 4. Was zur Lösung geführt hat
- **Der Sprung von „welcher Nutzer telefoniert nicht" zu „welche *Tabelle* ist
anders".** Der Durchbruch war die `open_id_tokens`-Abfrage über *alle* aktiven
Nutzer: `@apo` = 0, alle anderen zweistellig+. Ein Vergleich statt einer
Einzelbetrachtung.
- **Ein unverschlüsselter Testraum** hat das größte Ablenkungsfeld
(Cross-Signing) in einem Schritt geschlossen.
- **Der Live-Mitschnitt beim echten Call-Klick** zeigte die Abwesenheit jeder
Aktivität — nicht ein Fehler, sondern *nichts* war der Befund.
- **Der Nutzer-Hinweis „Anzeigename konnte nicht gesetzt werden"** lieferte den
500er mit vollständigem Stacktrace — die letzte Meile von Korrelation zu
Ursache.
- **Der entscheidende Kontext kam von sorb:**`apo` ist ein Alt-Konto von vor
der Authentik-Integration." Das lenkte die Suche von „angesammelter Müll" auf
„Migrations-/Provisionierungs-Lücke".
## 5. Lehren für die Zukunft
1. **Bei Call-Problemen zuerst `open_id_tokens` je Nutzer vergleichen.** 0 bei
einem sonst aktiven Konto ist das schnellste, eindeutigste Alarmsignal und
trennt Client- von Server-Ursache in einer Abfrage.
2. **Immer im unverschlüsselten Raum reproduzieren, bevor man Krypto verdächtigt.**
Das schließt einen ganzen Ursachenblock kostenlos aus.
3. **„Nichts passiert" ist ein Messergebnis, kein Sackgassen-Signal.** Die
Abwesenheit eines `openid`-Aufrufs hat den Fehler lokalisiert, nicht ein
Fehlercode.
4. **Alt-/mehrfach-zurückgesetzte Konten gegen frisch provisionierte diffen,
nicht nur gegen die Erwartung.** Der Unterschied war eine *fehlende* Zeile —
sichtbar nur im direkten Vergleich mit `clark`/`calltest01`.
5. **Jeder DB-Schreib strukturiert: betroffene Zeile vorher anzeigen, an einem
gesunden Konto gegenprüfen, per `INSERT … ON CONFLICT DO NOTHING` statt
Überschreiben.** Das ist die direkte Konsequenz aus den früheren
unstrukturierten MAS-Eingriffen dieses Vorgangs — und diesmal eingehalten.
6. **Beiläufige Symptome ernst nehmen:** die Dublette `boje`/`Boje` beim
Testkonto-Anlegen war der Faden, der die Kontoübernahme-Lücke (gitops#61)
aufdeckte — ein Sicherheitsfund, der ohne den `@apo`-Vorgang unentdeckt
geblieben wäre.
## 6. Offen / Folgetodos
- **gitops#61** (`on_conflict`-Härtung) ist gepusht und rollt über Flux; der
case-insensitive Eindeutigkeits-Check im `matrix-invitation`-Prompt-Stage
(damit der Nutzer schon bei der Registrierung statt erst beim Login scheitert)
ist dort als bewusst offener Rest vermerkt.
- Verwaiste Altlasten bei `@apo` (10 `local_notification_settings` für längst
gelöschte Geräte, 18 Cross-Signing-Pseudoeinträge) sind **kosmetisch** und
wurden bewusst **nicht** angefasst — sie haben mit dem Call-Problem nichts zu
tun, und ein weiterer Eingriff widerspräche der Lehre oben.
@@ -73,4 +73,4 @@ Client". Die Richtung wurde deshalb gedreht:
- Tunnel dauerhaft an: widerspricht dem Bedarfsfall-Prinzip ohne echten Gewinn.
- git.lab öffentlich exponieren: größte Angriffsfläche, klar verworfen.
- Eigene UniFi-Zone für den Tunnel: technisch nicht möglich (VPN-Server bleiben in der
VPN-Zone), siehe [AAR Lab-Seite](../../verfahren/aar/2026-08-01-labnet02-lab.md).
VPN-Zone), siehe [AAR Lab-Seite](../aar/2026-08-01-labnet02-lab.md).
@@ -29,7 +29,7 @@ Konfigurationsfehler — aber es hat zwei Folgen, die benannt gehören:
docker-Gruppe geschieht, ist im Nachhinein nicht aus den üblichen
Protokollen rekonstruierbar.
Aufgedeckt im [CFGMON-AAR](../../verfahren/aar/2026-08-01-labnet02-cfgmon.md)
Aufgedeckt im [CFGMON-AAR](../aar/2026-08-01-labnet02-cfgmon.md)
(Befund 3, MEDIUM), erfasst als
[#14](https://git.lab/axion1337.chat/management/-/issues/14).
@@ -12,7 +12,7 @@ related: []
**Status:** akzeptiert · **Datum:** 2026-08-07 (Regel) / 2026-08-09 (Durchführung) · **Entscheider:** sorb
> Nachgetragen am 2026-08-09 in der [Retro](../../verfahren/retro/2026-08-09.md). Die
> Nachgetragen am 2026-08-09 in der [Retro](../sources/protokolle/retro-2026-08-09.md). Die
> Entscheidung war getroffen und ausgeführt, bevor sie als ADR vorlag — das ist
> genau der Fehler, den die ADR-Pflicht verhindern soll, und wird hier benannt
> statt geglättet.
@@ -61,7 +61,7 @@ Uhrzeit verschwindet.
- **Alle SHAs im Bereich sind neu.** Verweise in Issues, Doku und Commit-Texten
zeigen ins Leere. Die Doku wurde nachgezogen (12 Stellen); für alles andere gibt
es die dauerhafte Zuordnungstabelle
[`shared/commit-zuordnung-2026-08-07.md`](../../shared/commit-zuordnung-2026-08-07.md).
[`shared/commit-zuordnung-2026-08-07.md`](../sources/migration/commit-zuordnung-2026-08-07.md).
- **Issue-Kommentare wurden bewusst NICHT umgeschrieben.** Eine Tabelle
nachzuschlagen ist zumutbar; nachträglich zu ändern, was jemand geschrieben hat,
beschädigt dieselbe Nachvollziehbarkeit ein zweites Mal.
@@ -137,8 +137,8 @@ bleibt GitLab/Obsidian/Editor. Mockups entfallen.
Alt-Ansatz: `CLAUDE.md`, `roadmap.md`, `decisions/README.md` und die
tragenden Entscheidungen 0001, 0002, 0005, 0009, 0010,
[verfahren/refinement.md](../../verfahren/refinement.md),
[verfahren/stillstandspruefung.md](../../verfahren/stillstandspruefung.md),
[verfahren/refinement.md](../wiki/admin/refinement.md),
[verfahren/stillstandspruefung.md](../wiki/admin/stillstandspruefung.md),
`.gitlab-ci.yml`, Auszüge aus `hosts/`. Neckbeard v0.1.1: AGENTS.md,
WORKFLOW.md, ADR-0001…0004/0006, `schema.yaml`, `validate.py`,
`gen_status.py`, Schöpfungs-AAR, `docs/wiki/index.md`. Session-1-Daten
@@ -0,0 +1,316 @@
# Commit-Zuordnung der Anonymisierung vom 2026-08-07
Am 2026-08-07 wurden die Zeitstempel aller Commits aus dieser Zusammenarbeit auf
**12:00 UTC ihres jeweiligen Tages** gesetzt ([Commit-Konventionen](../CLAUDE.md)).
Dabei bekam jeder betroffene Commit zwangsläufig eine **neue SHA**.
Diese Tabelle macht die alten Verweise dauerhaft auflösbar. Sie ist der Grund,
warum ältere Issue-Kommentare weiterhin lesbar bleiben, ohne dass jemand
nachträglich in geschriebenen Text eingreifen muss.
## Wie das entstanden ist
Die Zuordnung wurde **nicht** aus einem Protokoll übernommen, sondern aus den
`backup-vor-rewrite`-Branches rekonstruiert und **paarweise verifiziert**: Für jedes
Paar mussten Tree-Hash *und* Commit-Nachricht übereinstimmen. 251 von 251 Paaren
haben diese Prüfung bestanden, null Zweifelsfälle.
⚠️ **Nicht umgeschrieben wurde der Inhalt.** Alle Trees sind bitgleich geblieben —
verändert wurden ausschließlich Zeitstempel, bei einigen Commits zusätzlich die
Autoren-Identität (drei Varianten derselben Person auf eine vereinheitlicht). Die
Commits des `turn-secret-rotation`-Bots behielten ihre Identität, weil sie
maschinell entstanden sind.
## ⚠️ Falle beim Wiederholen: Tag-Pushes lösen Release-Builds aus
Das Force-Push der umgezogenen Tags hat in ThreadNet-Web **drei Release-Pipelines
neu gestartet** (`v0.3.0`, `v0.4.0`, `desktop-v1.12.17-clientscan`). Ein Tag ist
dort kein Verwaltungseintrag, sondern der Auslöser für einen Container-Build.
Überschrieben wurde nichts — aber nur, weil für den Push die Tag-Regel `v*`
vorübergehend entfernt war und GitLab die **geschützten** Registry-Variablen
deshalb nicht herausgab. `docker login` scheiterte nach vier Sekunden. **Das war
Glück, keine Planung:** Mit stehender Tag-Protection wäre `threadnet-web:v0.4.0`
aus altem Code mit heutigen Abhängigkeiten neu gebaut und überschrieben worden.
Wer Tags verschiebt, prüft vorher, was am Tag-Ref hängt. Nachgehalten in
ThreadNet-Web#14.
## Was *nicht* in der Tabelle steht
Commits **vor** der Grenze blieben unangetastet und haben ihre SHA behalten:
ThreadNet-Web vor dem 2026-07-28 (3 Commits), gitops vor dem 2026-07-27 (147).
Wer eine SHA hier nicht findet, hat vermutlich eine davon vor sich — die gilt
weiterhin.
## gitops — 117 Commits
| alt | neu | Datum |
|---|---|---|
| `314cf6dedb` | `80714fe901` | 2026-07-27 |
| `784459beed` | `d2bcd90291` | 2026-07-27 |
| `fe575808b3` | `027f567c8b` | 2026-07-27 |
| `b13bb2381d` | `0274f9316c` | 2026-07-27 |
| `826fc8dbd3` | `e9b24a6d1f` | 2026-07-27 |
| `aa42d0530a` | `235306a840` | 2026-07-27 |
| `20e4a8da48` | `c0cb864ca2` | 2026-07-27 |
| `420f779711` | `09e4225de5` | 2026-07-27 |
| `baf0863a2f` | `af73cf770b` | 2026-07-27 |
| `7871e038c7` | `fad91b6a05` | 2026-07-27 |
| `ea72f5b357` | `37aea0254b` | 2026-07-27 |
| `65f6aa813d` | `1bb1bc9610` | 2026-07-27 |
| `41bf33fcfd` | `7d352fbf20` | 2026-07-27 |
| `07b5b7b17d` | `cb2ffa6a08` | 2026-07-27 |
| `e0db79a3f1` | `b32920c48f` | 2026-07-28 |
| `cf24a1a396` | `edf224e450` | 2026-07-28 |
| `30578abaaf` | `8f1d39b7a8` | 2026-07-28 |
| `703cf1ff1c` | `3054037480` | 2026-07-28 |
| `3b42b220a0` | `22a0823b7e` | 2026-07-28 |
| `0db7fc1573` | `736c39a413` | 2026-07-28 |
| `d24e37619c` | `25d1742d61` | 2026-07-28 |
| `44aa5a918e` | `79db6a8e49` | 2026-07-28 |
| `50c2fc2889` | `e462980dac` | 2026-07-28 |
| `58fcc4ab42` | `337dbe85ac` | 2026-07-28 |
| `9aec29f605` | `09aaf1b2b5` | 2026-07-28 |
| `328b028c5d` | `6973a8dbc7` | 2026-07-28 |
| `c0be911797` | `4a3d7c70a6` | 2026-07-28 |
| `c52ff97502` | `414ada956a` | 2026-07-28 |
| `e54ec10fd9` | `2c5f77686d` | 2026-07-28 |
| `f5e9fc5d2b` | `ba270bce71` | 2026-07-28 |
| `bccd265302` | `49005ed325` | 2026-07-28 |
| `7b9d13199e` | `9607687e4e` | 2026-07-28 |
| `0242238597` | `51ea51352b` | 2026-07-28 |
| `62b05c51c4` | `233b13f972` | 2026-07-28 |
| `d24dbf84f7` | `9fa947cc21` | 2026-07-28 |
| `e53896e02c` | `7795f89fc9` | 2026-07-28 |
| `5bcbadd06a` | `be95bd921c` | 2026-07-28 |
| `947eaab71c` | `ac7e65100d` | 2026-07-28 |
| `62d4bf6630` | `d742c99ba5` | 2026-07-28 |
| `c1c189d281` | `debf980d5f` | 2026-07-28 |
| `f30d2f582b` | `802e09cd9c` | 2026-07-28 |
| `ee91f121b8` | `0f7ff1a896` | 2026-07-28 |
| `082817a0d7` | `2932ffffc3` | 2026-07-28 |
| `52b0de1b5a` | `eabde3747e` | 2026-07-28 |
| `39037d85cc` | `ccf6856f42` | 2026-07-28 |
| `d8acc1e26c` | `13385b68f2` | 2026-07-28 |
| `7ff3fb65e8` | `5f7fd8bc46` | 2026-07-28 |
| `4d8fa5f06c` | `174c58001b` | 2026-07-28 |
| `7972034298` | `2aed6e4932` | 2026-07-28 |
| `f56f8c2479` | `105194d450` | 2026-07-28 |
| `3e38955c12` | `9fa88bda5c` | 2026-07-29 |
| `e9f000f87b` | `23c66c97b8` | 2026-07-29 |
| `8ee937e56d` | `5c3a7b798a` | 2026-07-29 |
| `7f42a456eb` | `7cd177f744` | 2026-07-29 |
| `d31cc19fab` | `5e16719bfe` | 2026-07-29 |
| `893fe32976` | `f58add72ae` | 2026-07-29 |
| `c262d47809` | `bb51047e1d` | 2026-07-29 |
| `de2af6e199` | `e659242990` | 2026-07-29 |
| `7bd67a53a6` | `2e53387650` | 2026-07-29 |
| `db77785b6b` | `9db1ee6f45` | 2026-07-29 |
| `dc856186d1` | `7ac584d27b` | 2026-07-29 |
| `8a3274dd33` | `4ca87a68c7` | 2026-07-29 |
| `32e2c8e556` | `7b19586179` | 2026-07-29 |
| `95ab6db12f` | `5ca8f00d58` | 2026-07-29 |
| `3e147554eb` | `e094c4ab29` | 2026-07-29 |
| `bd25486e56` | `11a079db57` | 2026-07-29 |
| `d54c4aec65` | `2803fb384c` | 2026-07-29 |
| `194d47a0ef` | `100b46ec91` | 2026-07-29 |
| `1b1fa2b719` | `376772078a` | 2026-07-29 |
| `2b7f42dc8c` | `932443df63` | 2026-07-29 |
| `e8ba7589dd` | `5aca738f55` | 2026-07-30 |
| `e58b8db8fe` | `5e46a24e7d` | 2026-07-31 |
| `42d76cc13d` | `b28ef2c97a` | 2026-07-31 |
| `7735c504f2` | `8ad921519f` | 2026-07-31 |
| `b73d88729d` | `6f761da1a4` | 2026-08-01 |
| `d10a9285b9` | `60248bfd92` | 2026-08-01 |
| `93ee6a3549` | `228807fcc8` | 2026-08-01 |
| `6575cb942a` | `3c26cf2504` | 2026-08-01 |
| `640c93480d` | `717638d12c` | 2026-08-01 |
| `c631b0a917` | `44d3e7dae1` | 2026-08-01 |
| `9aa42f96bc` | `11f97b935f` | 2026-08-01 |
| `e963203376` | `af63755f1e` | 2026-08-01 |
| `f030586956` | `dfe7ff9fc0` | 2026-08-01 |
| `94244736c9` | `d4f3fc4c97` | 2026-08-01 |
| `82827c4857` | `018a4b7d83` | 2026-08-01 |
| `ed73a42772` | `8331bb212d` | 2026-08-01 |
| `686f50db8e` | `c8fc91ff1d` | 2026-08-01 |
| `75a319a7e3` | `ba9783fa08` | 2026-08-01 |
| `eff643edf6` | `cdecb006d4` | 2026-08-01 |
| `a2438e1b2b` | `b15a9f578c` | 2026-08-01 |
| `c47b0a3312` | `ac0afaf3d9` | 2026-08-02 |
| `8a9d778973` | `b10b607d70` | 2026-08-02 |
| `4c792172f8` | `bb5778e121` | 2026-08-02 |
| `da6d352902` | `42da25d0a6` | 2026-08-02 |
| `a777f973cb` | `316178bd51` | 2026-08-02 |
| `ee476782bb` | `0a07da260f` | 2026-08-02 |
| `02c60cbbe5` | `52573634df` | 2026-08-02 |
| `0cfbb4a89f` | `42632d6b2a` | 2026-08-02 |
| `7248da62bc` | `c7d4b6f376` | 2026-08-02 |
| `ad3c956cb5` | `b51a07b068` | 2026-08-06 |
| `cc6565e494` | `0213b0fe58` | 2026-08-06 |
| `9e15ba57c5` | `712cd430bf` | 2026-08-06 |
| `9b8145540f` | `0f28acb626` | 2026-08-06 |
| `8cfa7cb83e` | `d7aca3bd0c` | 2026-08-06 |
| `05197c40aa` | `f687b41441` | 2026-08-06 |
| `e5d74662a0` | `c1ee776070` | 2026-08-06 |
| `961641004f` | `7a1710c78d` | 2026-08-06 |
| `e54a1d57e0` | `ebb1d4e2a7` | 2026-08-06 |
| `f0b0019ee7` | `191981aefe` | 2026-08-06 |
| `480e28180c` | `f5fe27336f` | 2026-08-06 |
| `e14d1be69d` | `34442df75c` | 2026-08-06 |
| `24ab87e398` | `e7f1b5d6f8` | 2026-08-06 |
| `c6e72ac9bb` | `f1d732afbe` | 2026-08-06 |
| `6c693c9a8a` | `a81ea0dd2e` | 2026-08-06 |
| `e51142ec5b` | `1b35766ad3` | 2026-08-07 |
| `94596c2933` | `3c6f3645b3` | 2026-08-07 |
| `efb25f31a2` | `f0174decc1` | 2026-08-07 |
## management — 78 Commits
| alt | neu | Datum |
|---|---|---|
| `1dd8f282d8` | `8e98a39447` | 2026-07-30 |
| `d21eabb77f` | `a5eacfe39e` | 2026-07-30 |
| `1c5e21ce43` | `34686ee2e1` | 2026-07-30 |
| `7dc4e5bf8e` | `15396d53e8` | 2026-07-30 |
| `4dd452e0fa` | `49faf20243` | 2026-07-30 |
| `029ee29d9c` | `ff21788692` | 2026-07-30 |
| `838a186c91` | `02946481ae` | 2026-07-30 |
| `480d80e783` | `c3e84c53bf` | 2026-07-30 |
| `09f6e8c333` | `751df7dc01` | 2026-07-30 |
| `57a355ac38` | `a85d082f10` | 2026-07-31 |
| `f6055ee47c` | `2be5fb6f60` | 2026-07-31 |
| `2a2575781d` | `bd8486960f` | 2026-07-31 |
| `50bc39c0d1` | `490c2b7257` | 2026-07-31 |
| `bdb2c766e9` | `0671da1040` | 2026-07-31 |
| `aa6d5fc13a` | `a053fd9271` | 2026-07-31 |
| `1e8ee1c4ac` | `4e03950d3f` | 2026-07-30 |
| `93a0c9cecb` | `98c6483b9f` | 2026-07-31 |
| `142b24d65a` | `019c3e7279` | 2026-07-31 |
| `a9b8ca05ca` | `d9db323b62` | 2026-07-31 |
| `d43bb05d77` | `4c870854f1` | 2026-07-31 |
| `891adc6944` | `59df406a19` | 2026-07-31 |
| `9f78b04419` | `88850809c7` | 2026-07-31 |
| `0b27c26bec` | `3aa34f8635` | 2026-07-31 |
| `590a80f510` | `efbb4b9a1a` | 2026-08-01 |
| `a29235cd54` | `b4b4893389` | 2026-08-01 |
| `375259061f` | `f656c5d8b3` | 2026-08-01 |
| `18c9f1ed20` | `e5c78edb14` | 2026-08-01 |
| `1cbd2001db` | `59025c2e15` | 2026-08-01 |
| `4597ea1cc3` | `f22ede5124` | 2026-08-01 |
| `a23a149c0d` | `ce62396f0b` | 2026-08-01 |
| `cacb89175b` | `666aa9004a` | 2026-08-01 |
| `10f76f2942` | `0b6060339e` | 2026-08-01 |
| `eb9ada6427` | `3e81f40178` | 2026-08-01 |
| `b11284b844` | `777f8c375f` | 2026-08-01 |
| `1e5da7e630` | `77a9a94a7f` | 2026-08-01 |
| `eab452b3d5` | `e4991c050f` | 2026-08-01 |
| `0cd5a33402` | `aecbea0c89` | 2026-08-01 |
| `d1f9f22455` | `0f3f155bc5` | 2026-08-01 |
| `03ddd7b1d8` | `fdf30d42e1` | 2026-08-01 |
| `b96793da98` | `cc0d4d3e02` | 2026-08-01 |
| `b647645d2a` | `3e226fba40` | 2026-08-01 |
| `e8e1b36224` | `ea026a0057` | 2026-08-01 |
| `28cd06c13b` | `fa5c7a8681` | 2026-08-01 |
| `001f59fe44` | `97fe9e0fa8` | 2026-08-01 |
| `4a8e1b6ffd` | `830c740a58` | 2026-08-01 |
| `2b1f791fe6` | `1b8ef35e9b` | 2026-08-01 |
| `04d61ec6ac` | `cd7b2d0b75` | 2026-08-01 |
| `fcfe3b681d` | `7d7c1e86b0` | 2026-08-01 |
| `418d787f77` | `5091ba8f3f` | 2026-08-02 |
| `054fac4383` | `8e489415cd` | 2026-08-02 |
| `5d18924d28` | `8aebe3b03d` | 2026-08-02 |
| `2ec9858a91` | `9c3d2529b4` | 2026-08-02 |
| `404a12d97c` | `c079ec2d7d` | 2026-08-02 |
| `425a2645b6` | `71975d5d41` | 2026-08-02 |
| `aa3f47d4ec` | `0b518bb3b1` | 2026-08-02 |
| `81f207e9c3` | `786ca28895` | 2026-08-02 |
| `d5046621ca` | `b6aaad7abe` | 2026-08-02 |
| `01f0f9ad07` | `d3869e081e` | 2026-08-02 |
| `49f3f38b06` | `92dac0be23` | 2026-08-02 |
| `d967654077` | `e549de8247` | 2026-08-02 |
| `09bdd94da6` | `f3c5603955` | 2026-08-02 |
| `a4df631ee6` | `4f7500c00c` | 2026-08-02 |
| `ac11c274e4` | `83cee828b8` | 2026-08-02 |
| `ae982cdb4b` | `164d96dddd` | 2026-08-02 |
| `290709a900` | `3fe05e7bd1` | 2026-08-02 |
| `161df3a9cc` | `bd83975b2f` | 2026-08-02 |
| `49441bf332` | `cbfbf4b36e` | 2026-08-02 |
| `019c5cb6c9` | `9e0af1370b` | 2026-08-02 |
| `2a294f2261` | `d31fef4b28` | 2026-08-05 |
| `d6c98632f0` | `889cd47be7` | 2026-08-06 |
| `b51986de9f` | `ae62727a50` | 2026-08-06 |
| `5275bef15b` | `d019bfedda` | 2026-08-06 |
| `64670c51b6` | `f1b60d815b` | 2026-08-06 |
| `85aa8ef22d` | `3eb27b3a62` | 2026-08-06 |
| `b4ee92fe9a` | `0abd26e416` | 2026-08-06 |
| `a64ce123c1` | `63fd0b460a` | 2026-08-06 |
| `83150c3d8e` | `9cc5ba4fe0` | 2026-08-06 |
| `8ec67463b1` | `c1a604bd19` | 2026-08-07 |
## ThreadNet-Web — 47 Commits
| alt | neu | Datum |
|---|---|---|
| `5e3254d8a8` | `1822fe80f0` | 2026-07-28 |
| `7839b56a65` | `306cca041c` | 2026-07-28 |
| `05900602c6` | `44305556c3` | 2026-07-29 |
| `6a628533c2` | `42285021cb` | 2026-07-29 |
| `60f2cda962` | `45fb329577` | 2026-07-29 |
| `170d6c3538` | `77ffeb4514` | 2026-07-30 |
| `163504efc6` | `bd8298db39` | 2026-07-30 |
| `b24ce732dc` | `0f1d31021c` | 2026-07-30 |
| `8fb32d9360` | `f2abae85dd` | 2026-07-30 |
| `214fd5b70a` | `8faade6c7b` | 2026-07-30 |
| `6a8a2bb1a9` | `71ac7c4346` | 2026-07-30 |
| `59f9c81ef8` | `8d46fb33d2` | 2026-07-30 |
| `90251e4a55` | `e21e101004` | 2026-07-31 |
| `1354767643` | `bc0769809d` | 2026-07-31 |
| `c52b792511` | `0d0255cb7f` | 2026-07-31 |
| `6a90a12b2c` | `252b16ca71` | 2026-07-31 |
| `a3c7fb3c17` | `8465457a48` | 2026-07-31 |
| `a40a8665ee` | `a414bb8585` | 2026-07-31 |
| `29ffb88cdd` | `9e2f49b3dd` | 2026-07-31 |
| `282540be9f` | `624274f5f0` | 2026-07-31 |
| `a5597f0f8b` | `0d0c1659c6` | 2026-07-31 |
| `574c1bf89c` | `a8767583da` | 2026-07-31 |
| `5eeddc8a0a` | `1eb4968968` | 2026-07-31 |
| `d4ed5e8cc0` | `c401f54e4d` | 2026-07-31 |
| `95d43bcade` | `97e18bbf2f` | 2026-07-31 |
| `a3d51ddf70` | `cf66ff018d` | 2026-07-31 |
| `e7675d7b9f` | `a55adfdd65` | 2026-07-31 |
| `e1f508d12e` | `b656eefdc7` | 2026-07-31 |
| `15fd90655c` | `a2bfaaec8f` | 2026-08-01 |
| `59e1719a10` | `5505b9cfc1` | 2026-08-01 |
| `697e1674c0` | `5a8c42b5e9` | 2026-08-01 |
| `2a2bb3778d` | `6e021ee853` | 2026-08-02 |
| `c8d458734a` | `6b0261d66c` | 2026-08-02 |
| `cc3f860bf8` | `c51b68194a` | 2026-08-02 |
| `00bf0403c7` | `e21a895390` | 2026-08-02 |
| `88f9bdb8d7` | `f4995e5882` | 2026-08-02 |
| `55e1a0949f` | `80fcf6c227` | 2026-08-02 |
| `99a87a2cef` | `4fdad89fa6` | 2026-08-06 |
| `24cacf1000` | `c0774b5f58` | 2026-08-06 |
| `a3965d2eb6` | `6f1dfedde2` | 2026-08-06 |
| `b9358d91f7` | `6f33c7277a` | 2026-08-06 |
| `bfd72cdf3e` | `580827f7ca` | 2026-08-06 |
| `88127d9876` | `1d5a136dff` | 2026-08-06 |
| `977903bdf7` | `f47baa553a` | 2026-08-06 |
| `3c30dd512f` | `9ac56d671b` | 2026-08-06 |
| `ed1c7ff5bc` | `965c456dee` | 2026-08-06 |
| `83d733b9f5` | `26b6fd8938` | 2026-08-07 |
## threadnet-call — 9 Commits
| alt | neu | Datum |
|---|---|---|
| `6260cc4962` | `8fb630cfb3` | 2026-07-31 |
| `c01d55d517` | `0823c8a866` | 2026-08-06 |
| `7fb6251e1c` | `a6e555ac3f` | 2026-08-06 |
| `077dd43af2` | `ac688d36f7` | 2026-08-06 |
| `0be204e6d7` | `3f0f55e6bd` | 2026-08-06 |
| `b265395e6d` | `84419133b3` | 2026-08-06 |
| `760c6af264` | `6f94e6a8a3` | 2026-08-06 |
| `8a38224f51` | `846135e48d` | 2026-08-06 |
| `e8c7dfed36` | `e793902ae0` | 2026-08-07 |
@@ -0,0 +1,47 @@
# Issue-Migration Gitea → GitLab (gitops#48)
`migrate.py` überführt Issues (offen **und** geschlossen, inkl. Kommentare)
eines Gitea-Repos in ein bestehendes GitLab-Projekt. Einmal-Werkzeug für den
#48-Cutover; hier versioniert wegen Reproduzierbarkeit.
## Eigenschaften
- **Dry-Run per Default**, `--execute` schreibt wirklich
- **Idempotent** über Marker `<!-- gitea-migration: OWNER/REPO#N -->` in der
Ziel-Beschreibung — Wiederholungsläufe überspringen Migriertes
- **Zeitstempel bleiben erhalten** (Admin-Token darf `created_at` setzen);
Autorschaft läuft auf den Token-User, Original-Autor+Datum stehen im
Migrations-Fußtext bzw. Kommentar-Präfix (bekannter, akzeptierter Verlust)
- Labels namensgleich (Gruppen-Labels müssen vorher existieren — Stand
2026-08-01 sind die 9 Gitea-Labels + 5 `host:*` als Gruppe-13-Labels angelegt)
- PRs werden ausgefiltert, geschlossene Issues nach Anlage geschlossen
## Aufruf
```
python3 migrate.py sorb/<repo> <gitlab-projekt-id> # Dry-Run
python3 migrate.py sorb/<repo> <gitlab-projekt-id> --execute
```
Tokens: `~/.config/gitea-rohana/token` (read:issue) und
`~/.config/gitlab-lab/token` (Admin) auf dem Mac.
## Stand
| Repo | Ziel | Status |
|---|---|---|
| sorb/thread-net-git | Projekt 18 | ✅ 2026-08-01 (1 Issue, nummerngleich) |
| sorb/threadnet-call | Projekt 19 | ✅ 2026-08-01 (2 Issues, nummerngleich) |
| sorb/ThreadNet-Web | Projekt 16 | ✅ 2026-08-01 (9 Issues, nummerngleich) |
| sorb/axion1337.chat-gitops | Projekt 17 | ✅ 2026-08-01 (50 Issues, **Nummern verschoben**) |
⚠️ **gitops-Nummern sind NICHT deckungsgleich**: Gitea hatte Lücken (PRs zählen
mit), GitLab vergibt lückenlos — z. B. Gitea#48 → GitLab#46, Gitea#51 → GitLab#49,
Gitea#52 → GitLab#50. Die verbindliche Zuordnung steht im Migrations-Fußtext
jedes GitLab-Issues (`Migriert aus Gitea …#N`); alte Commit-/Doku-Verweise auf
„gitops#N" meinen die **Gitea**-Nummer.
**Cutover-Nachschritte** (siehe gitops#48): Gitea-Issues schließen/als migriert
markieren ✅ 2026-08-01, Doku-Verweise umgebogen (CLAUDE.md-Topologieregel) ✅,
Milestones wurden nicht migriert (akzeptierter Verlust, gitops hatte keine
aktiven), Bot-/Token-Workflows (claude-issues → GitLab-Äquivalent) offen.
+126
View File
@@ -0,0 +1,126 @@
# Retro light — 2026-08-09
Erste Retro des Frameworks, angehängt an das Refinement vom selben Tag
([Verfahren](../refinement.md)). Grundlage sind die vier AARs des Monats und 38
im August geschlossene Issues.
---
## 1. Welche Verfahren haben getragen, welche haben gestört?
### Getragen
**„Alles Offene wird ein Issue."** Das ist das Verfahren, das diesen Monat am
meisten eingebracht hat. Sämtliche stillen Fehler unten wurden nur deshalb nicht
vergessen, weil sie im Moment des Findens ein Issue bekamen — auch die, für die
gerade keine Zeit war.
**Die AAR-Pflicht.** Die vier AARs waren die einzige belastbare Vorbereitung für
diese Retro. Ohne sie wäre sie eine Erinnerungsübung geworden.
**Die Board-Pflege-Tabelle** (2026-08-06). Sie hat gehalten: `status:next` und
Meilenstein-Zuordnung blieben sorbs Entscheidung, auch als es unbequem war.
management#15 und #20 lagen drei Tage ohne Spalte — das ist der beabsichtigte
Preis, nicht ein Fehler.
### Gestört
**Der Status-Label-Satz ist in der Oberfläche nicht vollständig ablesbar.**
`status:next`, `status:doing`, `status:wartet` — und „ohne Label = Backlog". Die
vierte Spalte ist damit die einzige, die man nicht *sieht*, sondern erschließen
muss. Genau deshalb hat eine Session am 2026-08-06 ein `status:offen` erfunden und
in vier Projekten angelegt; aufgefallen ist es erst zwei Tage später.
Die Regel bleibt richtig — ein Label für „nichts Besonderes" wäre Rauschen. Aber
der Reiz, es zu erfinden, ist real und wird wiederkommen. **Festgehalten statt
geändert.**
---
## 2. Welche ADRs sind durch die Realität überholt?
**Keine überholt — aber eine Lücke.**
⚠️ **Die Commit-Konventionen und die Anonymisierung der Historie hätten eine ADR
gebraucht.** Am 2026-08-07 wurde eine dauerhafte Prozessregel eingeführt (englische
Conventional Commits, Zeitstempel auf 12:00 UTC) und am 2026-08-09 rückwirkend auf
251 Commits angewandt — eine **irreversible** Änderung an vier Repos, mit
Force-Push durch einen Mirror, von dem Flux liest.
Nach unserer eigenen Regel („ADR-Pflicht bei Architektur-/Prozessentscheidungen")
ist das ein Lehrbuchfall. Stattdessen steht die Regel nur in der `CLAUDE.md` und
die Durchführung in einer Zuordnungstabelle. Nachzuholen als **ADR-0009**.
**Beobachtung zu ADR-0005:** Das Kanban-Framework wurde diese Woche zweimal
erweitert (Titel ohne Priorität, Meilenstein-Pflicht) — beides in der `CLAUDE.md`,
nicht in der ADR. Das ist vertretbar, solange die ADR die *Entscheidung* hält und
die `CLAUDE.md` die *Regel*. Es ist aber genau die Zwei-Orte-Konstruktion, die wir
bei den Titel-Präfixen gerade aufgelöst haben. **Im Auge behalten.**
---
## 3. Fasert etwas aus?
**Nein — aber es gibt ein Muster, und das ist der eigentliche Befund des Monats.**
### Sechs stille Fehler in neun Tagen
| Was | Wie es aussah | Wie es wirklich stand |
|---|---|---|
| `build_embedded` (threadnet-call) | grün, seit jeher | lud **nie** ein Artefakt hoch, falscher Pfad |
| npm-Paket `0.19.2-threadnet.6` | veröffentlicht | 12,5 KB statt 12,8 MB, **ohne `dist/`** |
| Blueprint `matrix-recovery-flow` | Flux grün, ConfigMap aktuell | seit Tagen bei **jedem** Lauf verworfen |
| gitops-Arbeitskopie | „normal" | `main` trackte **Gitea** — ein `git push` wäre in die verbotene Richtung gegangen |
| Leere Pipelines | rot | **nichts kaputt** — der umgekehrte Fall, Rauschen, das rot abtrainiert |
| Release-Pipeline auf `v0.4.0` | lief nach Tag-Push an | hätte ein veröffentlichtes Image überschrieben |
Gefunden wurde **keiner** davon durch eine Überwachung. Vier durch Zufall beim
Suchen nach etwas anderem, zwei durch gezieltes Nachprüfen einer Behauptung.
### Das gemeinsame Merkmal
Alle sechs betreffen Vorgänge, die **erfolgreich aussehen, ohne es zu sein** — oder
die genau umgekehrt Alarm auslösen, wo nichts ist. Der Verbund hat für keinen
dieser Fälle eine Antwort auf die Frage: *Wer merkt es, wenn etwas leise aufhört zu
funktionieren?*
Es gibt Issues für Einzelfälle — gitops#50 (Configs greifen nicht ohne Neustart),
management#28 (Mirror-Ausfall unbemerkt), ThreadNet-Web#14 (Release überschreibbar,
behoben). Was fehlt, ist die Klammer.
⚠️ **Der letzte Fall ist der unangenehmste.** Dass `v0.4.0` nicht überschrieben
wurde, lag daran, dass die geschützten Registry-Variablen in genau diesem Fenster
nicht verfügbar waren — **Glück, nicht Absicht.** Eine Schutzmaßnahme, die
zufällig griff, ist kein Schutz.
### Vorschlag
Eine **Stillstandsprüfung**: ein geplanter Job, der die Invarianten prüft, die wir
diesen Monat einzeln und mühsam gelernt haben — Blueprint-Status ≠ error, Mirror
synchron, Pipeline ohne Jobs, Artefakt vorhanden, Paketgröße plausibel. Kein
weiterer Agent, der Meldungen erzeugt, sondern **eine** Prüfung mit einem Ergebnis.
Das ist die Verallgemeinerung von management#28, das am 2026-08-06 bewusst nach
hinten gestellt wurde. Die Rückstufung war zu dem Zeitpunkt vertretbar; sechs
Fälle später sieht der Einzelfall aus wie ein Symptom. **Zur Entscheidung
vorgelegt, nicht eigenmächtig umgestuft.**
---
## Beschlüsse dieses Refinements
- `status:next`: management#15 und #20 (fällig 31.08.) — Zusage von sorb
- `status:wartet` entfernt bei threadnet-call#4 und ThreadNet-Web#11: der im Issue
benannte Grund war weggefallen
- **M5 — Härtung** angelegt, 14 Issues aus M1 verschoben. Trennlinie: *Ist etwas
Vorhandenes kaputt (M1) oder fehlt etwas, das wir noch nie hatten (M5)?*
Verteilung danach: M1 18 · M2 21 · M3 4 · M4 13 · M5 14
⚠️ Die beiden letzten Punkte sind einer Session **allein** untersagt
([Board-Pflege](../refinement.md)). Sie fanden im Refinement mit sorb statt. Die
Regel ist damit nicht aufgeweicht.
## Offen aus dieser Retro
1. **ADR-0009** zu Commit-Konventionen und Historien-Anonymisierung nachziehen
2. **Stillstandsprüfung** — Entscheidung von sorb
+305
View File
@@ -0,0 +1,305 @@
---
type: wiki-page
area: admin
related: []
---
# CFGMON
Monitoring-Stack, Gitea und der Reverse Proxy für alles Öffentliche.
| | |
|---|---|
| **Hostname** | `CFGMON` |
| **OS** | Ubuntu 24.04.4 LTS |
| **IPv4** | `188.245.193.243` |
| **IPv6** | `2a01:4f8:c17:93eb::1` |
| **Privat** | `10.0.0.3` (`enp7s0`, Hetzner-Netz — dort liegt auch k3s auf `10.0.0.2`) |
| **DNS** | `rohana.axion1337.de` → Gitea, `selendis.axion1337.de` → Grafana |
| **Stand** | 2026-07-30 |
## Dienste
| Container | Image | Compose-Projekt | Definition |
|---|---|---|---|
| prometheus | `prom/prometheus:v3.3.1` | `monitoring` | `sorb/threadnet-operating`, `monitoring/` |
| loki | `grafana/loki:3.7.1` | `monitoring` | dito |
| grafana | `grafana/grafana:12.0.0` | `monitoring` | dito |
| alloy | `grafana/alloy:v1.16.0` | `monitoring` | dito |
| node-exporter | `prom/node-exporter:v1.9.1` | `monitoring` | dito |
| traefik | `traefik:v3.7.9` | `thread-net-git` | `sorb/thread-net-git`, seit 2026-07-30 in `main` (siehe [CFGMON-02](#cfgmon-02--traefik-gitea-cadvisor-und-runner-unter-iac-gebracht--erledigt-2026-07-30)) |
| gitea | `gitea/gitea:1.27.0` | `thread-net-git` | dito, gepinnt (war `:latest`) |
| cadvisor | `gcr.io/cadvisor/cadvisor:v0.49.1` | `thread-net-git` | dito, gepinnt (war `:latest`) |
| runner | `gitea/act_runner:0.6.1` | `thread-net-git` | dito, Container `gitea-runner`, siehe CFGMON-02 |
| portainer_agent | `portainer/agent:2.27.5` | — | standalone, kein Compose |
Prometheus-Jobs: `operating_prometheus`, `operating_node-exporter`,
`operating_cadvisor`, `operating_traefik` (alle lokal), `k3s_host_node`
(`10.0.0.2:9100`), `pterodactyl_host_node` und `gameserver_cadvisor`
(beide `157.90.155.206`, siehe [game](game.md)).
## Offene Punkte → git.lab-Issues
Seit dem Framework-Umbau (2026-08-01) leben offene Punkte als Issues im
[management-Projekt](https://git.lab/axion1337.chat/management/-/issues); die IDs bleiben in den Issue-Titeln erhalten.
Dieses File hält nur noch Bestand und Historie.
- [CFGMON-01 — Zertifikatserneuerung braucht offene Ports (zeitkritisch ab 2026-09-28)](https://git.lab/axion1337.chat/management/-/issues/7)
- [CFGMON-03 — Prometheus-Remote-Write/Loki öffentlich ohne Auth (Weg A, nachgelagerte Prüfung)](https://git.lab/axion1337.chat/management/-/issues/8)
- [CFGMON-04 — Grafana-Admin-Credentials aus `.env` gelten nicht für die API](https://git.lab/axion1337.chat/management/-/issues/9)
- [CFGMON-09 — Gitea-Backups off-host (⚠️ Backup-Cron deaktiviert)](https://git.lab/axion1337.chat/management/-/issues/10)
## CFGMON-11 — Gitea-CI-Rückbau nach GitLab-Umzug
**Status:** erledigt (2026-07-31 spätabends) — bis auf einen kosmetischen Handgriff:
auf CFGMON `cd /opt/thread-net-git && git checkout main && git pull` (Checkout parkt
noch auf dem inhaltsgleichen, inzwischen gelöschten Fix-Branch).
**Dazu neu (2026-08-01 ~05:00):** Auch `/opt/threadnet-operating` braucht einmal
`git fetch && git reset --hard origin/main` — der State-Persistenz-Commit wurde
dort direkt nach Gitea gepusht (dfe04c4a), vom Mirror überschrieben, vom Mac aus
per Patch gerettet und kanonisch als `6ffab68` neu aufgelegt (inhaltsgleich,
anderer Hash; Autorschaft erhalten).
**Erledigt (2026-08-01, autonom):**
- Actions-Toggles deaktiviert: `ThreadNet-Web`, `threadnet-call`, `axion1337.chat-gitops`
- `ThreadNet-Web`: alle `.github/workflows/`-Dateien entfernt (Commit `a876758`)
- gitops: Verifikations-Job nach GitLab portiert + `.gitea/workflows/` entfernt
(Commit `5e46a24`, Pipeline grün, Mirror→Gitea verifiziert; `milestone-release.yml`
war toter Code, siehe #33). Flux unberührt.
- `thread-net-git`: Runner-Service/Config/`.env.example` per Commit `d904734` entfernt
(auf git.lab; Mirror trägt nach Gitea) — **noch nicht deployt**, siehe unten.
- Registry-Entscheidung npm final (Evidenz: `@sorb/threadnet-call-embedded` ist
pnpm-Dependency von `apps/web`, Lockfile pinnt Tarball-URL auf rohana): **bleibt Gitea**.
**Verbleibende manuelle Schritte (User):**
1. ~~`thread-net-git`-Stand deployen~~ **erledigt (2026-07-31 spätabends, via
CFGMON-Session)**: Runner-Container/Netz/`runner-data/`/`.env`-Zeile entfernt,
`builder-1` aus der Gitea-Admin-UI gelöscht, Actions-Registrierungstoken rotiert.
Stolperstein dabei: Rückbau-Commit `d904734` hinterließ ein verwaistes
`networks:`-Fragment (YAML invalide) — Fix nahm den kanonischen Weg
Mac→git.lab→Mirror (`15c8f2d`), Hergang in thread-net-git#1 (geschlossen).
2. ~~Token-Rotation a~~ **geklärt (2026-07-31 abends)**: der versehentlich in die VM
getippte Token (`a89bfb…`) war der Gitea-**Actions-Runner-Registrierungstoken**
(taucht nicht unter Access Tokens auf) — kein Access-Token-Risiko; wird mit
Schritt 1 obsolet. Nach dem Runner-Löschen in der Admin-UI den
Registrierungstoken dort einmal neu generieren (ein Klick), dann ist auch der
exponierte Wert wertlos.
3. ~~Token-Rotation b~~ **erledigt (2026-07-31 abends)**: Generalschlüssel
(`Projectplaning and implemantation axion`, war npm-Publish UND Issue-Verwaltung,
Klartext in `.npmrc` + Scratchpad + macOS-Schlüsselbund) gelöscht und durch drei
least-privilege-Tokens ersetzt: `gitlab-ci-npm` (write:package, als maskierte
GitLab-Gruppen-Variable `GITEA_NPM_TOKEN`), `claude-issues` (write:issue,
`~/.config/gitea-rohana/token` auf dem Mac), `claude-push` (write:repository,
`~/.config/gitea-rohana/push-token`). Erster CI-Publish `0.19.2-threadnet.6`
verifiziert → threadnet-call#1 geschlossen. Alle Klartext-Reste entfernt
(.npmrc, Skript, Schlüsselbund-Eintrag). Zusätzlich aufgeräumt: ungenutzte
Alt-Tokens `claude-push-20260730`, `claude-monitoring-rework-20260730` gelöscht.
Entscheidung vom 2026-07-30: Build-CI/CD zieht ins Homelab-GitLab
(`git.lab/axion1337.chat`, Gruppe mit importierten Projekten angelegt; die Domain ist
**nur im Homelab auflösbar** — von CFGMON/MATRIX aus nicht erreichbar, was für den
Build-Anwendungsfall in Ordnung ist: Prod läuft bei Lab-Ausfall weiter, nur neue Builds
pausieren). Der am 2026-07-30 auf Gitea-Seite aufgebaute CI-Unterbau wird damit teilweise
überflüssig. **Erst zurückbauen, wenn die GitLab-CI nachweislich läuft.**
### Sicher rückbaubar
- **Actions-Toggle** `has_actions` bei `ThreadNet-Web` (am 2026-07-30 per API aktiviert)
wieder deaktivieren, ebenso bei `threadnet-call` (stoppt die fehlschlagende
Workflow-Kaskade dort).
- **`.github/workflows/` in `ThreadNet-Web`** (der kuratierte 6-Dateien-Satz) — wird durch
`.gitlab-ci.yml` ersetzt. Die Erkenntnisse aus den Läufen vom 2026-07-30 mitnehmen:
kein `layered.sh` (würde den js-sdk-Pin mit Upstream-develop überschreiben, braucht
außerdem jq), stattdessen `pnpm install --frozen-lockfile`; webpack-Build braucht
real ~4 GB Heap; `contains(needs.*.result, ...)`-Gates sind act-spezifisch kaputt.
- **Geerbte Upstream-Workflows in `threadnet-call`** (build/publish/test/translations/
zizmor/pr-deploy) — gleiches Schicksal.
- **Runner-Identität**: wenn der Runner-Service entfällt (siehe unten), `builder-1` in
der Gitea-Admin-UI deregistrieren und `runner-data/.runner` auf dem Host entfernen.
- **Token: npm-Token in `threadnet-call`s untracked `embedded/web/.npmrc`** (Klartext im
Arbeitsverzeichnis, Fund vom 2026-07-30): revoken/rotieren. Der GitLab-CI-Publish
bekommt einen eigenen, frisch erzeugten Token als GitLab-CI-Variable — der alte
verschwindet von der Platte.
### Entscheidungsabhängig
- **Runner-Service in `thread-net-git` ganz entfernen?** Hängt daran, ob das gitops-Repo
seinen leichten `deploy-on-push.yml` (YAML-Validierung/Notification, läuft sauber)
behält — dann bleibt ein Minimal-Runner nötig. Bei Komplett-Entfernung als
Revert-Commit in `thread-net-git`: Compose-Service `runner`, `runner/config.yaml`,
`.env.example` (RUNNER_TOKEN), Cache-Port-Bindung 8088, `runner-data/`.
- **Registry-Ziel für `@sorb/threadnet-call-embedded`**: bleibt die Gitea-npm-Registry
(dann braucht GitLab-CI einen Push-Token dorthin — Neuanlage) oder wandert in die
GitLab-Package-Registry (dann läuft die Gitea-Package-Seite leer).
- **Container-Images bleiben in der rohana-Registry** (Flux/k8s pullt von dort — spricht
stark für Beibehalt). Gegenstück zur Token-Bilanz: GitLab-CI braucht dann einen
**neuen** Deploy-/Push-Token für die rohana-Registry (Neuanlage, kein Rückbau).
### Explizit nicht rückbaubar
Gitea selbst, gitops-Repo als Flux-Source, Issues/Wiki/dieses Repo, der
API-Token für Issue-Verwaltung, das Gitea-Backup-Script (CFGMON-09).
*(Stand der Analyse 2026-07-31. Issues und dieses Repo sind seitdem doch
umgezogen — [ADR-0002](../../adr/0002-issues-und-management-ins-lab.md) —,
das Repo dabei von `Backlogs` zu `management` umgewidmet
[ADR-0005](../../adr/0005-pm-framework-kanban.md). „Nicht rückbaubar" galt für
den damaligen Rückbau der Gitea-CI, nicht auf Dauer.)*
Betroffene Issues (werden bei der GitLab-Migrations-Planung umformuliert):
`ThreadNet-Web#2` (Gitea-Zählung, Tracker stillgelegt — verbindlich: Migrations-Fußtext im GitLab-Issue),
`threadnet-call#1` (Gitea-Zählung, Tracker stillgelegt).
**Nächster Schritt:** die drei manuellen Schritte oben, dann → erledigt.
## CFGMON-13 — Absender-Design für Release-/CVE-Meldungen: eigener Bot?
**Status:** entschieden (2026-08-01, sorb) — **gleicher Bot (`@alerts`), eigener Raum**
`!YRJvcEbVXtRlUIkNld:axion1337.chat`. Umsetzungsplan inkl. CVE-Metriken/Grafana/
Alertmanager-Routing: [gitops#45 auf git.lab](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/45) (ehemals Gitea-gitops#47, Tracker stillgelegt).
release-watch ist bereits auf den Raum vorbereitet (Env `MATRIX_RELEASE_ROOM_ID`,
Fallback Alerts-Raum). ⬜ Rest: `@alerts` in den Raum **einladen** (Join wurde als
restricted abgelehnt — sorb), dann Deploy.
Zwei neue Meldequellen entstehen gerade neben dem klassischen Alerting:
1. **release-watch** (gitops#22, deploybereit): Upstream-Releases/Security-Releases
→ aktuell als Notiz über den `@alerts`-Bot in den Alerts-Raum
2. **Trivy-CVE-Scans** (gitops#31, läuft wöchentlich in der Lab-CI): Funde landen
bisher NUR als Job-Artifact/-Log — keine aktive Benachrichtigung
**Frage:** Sollen diese "Informations-Meldungen" (Releases, CVE-Reports) einen
**eigenen Bot** bekommen (z. B. `@releases:axion1337.chat`, ggf. eigener Raum),
damit `@alerts` ausschließlich für echte Betriebsalarme steht und separat
scharf/stumm schaltbar bleibt? Oder bewusst alles über `@alerts` bündeln?
Bei Entscheidung "eigener Bot": Anlage per mas-cli wie gehabt, release-watch-Env
umziehen, Trivy-Anbindung (CI-Job → Matrix-Notiz bei Funden) gleich mit auf den
neuen Absender bauen.
## CFGMON-12 — Gitea-Projektmetadaten nach GitLab umziehen/integrieren
**Status:** abgelöst durch [gitops#46 auf git.lab](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/46) (ehemals Gitea-gitops#48, Tracker stillgelegt) (2026-08-01, sorb: HOHE Priorität — vollständige Issue-Migration + zentrale Gruppen-Roadmap; Plan-Skizze und die offene Erreichbarkeits-Entscheidung git.lab-only vs. extern stehen dort)
**Umgesetzt am 2026-08-01/02**: Die Migration ist durch — 62 Issues liegen auf
git.lab, die Gitea-Issues sind geschlossen und tragen einen Migrations-Fußtext.
Alles darunter ist der **Stand vor dem Umzug** und bleibt als Historie stehen;
die Aufzählung „Noch auf Gitea" gilt nicht mehr. Die zunächst verbliebene Ausnahme
für Deploy-Übergabe-Issues ist am 2026-08-02 mit LABNET-03 ebenfalls zurückgebaut.
---
Beim CI/CD-Umzug (2026-07-31) ist nur der **Code** nach git.lab gewandert; alle
Projektmetadaten liegen weiterhin auf Gitea/rohana. Verifiziert per API am
2026-07-31: alle 20 git.lab-Projekte melden `open_issues=0`.
Noch auf Gitea:
- **Issues** inkl. Kommentare/Labels: ThreadNet-Web (#2, #5, …), threadnet-call (#1),
gitops (#24, #25, #32, …)
- **Meilensteine** (u. a. die Release-Meilensteine im gitops-Repo)
- **Wiki** (gitops-Wiki mit `00-TASKS.md`-Log — bisher bewusst direkt-Gitea)
- **Releases/Packages** (npm-Registry bleibt laut Lockfile-Entscheidung in
[CFGMON-11](#cfgmon-11--gitea-ci-rückbau-nach-gitlab-umzug) auf rohana — bei
diesem Punkt prüfen, ob das so bleibt)
Vor Umsetzung zu klären:
1. **GitLab-Gitea-Importer vs. API-Skript** — der Importer verliert Autorenschaft
(alles läuft unter dem Import-User) und Issue-Nummern können sich verschieben
→ Commit-/Kommentar-Referenzen prüfen.
2. **Erreichbarkeit**: rohana ist von überall erreichbar, git.lab nur im Homelab —
das gilt dann für alle Issues und muss bewusst entschieden werden. Gleiches
Argument betrifft dieses Repo selbst (damals noch `Backlogs`, bewusst
direkt-Gitea).
3. **Flux-relevante Teile**: ob die gitops-Wiki-Konventionen mitgehen.
Nach dem Umzug gehört ein Hinweis in die Topologie-Abschnitte (gitops
README/CLAUDE.md, Fork-Docs) — dort steht aktuell „Issues/Wiki/Releases bleiben
auf Gitea" als geltende Regel.
---
## Erledigt
### CFGMON-10 — threadnet-call-CI schlägt am Artifact-Schritt fehl · verworfen 2026-07-30
Ausgelöst durch einen Push nach `threadnet-call` am 2026-07-30: der Runner (`builder-1`)
verarbeitete mehrere geerbte Upstream-Workflows, die meisten scheiterten am
Artifact-Upload/-Download-Schritt. Ursprünglich unverifizierte Hypothese: das
Job-Container-Limit (2,2 GiB / 1,5 CPU) ist zu knapp.
**Hypothese inzwischen im Kern bestätigt** — beim parallelen ThreadNet-Web-CI-Versuch
starb der webpack-Build bei 92 % mit `FATAL ERROR: ... JavaScript heap out of memory`
(Job 3442, 2026-07-30): diese Build-Klasse braucht real ~4 GB, der Host (3,7 GiB gesamt,
ohne Swap, trägt daneben Gitea/Traefik/Monitoring) kann das strukturell nicht liefern.
**Verworfen statt gefixt**: Limit-Anhebung/Swap wird bewusst nicht weiterverfolgt —
Build-CI zieht ins Homelab-GitLab um (siehe
[CFGMON-11](#cfgmon-11--gitea-ci-rückbau-nach-gitlab-umzug)), CFGMON bleibt bei leichten
Jobs. Issue-Seite: `threadnet-call#1` (Gitea-Zählung, Tracker stillgelegt).
### CFGMON-02 — Traefik, Gitea, cAdvisor und Runner unter IaC gebracht · erledigt 2026-07-30
Liefen ursprünglich im Compose-Projekt `thread-net-git` aus `/data/compose/8`, einem von
Portainer verwalteten Stack ohne Repo dazu. Jetzt in `sorb/thread-net-git`: `:latest`-Tags
gepinnt (Gitea `1.27.0`, cAdvisor `v0.49.1`), Projektname `thread-net-git` beibehalten
(Volume-Kontinuität), README mit Betriebsregeln ("nie wieder über Portainer anfassen",
Volume-Namen, Downgrade-Verbot für Gitea), nächtliches Backup-Script. Zusätzlich neu: ein
`runner`-Service (`gitea/act_runner:0.6.1`, Container `gitea-runner`, Labels
`ubuntu-latest`/`linux-build`/`win-wine` — die letzten beiden gezielt für Electron-Builds)
— ursprünglich unter [CFGMON-08](#cfgmon-08) als offene Frage gelistet, siehe dort.
Entstanden auf Branch `rework/stack`, zunächst nicht gemergt (produktiv aber schon aktiv).
**2026-07-30 nach `main` gemergt** (`origin/main` == `origin/rework/stack` auf `02b3224`,
verifiziert) — damit spiegelt die Standardansicht des Repos jetzt den Live-Stand.
Verifiziert am 2026-07-30 über die Compose-Labels der laufenden Container
(`working_dir: /opt/thread-net-git`) und `docker compose ls`. `gitea-data` ist als
external Volume deklariert — ein Deploy mit falschem Projektnamen schlägt laut fehl,
statt leise ein leeres Volume anzulegen.
Zum Bootstrapping-Problem (Definition von Gitea liegt in Gitea): mitigiert,
weil das Deploy-Verzeichnis selbst der Checkout ist — fällt Gitea aus, liegt
die Definition weiterhin lokal auf dem Host. Gegen Verlust des ganzen Hosts
hilft nur die Off-Host-Kopie, siehe
[CFGMON-09](#cfgmon-09--gitea-backups-off-host-in-die-storage-box-eigenes-borg-repo).
### CFGMON-05 — Monitoring-Stack unter IaC bringen · erledigt 2026-07-30
Der Stack lief aus `/opt/monitoring` ohne Versionierung und mit `:latest`-Tags. Jetzt
in `sorb/threadnet-operating` unter `monitoring/`, Images gepinnt,
Grafana-Datasources und 9 Dashboards provisioniert. Projektname `monitoring`
beibehalten, dadurch blieben die Volumes erhalten.
### CFGMON-06 — Grafana-Certresolver zeigte ins Leere · erledigt 2026-07-30
Das Label sagte `certresolver=le`, Traefik kennt den Resolver aber als
`letsencrypt`. Traefik protokollierte `Router uses a nonexistent certificate
resolver` und lieferte für `selendis.axion1337.de` sein Default-Self-Signed-Cert
aus. Aus dem Altbestand in `/opt/monitoring` unverändert übernommen und dort
mindestens seit dem 2026-07-27 vorhanden.
Behoben in `threadnet-operating`, Commit `a400f8a`. Cert von Let's Encrypt (YR2)
ausgestellt, gültig bis 2026-10-28 — die Nachfolge davon ist
[CFGMON-01](#cfgmon-01--zertifikatserneuerung-braucht-offene-ports-ipv4-und-ipv6).
### CFGMON-07 — Alloy verlor seine Positions-Datei bei jedem Deploy · erledigt 2026-07-30
`--storage.path=/var/lib/alloy/data` war gesetzt, aber ohne Volume: die
Positions-Datei lag im Container-Layer. Nach jedem Recreate las Alloy alle
Docker-Logdateien von vorn, worauf Loki alles älter als 7 Tage mit HTTP 400 abwies
(`timestamp too old`, `reject_old_samples`). Betroffen waren nur Alt-Logzeilen bis
zurück zu 2025, keine aktuellen Daten.
Behoben durch ein `alloy_data`-Volume, Commit `edac97e`. Verifiziert: Positions
überleben `--force-recreate`, zweiter Recreate erzeugt 0 Fehler.
### CFGMON-08 — Kein Gitea-Actions-Runner registriert, Standort noch offen · erledigt 2026-07-30
**Korrektur einer falschen Prämisse**: der Eintrag ging davon aus, dass gar kein Runner
existiert und wo einer laufen sollte, noch offen sei. Beides falsch — ein Runner
(`builder-1`) läuft bereits, auf CFGMON, als Teil von `thread-net-git`s `rework/stack`-
Branch, mit gezielt für Electron-Builds eingerichteten Labels. Details siehe
[CFGMON-02](#cfgmon-02--traefik-gitea-cadvisor-und-runner-unter-iac-gebracht--erledigt-2026-07-30) — hier
nicht dupliziert. `gitops#33` (Gitea-Zählung, Tracker stillgelegt)
(dieselbe falsche Prämisse) entsprechend korrigiert/geschlossen.
+91
View File
@@ -0,0 +1,91 @@
---
type: wiki-page
area: admin
related: []
---
# game
Pterodactyl- / Gameserver-Host.
| | |
|---|---|
| **IPv4** | `157.90.155.206` |
| **IPv6** | kein AAAA-Record |
| **DNS** | `game.axion1337.de` |
| **Privat** | `10.0.0.4` (im vSwitch seit 2026-08-02) |
| **Stand** | 2026-08-02 |
> **Teil-inventarisiert (2026-08-02).** Die Compose-Definitionen sind bekannt, der
> Host selbst wurde weiterhin nicht betreten — OS-Stand, Plattenbelegung und
> Docker-Version fehlen. Beim ersten direkten Zugriff nachtragen.
## Was darauf läuft
Zwei über **Coolify** deployte Stacks, abgebildet in
[axion1337.chat/game-operating](https://git.lab/axion1337.chat/game-operating)
(angelegt 2026-08-02).
⚠️ Das Repo ist ein **Abbild, keine Quelle**: Verwaltet wird weiter in Coolify,
Änderungen müssen dort *und* im Repo passieren. Der Umstieg auf git-basiertes
Deployment ist gewollt, aber bewusst **zurückgestellt, bis das Matrix-Projekt
abgeschlossen ist** (sorb, 2026-08-02). Bis dahin fehlen dem Abbild noch die
mitgemounteten Konfigdateien (`entrypoint.sh`, wings-Config, die drei
Monitoring-Konfigs) und die Volume-Deklarationen — **aus dem Repo allein ließe
sich der Host derzeit nicht wiederherstellen.**
**Pterodactyl** (Gameserver-Verwaltung, in Benutzung durch Bekannte des Betreibers
— Ausfälle und Datenverlust sind hier real spürbar):
| Dienst | Image |
|---|---|
| `pterodactyl` (Panel) | `ghcr.io/pterodactyl/panel:v1.12.0` |
| `wings` (Daemon, fährt die Gameserver als Docker-Container) | `ghcr.io/pterodactyl/wings:v1.12.0` |
| `mariadb` | `mariadb:11.8` |
| `redis` | `redis:alpine` |
**Eigener Monitoring-Stack** (grafana-oss, prometheus v3.0.0 mit 15 d Retention,
loki 3.1.1, promtail 3.1.1, node-exporter v1.8.1, cadvisor v0.49.2). Wird
perspektivisch von CFGMON abgelöst — siehe unten.
⚠️ **Kein einziger `ports:`-Block in beiden Stacks.** Alles hängt an Coolifys
Docker-Netz und ist nur containerintern erreichbar. Genau das war die Ursache von
GAME-01: Auf 9100/8080 des Hosts lauscht nichts, CFGMONs Scrape-Ziele auf der
öffentlichen IP konnten nie funktionieren.
## Erreichbarkeit von außen (gemessen)
Vor dem Host liegt eine **quellbewusste Firewall** — dieselben Ports verhalten sich
je nach Quelle unterschiedlich:
| Port | von CFGMON (`188.245.193.243`, 2026-08-01) | vom Hausanschluss (`178.25.213.70`, 2026-08-02) |
|---|---|---|
| 80 / 443 | offen | offen (HTTP 404 bzw. 503) |
| **22** | **Timeout** | **offen** |
| 8080 / 9100 (Exporter) | Timeout | Timeout |
| ICMP | 100 % Verlust | 100 % Verlust |
Daraus folgt zweierlei: Es gibt bereits eine **SSH-Freigabe für den Hausanschluss**,
und CFGMON ist **nicht pauschal gesperrt** (sonst wären auch 80/443 von dort tot) —
es ist eine portbezogene Regel mit Quellliste. Die Exporter-Ports 8080/9100 sind
dagegen **für niemanden** freigegeben, auch nicht für den Hausanschluss.
Es fehlte also keine Ausnahme für CFGMON. Seit 2026-08-02 liegt der Host im
vSwitch (`10.0.0.4`); die Monitoring-Anbindung läuft künftig **per Push über das
private Netz** — Alloy sammelt lokal ein und schiebt nach `10.0.0.3`, wodurch der
Host **keinen einzigen eingehenden Port** braucht. Dasselbe Muster wie beim
k3s-Cluster. Details: [GAME-01](https://git.lab/axion1337.chat/management/-/issues/2).
Die Umstellung erfolgt **additiv**: Der lokale Monitoring-Stack läuft weiter, bis
auf CFGMON über Wochen belastbar Daten liegen. Auf diesem Host wird nichts
abgeräumt, solange nicht klar ist, dass nichts fehlt.
## Offene Punkte → git.lab-Issues
Seit dem Framework-Umbau (2026-08-01) leben offene Punkte als Issues im
[management-Projekt](https://git.lab/axion1337.chat/management/-/issues); die IDs bleiben in den Issue-Titeln erhalten.
Dieses File hält nur noch Bestand und Historie.
- [GAME-01 — Host von CFGMON aus nicht erreichbar, 2 Targets down (⚠️ Silences bis 2026-08-04)](https://git.lab/axion1337.chat/management/-/issues/2)
- [GAME-02 — `www.game.axion1337.de` ist überflüssig](https://git.lab/axion1337.chat/management/-/issues/3)
+217
View File
@@ -0,0 +1,217 @@
---
type: wiki-page
area: admin
related: []
---
# matrix
Matrix-Homeserver (Element Server Suite / Synapse) + K3s-Single-Node-Cluster, GitOps-verwaltet.
| | |
|---|---|
| **Hostname** | `MATRIX` |
| **IPv4** | `49.13.132.245` |
| **IPv6** | kein AAAA-Record |
| **Privat** | `10.0.0.2` (`enp7s0`, dasselbe Hetzner-Netz wie CFGMON `10.0.0.3`) |
| **OS** | Debian 13 (trixie) |
| **DNS** | `matrix.axion1337.de` **und** `matrix.axion1337.chat` zeigen auf dieselbe IP — ebenso `axion1337.chat` (Apex) und `account.axion1337.chat` (MAS). `axion1337.de` ist die ältere/Registrar-Domain (IONOS-Mail läuft dort), `axion1337.chat` die eigentliche Matrix-Service-Domain. |
| **Stand** | 2026-07-30 |
**Inventarisiert** (direkter SSH-Zugriff, `~/.ssh/config`-Alias `axion1337`, Port 2248):
K3s + FluxCD + Element Server Suite (Synapse, MAS, Element Web, MatrixRTC), Authentik (OIDC),
Traefik, Cert-Manager, coturn, Draupnir, ClamAV, NetworkPolicies (Default-Deny). IaC-Repo:
[`sorb/axion1337.chat-gitops`](https://rohana.axion1337.de/sorb/axion1337.chat-gitops) - dieser
Host **ist** das Deployment-Ziel dieses Repos, nicht nur verwandt. Client-Forks:
`sorb/ThreadNet-Web` (Element Web), `sorb/threadnet-call` (Element Call/LiveKit-Widget).
`sorb/element-web` und `sorb/ThreadNet-Stack` sind **veraltete/abgelöste** Vorgänger-Repos
(letzte Aktivität 2026-05-11 bzw. 2025-10-24) - nicht mehr das, was hier läuft.
`ufw`: aktiv, Default Deny Incoming / Allow Outgoing, explizite Allow-Regeln für
2248/tcp (SSH), 80/443, TURN/RTC-Ports. `unattended-upgrades` aktiv (Debian-Security +
Debian-Origin), siehe [MATRIX-04](#matrix-04--host-level-pre-update-benachrichtigung-erledigt).
## Offene Punkte → git.lab-Issues
Seit dem Framework-Umbau (2026-08-01) leben offene Punkte als Issues im
[management-Projekt](https://git.lab/axion1337.chat/management/-/issues); die IDs bleiben in den Issue-Titeln erhalten.
Dieses File hält nur noch Bestand und Historie.
- [MATRIX-03 — `www.matrix.axion1337.de` ist überflüssig](https://git.lab/axion1337.chat/management/-/issues/1)
## MATRIX-05 — node-exporter-DaemonSet in CrashLoopBackOff, Cluster-Scrape seit 2026-08-01 tot
**Status:** erledigt (2026-08-01 ~04:10, vom Mac aus mit kubectl/SSH)
**Auflösung:** Beide Teile hatten dieselbe Wurzel — ein **doppelter node-exporter**.
Teil 1 bestätigt per Pod-Log: `listen tcp 0.0.0.0:9100: bind: address already in use`;
den Port hält der systemd-Dienst `prometheus-node-exporter` (der etablierte, den CFGMON
via `10.0.0.2:9100` scrapt). Teil 2 erklärt: der Cluster-Service "funktionierte" nur in
den Ready-Momenten des Crashloop-Pods, dessen hostNetwork-Endpoint (= Host-IP) zufällig
auf den gesunden systemd-Exporter zeigte; seit der Endpoint dauerhaft NotReady war,
setzte kube-proxy `no endpoints → REJECT`. ufw-Regeln seit April unverändert — kein
Firewall-Drift; extern war 9100 nie freigegeben (und soll es nicht sein).
**Fix (gitops `228807f`, Weg A aus gitops#45):** HelmRelease + Alloy-Scrape entfernt,
Flux hat gepruned — DaemonSet/Service/Pod sind weg, Host-Metriken kommen unverändert
vom systemd-Exporter. Volle Diagnose:
`gitops#45` (Gitea-Zählung, Tracker stillgelegt — verbindlich: Migrations-Fußtext im GitLab-Issue).
<details><summary>Ursprünglicher Befund (CFGMON-Session, vor der Host-Prüfung)</summary>
Von CFGMON aus diagnostiziert, per kube-state-metrics und den remote-geschriebenen Serien.
**Auf dem Host selbst wurde nichts geprüft** — CFGMON hat kein `kubectl`.
### Teil 1 — der Pod crasht seit Monaten (chronisch)
| Messwert | Stand 2026-08-01 |
|---|---|
| Pod `prometheus-node-exporter-4wwn7`, Restarts | **4880**, davon 1970 in 7 Tagen |
| Vorgängerpod `…-fv7hj`, Zählerstand vor 30 Tagen | 13918 |
| aktuelle Rate | ~12/h — entspricht dem CrashLoopBackOff-Deckel von 5 min |
| `last_terminated_reason` | `Error` (Exit ≠ 0, **kein** OOMKill) |
| `waiting_reason` / `ready` | `CrashLoopBackOff` / `0` |
Das besteht mindestens seit 30 Tagen, also **lange vor** dem Monitoring-Rework und vor dem
Alerting-Rollout. Neu ist nur die Sichtbarkeit: die Regel `KubePodRestartLoop` kam am
2026-08-01 dazu. Wie bei Synapse hat das Alerting einen stillen Altfehler aufgedeckt.
**Vermutete Ursache, nicht verifiziert:** Der Pod läuft mit `hostNetwork: true` und will
Port 9100 auf dem Host binden. Dort hört bereits ein **eigenständiger node-exporter**
derselbe, den CFGMON als Job `k3s_host_node` direkt auf `10.0.0.2:9100` scrapt und der
2706 Metriken sauber ausliefert. Zwei Exporter, ein Port; der Pod bekäme
`address already in use` und beendete sich sofort, was zu `reason: Error` ohne OOM passt.
Zum Bestätigen auf dem Host:
```
ss -lntp | grep :9100
kubectl -n monitoring logs prometheus-node-exporter-4wwn7 --previous | tail -20
```
### Teil 2 — der Cluster-Scrape ist am 2026-08-01 01:19 UTC ausgefallen (akut)
| Messwert | Stand |
|---|---|
| `up`-Mittel 24 h für `prometheus-node-exporter.monitoring.svc.cluster.local:9100` | 98,3 % |
| Zustandswechsel in 24 h | **1** — einmal runter, nicht zurück |
| Host-Uptime | 77,6 Tage → **kein Reboot** |
Ein Pod, der 12× pro Stunde stirbt, kann kein Target sein, das zu 98 % up ist. Geantwortet
hat also nie der Pod, sondern der eigenständige Exporter über die Host-IP. Der Pod hat
`hostNetwork`, sein Pod-IP ist die öffentliche `49.13.132.245`, dorthin zeigt der
Service-Endpoint — und dort kommt seit 01:19 nichts mehr.
Von CFGMON aus gemessen:
| Pfad | Ergebnis |
|---|---|
| `10.0.0.2:9100` (privat) | offen, 2706 Metriken |
| `49.13.132.245:9100` (öffentlich) | **keine Antwort** |
| `49.13.132.245:80` / `:443` | offen — Host lebt |
Ohne Reboot heißt das: in der Nacht wurde die Erreichbarkeit auf 9100 eingeengt. Zwei
Möglichkeiten, von hier aus nicht unterscheidbar:
1. Der Exporter bindet jetzt `10.0.0.2:9100` statt `0.0.0.0:9100`.
2. Eine `ufw`-Regel wurde geändert. Die Allow-Liste oben in dieser Datei führt 9100
ohnehin nicht auf — die private Zustellung muss also über eine Interface- oder
Subnetz-Regel laufen, an der sich etwas geändert haben kann.
Zeitlich fällt das exakt in das Fenster der Synapse-Port-Korrektur derselben Nacht. Wer
dort die Exposition aufgeräumt hat, hat den Cluster-Scrape-Pfad mitgenommen. Prüfen mit
`ss -lntp | grep :9100` und `ufw status numbered`.
### Fix — es ist ein node-exporter zu viel
**Weg B (empfohlen):** Den DaemonSet-Exporter abschalten (`nodeExporter.enabled: false` in
den kube-prometheus-stack-Values) und das Cluster-Alloy statt auf den Service-Namen direkt
auf `10.0.0.2:9100` zeigen lassen. Beendet den Crashloop und erhält die enge Bindung ans
private Netz, die in der Nacht vom 2026-08-01 gesetzt wurde.
**Weg A:** Den eigenständigen Exporter stilllegen und dem DaemonSet den Port überlassen.
Ebenfalls sauber, aber er bindet dann wieder `0.0.0.0` — also auch die öffentliche IP,
abgesichert nur noch durch `ufw` und die Cloud-Firewall. Das nähme die Einschränkung
zurück, die gerade erst gesetzt wurde.
### Nebenbefund — Job-Label kollidiert zwischen zwei Hosts
Das Label `prometheus.scrape.node_exporter` existiert zweimal, weil CFGMONs Alloy und das
Cluster-Alloy ihre Scrape-Komponente gleich benennen:
```
up=1 instance=node-exporter:9100 -> CFGMON (Kernel 6.8.0-136-generic)
up=0 instance=prometheus-node-exporter.monitoring.svc...:9100 -> MATRIX
```
Die Serien kollidieren nicht, `instance` trennt sie. Aber jede Abfrage, die nur nach `job`
filtert, mischt zwei Maschinen — und in Alarmtexten steht dann ein Job-Name, der nicht sagt,
welcher Host gemeint ist. Ein External Label auf der Remote-Write-Seite dieses Clusters
(`cluster="matrix"`) würde das sauber trennen.
---
</details>
## Erledigt
### MATRIX-01 — Klären, ob der Server Mail als `@matrix.axion1337.de` verschickt · erledigt 2026-07-30
Für `matrix.axion1337.de` existiert der komplette IONOS-Mail-Satz: `MX mx00/mx01`,
`TXT "v=spf1 include:_spf-eu.ionos.com ~all"`, `CNAME s1-ionos._domainkey` und
`CNAME autodiscover`. Bei `selendis` ist dasselbe Muster reine Altlast; hier war die Frage
offen, weil Matrix-Homeserver typischerweise Mail für Registrierung/Passwort-Reset
verschicken.
**Antwort, verifiziert per Config** (nicht nur vermutet) — direkt im IaC-Repo
`sorb/axion1337.chat-gitops`, dem tatsächlich hier deployten Stand geprüft:
- `apps/production/custom-configs/synapse-values.yaml` — kein `email:`/`smtp_host`/
`notif_from`-Block.
- `apps/production/custom-configs/mas-secret.yaml` (SOPS-entschlüsselt geprüft) — kein
`email`/`smtp`/`mailer`-Eintrag.
- `apps/production/element-server-suite.yaml` (HelmRelease values) — dito, nichts.
Weder Synapse noch MAS versenden aktuell irgendeine Mail. Registrierung/Passwort-Reset
laufen ausschließlich über Authentik (OIDC, `auth.axion1337.chat`) und Einladungslinks.
Der komplette IONOS-Mail-Satz auf `matrix.axion1337.de` ist damit **funktional unnötig**
dieselbe Härtung wie bei `selendis` anwenden (Null-MX, `v=spf1 -all`, `_dmarc p=reject`),
`autodiscover.matrix` kann ebenfalls weg. Damit ist auch
[ZONE-02](../architecture/zone-axion1337.md) an dieser Stelle entblockt.
**Separat davon** (andere Domain-Ebene, kein Widerspruch): auf diesem Host läuft seit
2026-07-30 ein eigener Mailversand für Host-Wartungsbenachrichtigungen
(`wartung@axion1337.de`, **Apex**-Postfach, nicht die `matrix.`-Subdomain) — siehe
MATRIX-04 unten. Nutzt die ohnehin am Apex laufende echte IONOS-Mail-Infrastruktur,
betrifft die `matrix.`-Subdomain-Records oben also nicht.
### MATRIX-02 — Pusht per Remote-Write auf einen offenen Prometheus · erledigt 2026-07-30
**Korrektur einer falschen Annahme im ursprünglichen Eintrag**: der Text ging von zwei
getrennten Absendern aus - "CFGMON (`10.0.0.3`) und der k3s-Host (`10.0.0.2`)" - als wären
das zwei verschiedene Maschinen. Es ist **dieselbe Maschine**: dieser Host (`matrix`) hat
selbst die private IP `10.0.0.2` (verifiziert per `ip -4 addr show` auf dem Host).
Verifiziert in `apps/monitoring/alloy-config.yaml` (diesem Cluster): Der Remote-Write-Push
geht bereits an `http://10.0.0.3:9090/api/v1/write` und Loki an `http://10.0.0.3:3100/...` -
**private IP, nicht die öffentliche** `188.245.193.243:9090`. Von dieser Seite aus ist hier
nichts mehr zu tun. Ob Prometheus/Loki auf CFGMON zusätzlich öffentlich erreichbar sind
(unabhängig davon, ob dieser Host den privaten Weg nutzt), ist
[CFGMON-03](cfgmon.md#cfgmon-03--prometheus-remote-write-und-loki-sind-öffentlich-ohne-auth)
- ein reines CFGMON-Thema, nicht mehr blockiert durch etwas auf diesem Host.
### MATRIX-04 — Host-Level Pre-Update-Benachrichtigung · erledigt 2026-07-30
Neuer, eigenständiger Mechanismus auf diesem Host, außerhalb von Flux/GitOps (Details:
`docs/deployment-guides/07-host-maintenance-notifications.md` im gitops-Repo,
`gitops#24` (Gitea-Zählung, Tracker stillgelegt)):
`unattended-upgrades` war bereits aktiv, neu ergänzt ist ein systemd-Timer
(`maintenance-notify.timer`, fest 05:00 Uhr, vor dem 06:00-07:00-Update-Fenster), der bei
anstehenden Paket-Updates per Mail **und** Matrix (Thread-Reply im `wartung`-Raum)
benachrichtigt.
Mail-Versand läuft über `msmtp`, Absender `wartung@axion1337.de` (IONOS SMTP,
`smtp.ionos.de:587`, STARTTLS - **Port 465 war ausgehend blockiert**, vermutlich
Cloud-Provider-Firewall-Regel, 587 ging durch). Relevant für die Mail-Policy-Diskussion
oben: dieses Postfach nutzt die reale, bereits am Apex laufende IONOS-Mail-Infrastruktur,
keine neue Subdomain, kein neuer Handlungsbedarf für die Zone.
+152
View File
@@ -0,0 +1,152 @@
---
type: wiki-page
area: admin
related: []
---
# Overmind
Homelab-Host: GitLab (Dokploy-verwaltet) + CI-Runner. **Nur im Lab erreichbar**
`git.lab` löst außerhalb des Homelabs nicht auf; Produktion (Flux auf CFGMON/MATRIX) hängt
nicht von diesem Host ab, nur neue Builds pausieren bei Ausfall.
| | |
|---|---|
| **Hostname** | `Overmind` |
| **DNS (Lab)** | `git.lab``10.58.73.17` (TLS via Dokploy-Proxy, Zertifikate von der aXionLabs-CA: step-ca, 24h-Leaf, Intermediate bis 2035) |
| **CPU/RAM** | 14 Kerne, 30 Gi (Stand 2026-07-31: ~11 Gi verfügbar) |
| **Disk** | 444 G NVMe (~278 G frei, Stand 2026-07-31) |
| **KVM** | `/dev/kvm` vorhanden — Basis für die Windows-Build-VM |
| **Stand** | 2026-07-31 |
## Dienste
| Dienst | Definition | Anmerkungen |
|---|---|---|
| GitLab CE 18.7.1 + Postgres 16 + Redis 7 | Dokploy-Stack `management-gitlabce` | `external_url https://git.lab`, SSH 2224; TLS terminiert der Dokploy-Proxy (GitLab-nginx lauscht nur :80) |
| gitlab-runner `lab-builder-1` (v18.7.0) | gleicher Stack, Service `gitlab-runner` | Docker-Executor + Socket, `concurrent = 1`. **Stolpersteine, live gefunden**: (1) Docker-interner DNS löst `git.lab` auf den GitLab-Container auf, wo 443 zu ist → `extra_hosts: git.lab:10.58.73.17` nötig; (2) Lab-CA muss nach `/etc/gitlab-runner/certs/git.lab.crt` (Config-Volume, übersteht Redeploys) |
| gitlab-runner `lab-windows-1` | Windows-Gast in der Build-VM | Tags `[windows]`, `run_untagged=false`, Shell-Executor PowerShell — siehe Runbook |
| Windows-Build-VM | Dokploy-Stack `windows-runner` (live seit 2026-07-31) | Image `registry.git.lab/axion1337.chat/vendor/windows:stable` (Eigenbau aus reviewtem Pin `7645a2b`, Vendor-Repo `git.lab/axion1337.chat/vendor/windows`), **on-demand** (`restart: "no"`, Start/Stop über CI-Jobs), 8G/6 Kerne/96G. Runbook: `docs/axion-runner.md` im Vendor-Repo. Gast-Uhr geht falsch (Traces stempeln ~+7h) — kosmetisch |
## Repo-Topologie (Kontext)
git.lab ist seit 2026-07-31 **kanonisch** für die gespiegelten Repos der Gruppe
`axion1337.chat` — Stand 2026-08-09 **sieben**: die sechs Produkt-Repos (ThreadNet-Web,
threadnet-call, thread-net-git, threadnet-operating, axion1337.chat-gitops, seit heute auch
`game-operating`) **und `management`, also dieses Repo**. Push-Mirrors nach rohana/Gitea,
direkte Gitea-Pushes tabu.
⚠️ `gameserver` (achtes Projekt der Gruppe) hat **keinen** Mirror — offen in
[management#32](https://git.lab/axion1337.chat/management/-/issues/32), dort liegt auf Gitea
ein gleichnamiges Repo mit anderem Stand.
Gitea bleibt: Flux-Source (via Mirror beliefert), Registry, Packages.
**Issues nicht mehr** — die sind am 2026-08-01/02 nach git.lab gewandert
([ADR-0002](../../adr/0002-issues-und-management-ins-lab.md)). Die letzte Ausnahme,
die Deploy-Übergabe-Issues auf dem Gitea-Tracker `sorb/management`, ist am 2026-08-02
mit LABNET-03 zurückgebaut: beide umgezogen (#25, #26), der Tracker ist leer.
**Ohne Ausnahme: Issues leben auf git.lab.**
*(Bis 2026-08-01 stand hier „Backlogs (dieses Repo, ungespiegelt)" — das Repo heißt
seit der Umwidmung zum Management-Repo `management` und wird seither gespiegelt,
[ADR-0005](../../adr/0005-pm-framework-kanban.md).)*
## OVERMIND-01 — GitLab-Container-Registry aktivieren, Images nach Konsument sortieren
**Status:** erledigt (2026-08-01)
**Abschluss-Verifikation**: `desktop_image` baut und pusht per `CI_JOB_TOKEN` nach
`registry.git.lab/axion1337.chat/threadnet-web/desktop-build:bullseye` (Job 386 grün,
Tag per API bestätigt); `desktop_linux` nutzt dieses Image als Job-Container und lief
damit grün durch (Job 398 - beweist auch den anonymen Pull des public Projekts durch
den Runner-Daemon). Die rohana-`REGISTRY_*`-Variablen bleiben nur noch für den
App-Image-Push (`docker_web`) in Gebrauch - genau die Ziel-Sortierung nach Konsument.
Die eingebaute GitLab-Registry ist aus (kein `registry_external_url` im Omnibus-Config).
Folge: lab-interne Build-Images (`windows-vm`, `element-desktop-build`) machen den Umweg
Lab-CI → rohana (Prod, Internet) → zurück ins Lab — koppelt Lab-Infrastruktur unnötig an
die Verfügbarkeit des Prod-Hosts.
**Ziel-Sortierung nach Konsument:**
- **rohana (Gitea) behält**: `sorb/threadnet-web` (App-Image — Flux/Prod pullt es),
npm-Packages
- **Lab-Registry (`registry.git.lab`) bekommt**: `windows-vm`,
`element-desktop-build` — beides konsumiert nur das Lab selbst
**Umsetzung** (TLS terminiert wie bei `git.lab` der Dokploy-Proxy):
1. Omnibus-Config: `registry_external_url 'https://registry.git.lab'`,
`registry_nginx['listen_port'] = 5050`, `registry_nginx['listen_https'] = false`;
Port 5050 in der Compose exposen
2. Lab-DNS: `registry.git.lab``10.58.73.17`
3. Dokploy: Domain `registry.git.lab` → GitLab-Service Port 5050 (Zertifikat von der
aXionLabs-CA wie gehabt)
4. Docker-Daemon-Trust auf Overmind: CA-Kette nach
`/etc/docker/certs.d/registry.git.lab/ca.crt` (Datei liegt schon als
`/tmp/git.lab.crt` vom Runner-Setup — kopieren reicht; kein Daemon-Restart nötig)
5. CI-Umstellung: `vendor/windows` pusht nach `registry.git.lab` (Bonus: GitLabs
eingebaute `$CI_REGISTRY`/`$CI_JOB_TOKEN`-Auth statt Gruppen-Secrets),
`desktop_image`/`desktop_linux` in ThreadNet-Web folgen; Registry-Speicher liegt im
`gitlab_data`-Volume (278 G frei)
**Fortschritt 2026-07-31**: Punkte 14 umgesetzt (Registry live auf
`registry.git.lab`, 401/Bearer-Auth korrekt, CA-Trust auf dem Host); `vendor/windows`
pusht per `CI_JOB_TOKEN` in die Lab-Registry — verifiziert, Tags `5bc25447` + `stable`
vorhanden, Runbook referenziert `stable`.
**Nächster Schritt:** `element-desktop-build` von rohana in die Lab-Registry umziehen
(ThreadNet-Web-CI: `desktop_image`-Push-Ziel + `desktop_linux`-Image-Referenz) — bewusst
zurückgestellt, bis kein Auto-Job das alte Image parallel referenziert (Reihenfolge:
erst neues Image bauen, dann Referenz umstellen).
## OVERMIND-02 — Host-Ausfall 2026-07-31 ~19:15 lokal (NIC-Hang, Fix aktiv)
**Status:** Fix aktiv — die Beobachtung läuft als [Issue #4](https://git.lab/axion1337.chat/management/-/issues/4)
(Framework-Umbau 2026-08-01); dieser Abschnitt ist Bestand/Historie.
**Ursache (Journal des Vor-Boots, via Claude-Session auf Overmind):** `e1000e`
`Detected Hardware Unit Hang` auf `eno1` in Endlosschleife — die Intel-NIC hing
(bekanntes e1000e-Problem in Kombination mit EEE/Energiesparen), der Host lief weiter,
war aber netzwerktot. Kein OOM, kein Bezug zur Windows-VM/CI. Passt zu allen Symptomen:
Ping tot, aber der Windows-Runner erreichte GitLab host-intern noch (Job 413 wurde
aufgegriffen) und scheiterte erst an der DNS-Auflösung übers tote Interface.
**Fix (2026-07-31, Overmind-Session):** `ethtool --set-eee eno1 eee off` live gesetzt
+ persistente udev-Regel `/etc/udev/rules.d/71-disable-eee-eno1.rules` (greift bei
jedem Boot). Temporäre sudoers-Freigabe danach wieder entfernt.
**Offen:**
- ~~NIC-/BIOS-Firmware-Update 2.4.0.0 → 2.5.2.0~~ **erledigt** (Wartungsfenster
2026-08-01, durch sorb)
- Falls der Hang trotz EEE-off + neuer Firmware wiederkehrt: gezielter ASPM-Fix
statt globalem Kernel-Parameter
**Zeitleiste (lokal, UTC+2):**
- 18:58 — Windows-VM nach händischem Container-Stop+Start zurück, Runner online
- 19:00 — desktop_windows Job 399 (Versuch 5): läuft bis `build:native`, scheitert an
fehlendem rustc (kein Host-Problem)
- 19:0519:12 — Provision-Job 409 grün (Rust 1.97.1 maschinenweit, Strawberry Perl,
Python 3.14, NASM-PATH), Dienst-Neustart via Scheduled Task funktionierte
- 19:13/19:14 — letzte saubere Runner-Herzschläge
- **zwischen 19:14 und 19:21 — Host fällt aus**: Ping 100 % Verlust, git.lab-API tot
- 19:21 — Job 413 (Versuch 6) wird noch aufgegriffen, Git-Fetch scheitert nach 8 s mit
`Could not resolve host: git.lab` → Host-Netz/DNS zu dem Zeitpunkt bereits kaputt;
danach Funkstille
- ~20:15 — Host pingt wieder (Reboot durch sorb), 20:21 GitLab-API zurück,
lab-builder-1 online; windows-runner-Container down (`restart: "no"` — korrekt)
**Wichtig:** Der Windows-Build war NICHT die Ursache — Job 413 hat nie Quellcode
geholt, die VM lief nur idle (frisch provisioniert; denkbare Gast-Hintergrundlast:
Windows Update/Defender nach den choco-Installs). Die 8-G-Zuteilung der VM plus
GitLab-Stack blieb aber auch nach dem Puma-Fix ein enges Budget auf 30 Gi.
**Konsequenz für die Build-Kette:** RAM-Budget war nicht das Problem — VM bleibt bei
8G. Nach dem NIC-Fix lief die Kette durch: **desktop_windows Job 438 grün**
(2026-07-31 ~21:50 lokal, `Element Setup 1.12.17.exe`, 141 MB, unsigniert) —
ThreadNet-Web#5 geschlossen, Folgethemen (Signing/Branding) in ThreadNet-Web#6.
Auf dem Weg dahin zusätzlich gefixt: GitHub-CDN-Abrisse bei app-builder-Downloads
(resumefähiges Prefetch-Skript im ThreadNet-Web-Repo, Jobs 415/416/424/431).
---
Weitere CI-Betriebsthemen laufen über die Projekt-Issues (ThreadNet-Web#5
Windows-Strecke, threadnet-call#1 npm-Ziel) und CFGMON-11 (Gitea-CI-Rückbau).
+100
View File
@@ -0,0 +1,100 @@
---
type: wiki-page
area: admin
related: []
---
# Refinement und Retro — die Termine des Frameworks
Kanban braucht wenige, aber verlässliche Termine, sonst verkommt das Board zur
Ablage. Festgelegt in [ADR-0005](../../adr/0005-pm-framework-kanban.md); hier
steht, wie sie ablaufen.
## Termine (festgelegt im Struktur-Workshop, 2026-08-06)
| Termin | Wann |
|---|---|
| **Refinement** | **sonntagabends**, wöchentlich, 3045 min |
| **Retro light** | im **ersten Refinement des Monats**, +2030 min |
Sonntag, weil die GitLab-Backups dort ohnehin laufen (00:00) — die Woche hat an
dieser Stelle eine Kante, und die Abendblöcke, in denen real gearbeitet wird,
liegen meist am Wochenende.
Die Retro bekommt **keinen eigenen Termin**: Ein monatlicher Extra-Termin im
Solo-Betrieb ist ein Termin, der ausfällt. Sie hängt sich an das erste Refinement
des Monats an — dann ist die Vorbereitung (die AARs des Monats) ohnehin offen.
## Refinement (≈ wöchentlich, 3045 min)
Der eine Termin, der das System am Leben hält. Immer dieselbe Reihenfolge:
1. **Board von rechts nach links lesen** — zuerst `status:doing`: Läuft es noch,
oder ist es in Wahrheit blockiert? Dann `status:wartet`: Wartet es noch auf das,
was im Issue steht? Erst zuletzt `status:next`.
2. **WIP-Limit prüfen** — höchstens zwei Issues in `doing`. Ist es voll, wird nichts
Neues gezogen; stattdessen wird gefragt, was das Laufende blockiert.
3. **Nachziehen** — freie Plätze aus `next` füllen, `next` aus dem Backlog auffüllen.
Auswahlkriterium ist nicht Priorität allein, sondern **was still kaputtgeht**
(Fristen, abgeschaltete Schutzmechanismen) vor **was nervt** vor **was Spaß macht**.
4. **Entscheidungsvorlagen** — offene Fragen, die eine Entscheidung von sorb brauchen,
werden als Optionen mit Empfehlung vorgelegt, nicht als offene Fragen geparkt.
Dauerhafte Ausnahmen von Regeln werden hier zu ADRs.
5. **Datumspflicht prüfen** — jedes zeitkritische Issue trägt ein Datum, kein „bald".
## Retro light (≈ monatlich, 2030 min)
Drei Fragen, mehr nicht:
- Welche **Verfahren** haben diesen Monat getragen, welche haben gestört?
- Welche **ADRs** sind durch die Realität überholt (→ neues ADR, altes auf
„abgelöst durch")?
- **Fasert etwas aus?** Gibt es wieder Arbeit, die nur in Chatverläufen lebt?
Grundlage sind die AARs des Monats — sie sind die Retro-Vorbereitung, nicht ihr
Ersatz.
Ergebnisse werden unter [`docs/sources/protokolle/`](../../sources/protokolle/retro-2026-08-09.md) abgelegt, eine Datei je Termin. Die
erste: [2026-08-09](../../sources/protokolle/retro-2026-08-09.md).
## AAR (anlassbezogen)
Nach jedem Deploy mit Übergabe und nach jedem Incident, Vorlage in
[docs/aar/template.md](../../aar/template.md). Ein AAR ist keine Chronik, sondern ein
Wissensspeicher: Was war das Ergebnis, welche Befunde, was hat die Eingrenzung
ermöglicht, welche Lehren, was bleibt offen. **Offene Punkte aus einem AAR werden
im selben Zug zu Issues** — sonst versacken sie in der Prosa (real passiert am
2026-08-01, nachgezogen als #14#16).
## Board-Pflege, wenn sorb länger nicht dazukommt
Festgelegt 2026-08-06. Eine Session darf das Board **abbilden**, aber nichts
**zusagen**:
| erlaubt | nicht erlaubt |
|---|---|
| `status:wartet` setzen (mit benanntem Grund) | nach `status:doing` ziehen |
| Erledigtes schließen, mit Begründung im Issue | `status:next` vergeben |
| Fristen ins `due_date`-Feld nachtragen | Prioritäten umsortieren |
| Befunde als neues Issue anlegen | Milestones neu zuordnen |
Die Trennlinie ist nicht Vorsicht, sondern Bedeutung: `doing` und `next` sind die
**Zusage-Spalten** — sie sagen, was als Nächstes wirklich passiert. Das entscheidet
sorb. Alles links davon bildet nur ab, was ohnehin schon der Fall ist.
**Jede Änderung wird im Issue begründet**, nicht still vorgenommen. Ein Board, dem
man nicht ansieht, wer warum etwas verschoben hat, ist beim nächsten Refinement
wertlos.
## Zusammenspiel mit den Sessions
Mehrere Claude-Sessions arbeiten parallel (Mac-Session, Host-Sessions auf CFGMON
und Overmind). Für sie gilt:
- Die **kanonischen Arbeitskonventionen** stehen in [`CLAUDE.md`](../../../AGENTS.md) und
sind über den Gitea-Mirror von überall lesbar.
- Arbeit zwischen Sessions läuft über das
[Deploy-Übergabe-Verfahren](../deployment/deploy-uebergabe.md) — Auftrag, Meldung, Protokoll
im Issue, nicht im Chat.
- Was eine Session lernt, gehört ins Repo (AAR/ADR/Doku), nicht nur in ihr
Gedächtnis — Sessions gehen verloren, Repos nicht.
+77
View File
@@ -0,0 +1,77 @@
---
type: wiki-page
area: admin
related: []
---
# Stillstandsprüfung
Sucht Dinge, die **leise aufgehört haben zu funktionieren**. Beschlossen in der
[Retro 2026-08-09](../../sources/protokolle/retro-2026-08-09.md).
## Warum es sie gibt
In neun Augusttagen sind sechs Fehler aufgefallen, die alle dasselbe Merkmal
hatten: Sie sahen erfolgreich aus, ohne es zu sein — eine grüne Pipeline, die nie
ein Artefakt hochlud; ein veröffentlichtes npm-Paket ohne Inhalt; ein Blueprint,
der bei jedem Lauf verworfen wurde, während Flux grün meldete.
**Keiner davon wurde durch eine Überwachung gefunden.** Vier durch Zufall beim
Suchen nach etwas anderem. Genau diese Lücke schließt das Skript.
## Was geprüft wird
Jede Prüfung bildet einen **real passierten** Fall ab. Nichts steht hier auf
Vorrat.
| Prüfung | Der Fall dahinter |
|---|---|
| Repo ohne aktiven Push-Mirror | `game-operating` wurde angelegt und nie gespiegelt — auf Gitea existierte es nicht |
| Mirror-Drift | MIRROR-01 (management#28): fällt der Mirror aus, liefert Flux still den letzten Stand weiter |
| Pipeline mit null Jobs | ThreadNet-Web 203/204, threadnet-call 187 — rot, ohne dass etwas kaputt war |
| Erfolgreicher Job ohne Artefakt | `build_embedded` lief seit jeher grün und lud **nichts** hoch |
| npm-Paket zu klein | `0.19.2-threadnet.6`: 12,5 KB statt 12,8 MB, ohne `dist/` |
| Authentik-Blueprint ≠ successful | `matrix-recovery-flow` wurde tagelang bei jedem Lauf verworfen |
Die Projektliste wird **zur Laufzeit aus der Gruppe gelesen**, nicht im Code
gepflegt — eine Liste im Quelltext wäre genau die Stelle, an der ein neues Repo
jahrelang durchrutscht. (Beim ersten Lauf kamen so zwei Projekte zum Vorschein,
die niemand auf dem Schirm hatte.)
## Wie sie läuft
Geplanter CI-Job im management-Repo, zusätzlich von Hand über *Run pipeline*
auslösbar. Befunde färben die Pipeline **rot** — das ist bei uns die Alarmanlage,
nicht ein zusätzlicher Meldeweg (siehe `gitops/CLAUDE.md` zur TURN-Rotation).
Lokal:
```bash
export GITLAB_TOKEN=$(cat ~/.config/gitlab-lab/token)
export GITEA_TOKEN=$(cat ~/.config/gitea-rohana/push-token) # fuer private Spiegel
export LAB_CA=.../ci/lab-ca-chain.crt
python3 scripts/stillstandspruefung.py
```
## Zwei Regeln für diese Prüfung
**Ein „kann nicht geprüft werden" ist ein Befund, kein Übersprungen.** Real
aufgefallen am 2026-08-09: `game-operating` wurde auf Gitea privat gestellt, und
die Prüfung übersprang den Mirror-Abgleich klaglos. Ein Repo, das gespiegelt wird,
dessen Gegenseite aber unlesbar ist, ist **ungeprüft** — und das darf nicht wie
„in Ordnung" aussehen.
**Ein Befund wird zum Issue, nicht weggeklickt.** Sonst wird die Prüfung zu dem,
was sie sucht: etwas, das läuft, ohne dass jemand hinsieht.
⚠️ **Fehlt ein Zugang, bricht sie ab — sie überspringt sich nicht still.** Eine
Prüfung, die sich bei fehlendem Token selbst deaktiviert, ist wertlos: Sie meldet
dann jahrelang nichts, und niemand merkt den Unterschied zu „alles in Ordnung".
Ausnahme sind die klar benannten optionalen Teile (Authentik), die ihr Fehlen im
Ergebnis ausweisen.
## Erweitern
Neue Prüfungen kommen dazu, **wenn wieder etwas still ausgefallen ist** — mit einem
Docstring, der den konkreten Fall nennt. Prüfungen auf Verdacht erzeugen Rauschen
und kosten die Glaubwürdigkeit, die diese hier braucht.
+122
View File
@@ -0,0 +1,122 @@
---
type: wiki-page
area: admin
related: []
---
# Textbausteine für Sessions
Kurze, kopierbare Blöcke, die man einer Claude-/Agenten-Session voranstellt.
Die Konventionen stehen kanonisch in [`CLAUDE.md`](../../../AGENTS.md) — aber eine
Session liest sie nur, wenn sie dazu aufgefordert wird. Diese Bausteine sind die
Aufforderung.
## Zwei Regeln für diese Datei
**Die Bausteine verweisen auf die Regeln, sie wiederholen sie nicht.** Stünden die
Regeln hier ausgeschrieben, gäbe es eine zweite Fassung, die driftet — real
passiert am 2026-08-02, als `gitops/CLAUDE.md` „keine Gitea-Ausnahme mehr" behauptete,
während die `management/CLAUDE.md` zwei nannte.
**Höchstens acht Zeilen je Baustein.** Der Test ist banal: Wer zum Kopieren scrollen
muss, benutzt es nicht. Was länger wäre, gehört in die CLAUDE.md — nicht hierher.
---
## 1 · Session-Start (Mac, mit Lab-Zugang)
```
Lies zuerst CLAUDE.md im management-Repo auf git.lab und halte dich daran.
Kanonisch ist git.lab; nie direkt nach Gitea pushen.
Alles Offene wird zum Issue, nicht zur Chat-Notiz — auch Nebenbefunde.
Bevor du ein Issue schließt oder darüber urteilst: vollständig lesen, inklusive
Kommentare.
Bevor du aus einer Vorlage/Spezifikation ableitest: die Quelle öffnen, nicht raten.
Verifiziert und vermutet klar trennen; fremde Messungen als fremde kennzeichnen.
```
> Die letzten drei Zeilen stehen hier, weil genau das dreimal an einem Tag
> schiefging: erfundene Theme-Paletten statt gelesener Skill-Quelle; ein Sweep nach
> dem Pfad `sorb/Backlogs` statt nach dem Namen `Backlogs`; und ein Issue, von dem
> 750 von 1237 Zeichen gelesen wurden — samt übersehenem Korrekturkommentar, der
> seit 16 Stunden darunterstand.
## 2 · Host-Session (CFGMON, MATRIX — ohne Lab-Zugang)
```
Du arbeitest auf einem Hetzner-Host ohne direkte Lab-Route.
Konventionen: CLAUDE.md im management-Repo — von hier lesbar über den Gitea-Mirror
rohana.axion1337.de/sorb/management. Dort NUR lesen, niemals hinpushen.
Für git.lab (Issues, Pushes) muss sorb erst den Site-to-Site-Tunnel einschalten.
git.lab-API: PRIVATE-TOKEN-Header — .netrc gilt nur für clone/push (sonst 401,
bei privaten Projekten irreführend 404, sieht aus wie "Projekt gibt es nicht").
Ping auf 10.58.73.17 schlägt IMMER fehl (nur 443 + DNS offen), das ist kein
Tunnelproblem — prüfen mit: curl https://git.lab/users/sign_in
```
> Soll in dieser Session etwas ausgerollt werden, kommt **Baustein 3** dazu — der
> Deploy-Weg samt AAR-Pflicht steht dort, nicht hier, damit dieser Block kurz bleibt.
## 3 · Deploy-Übergabe
```
Öffne auf git.lab ein Issue aus der Vorlage "Deploy-Übergabe"
(Feld "Description template") und fülle ALLE Felder — Verfahren und Begründung
je Feld: verfahren/deploy-uebergabe.md.
Pflicht: Stand (Repo/Branch/Commit) · Testtiefe (ehrlich, "ungetestet" ist gültig)
· Mengengerüst (geschätzt oder gemessen, dazuschreiben welches) · vollständiges
Deploy-Kommando inkl. Reload/Recreate · Verifikation DORT WO DER DIENST LIEST
· Außenwirkung und Not-Aus · Rollback · bewusst offen Gelassenes.
Wo nichts zutrifft: "-" eintragen, nicht das Feld löschen.
```
## 4 · Abschluss einer Session
> **Dieser Baustein ist zugleich unsere Definition of Done für Änderungen ohne
> Deploy** (festgelegt 2026-08-06). Für Deployments gilt weiterhin das
> [Übergabe-Verfahren](../deployment/deploy-uebergabe.md) — das ist die längere DoD.
>
> Bewusst kein eigenes DoD-Dokument: Es wäre die dritte Fassung derselben Regeln
> und damit die dritte, die driften kann.
```
Vor dem Ende prüfen und benennen:
- Alle Commits über git.lab gepusht, kein Rest im Arbeitsverzeichnis, Mirror grün.
- Jeder offene Punkt und Nebenbefund ist ein Issue — nichts bleibt nur im Chat.
- Zeitkritisches trägt ein Datum im due_date-Feld, nicht nur im Fließtext.
- Genau ein status:*-Label je angefasstem Issue; status:wartet nur mit Grund.
- Gedächtnis aktualisiert: nur was kein Repo festhält.
- Wiederaufsetzpunkt in einem Satz: Was ist als Nächstes dran, und wer ist dran?
```
## 5 · Entscheidungsvorlage
```
Leg mir das als Entscheidung vor, nicht als offene Frage:
24 Optionen, je eine Zeile Konsequenz, und deine Empfehlung zuerst mit Begründung.
Sag dazu, was du gemessen und was du angenommen hast.
Wenn die Entscheidung eine dauerhafte Ausnahme von einer Regel schafft, ist sie
ADR-pflichtig (decisions/, siehe CLAUDE.md) — dann leg die ADR gleich mit vor.
```
---
## Wann welcher
| Situation | Baustein |
|---|---|
| Neue Session auf dem Mac | 1 |
| Session auf CFGMON/MATRIX/game | 2 |
| Etwas gebautes soll ausgerollt werden | 3 |
| Session neigt sich dem Ende | 4 |
| Eine Frage braucht sorbs Entscheidung | 5 |
Bausteine 1 und 2 schließen sich aus; 35 kommen anlassbezogen dazu.
## Pflege
Ein Baustein wird ergänzt, wenn **derselbe Fehler zweimal** passiert ist — nicht
vorsorglich. Sonst wachsen sie, bis sie niemand mehr kopiert, und dann wirken sie
gar nicht mehr. Wächst einer über acht Zeilen, gehört der Inhalt in die CLAUDE.md
und hier bleibt der Verweis.
+220
View File
@@ -0,0 +1,220 @@
---
type: wiki-page
area: architecture
related: []
---
# Branding: Marke, Farben, Bildsprache
Übergreifend, weil dieselben Farben in mehreren Oberflächen eingestellt werden
(Chat-Clients, Wikis, künftige Dienste) und sich das nicht pro Host oder Linie
trennen lässt: **Bestand** (welche Farbe wo eingetragen ist) und **Historie**
(was wann warum geändert wurde).
Hier im `management`-Repo, weil es als einziges der beteiligten Repos
**gespiegelt** ist und jede Werkzeugentscheidung überlebt: Wird das
BookStack-Experiment nach [ADR-0007](../../adr/0007-wiki-oberflaeche-docusaurus-vs-bookstack.md)
abgeräumt oder ein Client ersetzt, bleiben Marke und Paletten bestehen.
## Bildmarke
`threadnet-mark.png` (Bildmarke) und `threadnet-logo-wortmarke.png` (mit
Schriftzug), erstellt von sorb. Sie liegen im Wiki-Repo unter `static/img/` und
sind die Quelle für alle abgeleiteten Formate — Favicon, App-Icons für macOS,
Windows und Linux, Web-Icons.
⚠️ Beim Ableiten den **transparenten Rand wegschneiden** und mit `resize()`
skalieren, nicht mit `thumbnail()` — Letzteres verkleinert nur und lässt das Motiv
in großen Icons als Briefmarke zurück (real passiert, siehe
[AAR 2026-08-02](../../aar/2026-08-02-wiki-und-desktop-clients.md)).
⚠️ **Und danach mittig setzen — das ist ein zweiter, eigener Schritt.** Beim ersten
Anlauf wurde nur beschnitten und skaliert: Das Motiv füllte 81 % der Breite, saß
aber mit 3 % Rand oben und 40 % unten an der Oberkante. In runden und quadratischen
Icon-Slots fällt das sofort auf. Korrigiert am 2026-08-06 auf 21 % oben wie unten.
**Das Rezept, das jetzt gilt:** Bounding-Box des Motivs ermitteln, darauf
beschneiden, auf ~81 % der Kantenlänge skalieren, auf einer quadratischen,
transparenten Fläche **mittig einsetzen**. Alle Größen aus *derselben* Quelldatei
ableiten — dann können sie nicht auseinanderlaufen.
### Wo überall Icons liegen
Elf Artefakte, alle aus einer Quelle (Stand 2026-08-06, `v0.4.0`):
| Ort | Was |
|---|---|
| `apps/web/res/vector-icons/` | 1024, 512, 180, 152, 144, 120, 24 px |
| `apps/desktop/build/icon.png` | App-/Installer-Icon |
| `apps/desktop/build/icon.ico` | Windows, 7 Größen von 16 bis 256 |
| `apps/desktop/build/icon.icns` | macOS, via `iconutil` aus einem `.iconset` |
| `apps/desktop/build/icon.icon/Assets/element.png` | Layer des macOS-Icon-Composers |
Prüfen lässt sich die Gleichheit über die Prüfsumme von `vector-icons/1024.png`
gegen `build/icon.png` — weichen sie ab, ist eine Seite nachgezogen worden und die
andere nicht.
## Paletten
### aXion1337 Dark — das Stammschema
Gruvbox Dark. Grundtöne `#282828` / `#1d2021`, Text `#ebdbb2`, Akzent `#bd93f9`,
Sekundär `#fe8019`. Die acht Username-Farben sind der Gruvbox-Satz.
**`aXion1337 Light` ist das exakte helle Gegenstück** (Gruvbox Light) — gleiche
Rollenverteilung, gespiegelte Helligkeitsachse. Wer eines ändert, zieht das andere
mit.
### Terrakotta-Beige — sorbs BookStack-Schema
Am 2026-08-02 in der BookStack-Oberfläche eingestellt und von dort extrahiert
(die Werte stehen als CSS-Variablen im ausgelieferten HTML). Fünf der sieben sind
der Coolors-Satz `#264653 · #2A9D8F · #E9C46A · #F4A261 · #E76F51`:
| Rolle in BookStack | Wert | Ton |
|---|---|---|
| Primäre Farbe | `#264653` | Charcoal |
| Standard-Linkfarbe | `#5f757b` | gedämpftes Blaugrau |
| Regalfarbe | `#e76e51` | Burnt Sienna |
| Buchfarbe | `#e9c46a` | Saffron |
| Kapitelfarbe | `#f3a261` | Sandy Brown |
| Seitenfarbe | `#77bb41` | Grün |
| Seitenentwurfsfarbe | `#e32400` | Rot |
Bemerkenswert: **dieses von Hand eingestellte Schema ist bis auf zwei Ziffern die
offizielle Sunset-Boulevard-Palette** (siehe nächster Abschnitt) — `#e76e51` statt
`#e76f51`, `#f3a261` statt `#f4a261`. Beide Wege sind unabhängig voneinander auf
demselben Coolors-Satz gelandet. Die Irritation an der ersten Theme-Fassung war
also berechtigt und ließ sich an der Originalquelle belegen.
### theme-factory — die zehn benannten Themes
Quelle ist Anthropics [theme-factory-Skill](https://github.com/anthropics/skills/tree/main/skills/theme-factory):
je Theme vier Farben plus ein Schriftpaar. Sie sind seit 2026-08-02 **wörtlich
übernommen** (gitops `b10b607`), nachdem eine erste Fassung die Namen frei
interpretiert hatte.
| Theme | Hintergrund | hell/dunkel | Akzent · Sekundär · Highlight |
|---|---|---|---|
| Ocean Depths | `#f1faee` | hell | `#2d8b8b` · `#a8dadc` · `#457b9d` |
| Sunset Boulevard | `#264653` | dunkel | `#e76f51` · `#f4a261` · `#e9c46a` |
| Forest Canopy | `#faf9f6` | hell | `#2d4a2b` · `#7d8471` · `#a4ac86` |
| Modern Minimalist | `#ffffff` | hell | `#36454f` · `#708090` · `#d3d3d3` |
| Golden Hour | `#4a403a` | dunkel | `#f4a900` · `#c1666b` · `#d4b896` |
| Arctic Frost | `#fafafa` | hell | `#4a6fa5` · `#d4e4f7` · `#c0c0c0` |
| Desert Rose | `#5d2e46` | dunkel | `#d4a5a5` · `#b87d6d` · `#e8d5c4` |
| Tech Innovation | `#ffffff` | hell | `#0066ff` · `#00ffff` · `#1e1e1e` |
| Botanical Garden | `#f5f3ed` | hell | `#4a7c59` · `#f9a620` · `#b7472a` |
| Midnight Galaxy | `#e6e6fa` | hell | `#2b1e3e` · `#4a4e8f` · `#a490c2` |
⚠️ **Ob ein Theme hell oder dunkel gemeint ist, steht nicht verlässlich in den
Beschreibungen** — „Warm Sand · backgrounds" findet sich bei einem Theme, dessen
Showcase-Seite dunkel ist. Maßgeblich ist `theme-showcase.pdf` im Skill: sieben der
zehn sind hell, nur Sunset Boulevard, Golden Hour und Desert Rose dunkel.
„Midnight" Galaxy ist trotz des Namens ein helles Theme mit dunkelviolettem Akzent.
Wer die Werte prüfen will, rendert die PDF-Seiten und misst die Hintergrundfarbe,
statt der Prosa zu glauben.
Die übrigen Rollen (Flächenabstufungen, Username-Farben) sind aus diesen vier
Farben gemischt — so bleibt jedes Theme in sich stimmig, ohne dass Werte
dazuerfunden werden.
## Namensgebung: ThreadNet oder aXion1337.Chat?
**Beides, auf verschiedenen Ebenen — und das ist Absicht, kein Versehen.**
| Ebene | Name | Wo gesetzt |
|---|---|---|
| Betriebssystem, Startmenü, Installer, PWA | **ThreadNet** | `productName` in `apps/desktop/axion1337/build.json`, `name` in `apps/web/res/manifest.json` |
| in der Anwendung | **aXion1337.Chat** | `brand` in `element-values.yaml` (Prod) und `apps/desktop/axion1337/config.json` |
| eingebettetes Call-Widget | **aXion1337.Chat** | `VITE_PRODUCT_NAME` in `.env.production` (threadnet-call) |
| Anmeldeseite (Authentik) | **ThreadNet** | `branding_title` im Brand-Blueprint (gitops, `apps/authentik/authentik-blueprints.yaml`) |
Das Call-Widget folgt der Zeile darüber: Es läuft *in* der Anwendung, also heißt es
dort auch so. Die Anmeldeseite dagegen kommt **vor** der Anwendung — dort meldet man
sich am Werkzeug an, nicht in der Gemeinschaft. Deshalb ThreadNet.
Die Leitplanke dahinter steht in [`vision/threadnet.md`](../vision/threadnet.md):
*ThreadNet ist das Tool, axion1337.chat die Community.* Das Programm heißt
ThreadNet — auch wenn es jemand für eine andere Instanz nutzt; die Gemeinschaft
darin heißt aXion1337.Chat.
⚠️ **Nicht „geradeziehen".** Wer nur eine der beiden Stellen sieht, hält es für
eine Inkonsistenz. Ist es nicht.
**Attribution:** „ThreadNet — powered by Element" steht seit `v0.4.0` in
*Einstellungen → Hilfe & Info*, direkt unter der Client-Version, verlinkt auf
element.io. Element Web steht unter der AGPL; ein Fork unter eigenem Namen ist
erlaubt, die Nennung ist die faire Form davon. Bewusst **nicht** im Kopiertext der
Versionsangabe — der landet in Fehlerberichten, dort ist die Fork-Herkunft nur
Rauschen.
## Wo was eingestellt ist
| Oberfläche | Ort | Anmerkung |
|---|---|---|
| Element/ThreadNet-Web | `apps/production/custom-configs/element-values.yaml` (gitops), `setting_defaults.custom_themes` | 17 Themes; Änderungen chirurgisch, **nie die YAML neu serialisieren** |
| Web-Icons + PWA | `apps/web/res/vector-icons/`, `apps/web/res/manifest.json` (ThreadNet-Web) | `theme_color` = `#ed4f4c`, die Markenfarbe — nicht Elements `#76CFA6` |
| Desktop-Icons | `apps/desktop/build/` (ThreadNet-Web) | `.png`, `.ico`, `.icns`, Layer-Asset — alle aus derselben Quelle |
| ThreadNet Desktop | `apps/desktop/axion1337/config.json` (ThreadNet-Web) | eigene Kopie derselben Themes — beim Ändern beide mitziehen |
| BookStack | *Settings → Customization*, getrennt für hell und dunkel | liegt in der Datenbank, **nicht im Repo** — schriftlich hier und in `theme/sorbs-palette.md` |
| BookStack (Feinschliff) | `theme/*.css` im Wiki-BookStack-Repo | nur Flächen, Text, Ränder — die sieben Farben oben gehören in die Oberfläche |
| Docusaurus-Wiki | `src/css/custom.css` (homelab/wiki) | bislang nur Akzentfarbe |
| Titelbild Login | `apps/web/res/themes/element/img/backgrounds/alpenglow.jpg` (ThreadNet-Web), gesetzt in `SdkConfig.ts` | siehe unten — Bilddatei kommt nur über einen Build in den Container |
| Anmeldeseite Authentik | Brand-Blueprint in `apps/authentik/authentik-blueprints.yaml` (gitops) | Favicon und Hintergrund werden **von axion1337.chat referenziert**, nicht hochgeladen. **Logo ist noch Authentiks eigenes** → gitops#55 |
### Titelbild
Seit 2026-08-06 zeigt die Login-Seite ein Alpenglühen über einer Bergkette statt
Elements See: **John Towner** ([@heytowner](https://unsplash.com/@heytowner)),
[Unsplash](https://unsplash.com/photos/JgOeRuGD_Y4), *Unsplash License*.
Die Lizenz erlaubt kommerzielle Nutzung und Bearbeitung ohne Genehmigung und
**verlangt keine Namensnennung**. Genannt wird er trotzdem — in *Einstellungen →
Hilfe & Info* unter „Danksagungen". Verboten wäre nur der Weiterverkauf
unbearbeiteter Bilder und das Nachbauen eines konkurrierenden Bilddienstes; beides
trifft uns nicht.
⚠️ **Die Danksagung ist bewusst nicht übersetzt.** Elements Schlüssel
`credits|default_cover_photo` liegt in 32 Sprachdateien, 31 davon nennen deren
Fotografen namentlich. Nur `en`/`de` anzupassen hätte in 29 Sprachen eine **falsche
Attribution** stehen lassen — und diese 29 pflegen wir nicht, sie kommen aus Elements
Übersetzungsdienst. Deshalb steht die Zeile als fester Text im TSX, wie schon die
„powered by Element"-Attribution.
⚠️ **Authentik hängt an dieser Datei.** Der Flow-Hintergrund dort zeigt auf
`https://axion1337.chat/themes/element/img/backgrounds/alpenglow.jpg`. Wer das Bild im
Client umbenennt oder entfernt, macht die Anmeldeseite grau — und merkt es nicht,
weil im Client alles stimmt.
### Warum in Authentik kein PNG-Logo funktioniert
Der erste Versuch setzte `branding_logo` auf `vector-icons/512.png`. Ergebnis: das
Logo rendert in der Anmeldemaske in **Naturgröße**. Authentiks Default ist ein SVG,
das sich seiner Box anpasst — ein PNG tut das nicht, und der Slot begrenzt die Höhe
nicht.
Zurückgesetzt am 2026-08-06 auf Authentiks eigenes Logo. Ein Ersatz braucht eine
**Wortmarke im Querformat, am besten als SVG**; alles Vorhandene ist quadratisch,
auch `threadnet-logo-wortmarke.png` (Bildmarke *über* Schriftzug). Offen in
gitops#55.
⚠️ **Nicht über Authentiks Oberfläche einstellen.** Solange `branding_logo` im
Blueprint steht, gewinnt der Blueprint: eine Auswahl in der UI hält bis zur nächsten
Reconciliation und ist dann wieder weg.
⚠️ **Das BookStack-Schema lebt nur in der Datenbank.** Bei einem Volume-Verlust
ist es weg — deshalb steht es oben in dieser Tabelle. Wiederherstellen heißt:
sieben Felder in der Oberfläche neu eintragen.
Es steht bewusst an **zwei** Stellen schriftlich, und die Rollen sind verschieden:
`theme/sorbs-palette.md` im BookStack-Repo ist die betriebsnahe Kopie mit den
DB-Schlüsseln, liegt aber in der Gruppe `homelab` — die hat **keine Mirrors** und
ist von außerhalb des Labs nicht lesbar. Diese Datei hier ist die gespiegelte
und damit maßgebliche Fassung. Wer die Farben ändert, zieht beide mit; im
Zweifel gilt, was in der laufenden Instanz eingestellt ist.
## Offen
Ob die Terrakotta-Richtung das Stammschema ablösen oder eine Alternative neben
Gruvbox bleiben soll, ist nicht entschieden — das gehört in die Rebranding-Runde
(→ [`vision/threadnet.md`](../vision/threadnet.md), ThreadNet-Web#10).
+133
View File
@@ -0,0 +1,133 @@
---
type: wiki-page
area: architecture
related: []
---
# Lab-Netzwerk (Heimnetz: Fritzbox + UDM Pro)
Themen rund um das Homelab-Netz selbst — Router-Kaskade, VLANs, VPN-Zugänge.
Hosts im Lab: Overmind (git.lab, [hosts/overmind.md](../admin/overmind.md)),
der Mac. Kaskade: **Fritzbox (WAN) → UDM Pro**, kein Doppel-NAT, statische
Route in der Fritzbox für das Lab-VLAN.
> ✅ **Das VPN-Thema ist abgeschlossen und validiert** (sorb, 2026-08-02).
> Beide Zugänge laufen und sind abgenommen, ADR-0004 steht auf *akzeptiert*
> (Testreihe 17 in [#12](https://git.lab/axion1337.chat/management/-/issues/12)).
> Es gibt dazu **keine offenen Issues mehr** — auch die Restpunkte #11
> (MacBook-Profil) und #16 (LABNET-04, Feinschliff an den UniFi-Regeln) sind
> geschlossen. Alles Folgende ist **Bestand und Historie**, keine offene Arbeit.
**Zwei WireGuard-Zugänge (Stand 2026-08-01, beide gelöst/abgenommen):**
| Zugang | Server | Port | Tunnelnetz | Zweck |
|---|---|---|---|---|
| Roadwarrior „Thore" | UDM | 51840 | 10.58.74.0/24 | Handy/MacBook ins Lab (LABNET-01) |
| Site-to-Site „Matrix" | UDM | 51841 | 10.58.75.0/24 | Hetzner-Netz 10.0.0.0/24 ↔ Lab (LABNET-02, [ADR-0004](../../adr/0004-site-to-site-vpn-hetzner-lab.md)) |
### Verhältnis zu `homelab/docs`
Die Tabelle oben steht **absichtlich doppelt**. Maßgeblich für die
Soll-Konfiguration ist
[homelab/docs → MorninglightMountain](https://git.lab/homelab/docs/-/blob/main/netz/morninglightmountain.md)
(dort auch Portfreigaben, Firewall-Zonen, Diagnose-Merksätze) — dieses Repo
führt die **Historie**: was wann warum geändert wurde und mit welchem Issue.
Der Grund für die Doppelung ist der Mirror-Geltungsbereich aus der
[CLAUDE.md](../../../AGENTS.md): Die Gruppe `homelab` hat bewusst **keine Mirrors** und
ist von außerhalb des Labs nicht lesbar. Wer ohne Tunnel nachsehen muss, welcher
Tunnel überhaupt auf welchem Port liegt, findet es nur hier. Deshalb hält dieses
Dokument einen Kurzüberblick vor — Ports, Tunnelnetze, Zweck — und nichts
darüber hinaus. **Bei Widerspruch gilt `homelab/docs`.**
---
## LABNET-01 — WireGuard-Roadwarrior ins Lab kaputt (seit einigen Monaten)
**Status:** GELÖST 2026-08-01 ~15:20 — `git.lab` lädt vom Handy über 5G/VPN. ✅
Damit ist die Cutover-Voraussetzung für gitops#48 erfüllt.
**Drei gestapelte Ursachen (jede verdeckte die nächste):**
1. **Privater Endpunkt** in jeder UDM-generierten Client-Config (UDM kennt hinter
der Fritzbox ihre öffentliche IP nicht) → Fix: Endpunkt `178.25.213.70`;
dauerhaft gelöst über UniFi-Option **„Alternate Address for Clients"**.
2. **FritzOS reserviert UDP 51820 für seinen eigenen WireGuard-Stack** — die
Portfreigabe 51820→UDM lief ins Leere (erklärt die „invalid response"-Stürme
im Juli: zwei WG-Stacks auf einem Port) → Fix: UDM-WG auf **51840**.
3. **Docker-Routen-Kollision auf Overmind**: Dokploys Bridge-Netze belegen zehn
/20-Blöcke in 192.168.0.0/16; `192.168.0.0/20` verschluckte das VPN-Subnetz
192.168.5.0/24 → Antworten an VPN-Clients endeten in der Bridge (SYN kam an,
SYN-ACK verschwand — exakt der Chrome-Connection-Timeout, während Ping/DNS/
fremde Hosts funktionierten) → Fix: **VPN-Subnetz auf 10.58.74.0/24** (Docker
fasst 10.x nie an).
**Restarbeiten:** MacBook-WG-Profil → [Issue #11](https://git.lab/axion1337.chat/management/-/issues/11). ⚠️ Latente Wiederholungsgefahr
notiert: Overminds Docker-Pool deckt auch `192.168.176.0/20` ab = kollidiert mit
dem Fritzbox-Netz 192.168.178.x — aktuell folgenlos, aber bei künftigen Subnetz-
Entscheidungen 192.168.x auf Overmind grundsätzlich meiden (oder Docker
default-address-pools begrenzen).
**Bestätigte Ursachenkette (Diagnose-Session 2026-08-01):**
1. Handy-Profil „@home" hatte seit 07.07. die **private** UDM-WAN-IP
(192.168.178.20) als Endpunkt — die UDM kennt hinter der Fritzbox ihre
öffentliche IP nicht und schreibt sie in jede generierte Config
(⚠️ gilt für ALLE künftig exportierten Profile: Endpunkt manuell auf
178.25.213.70 ändern!).
2. Nach Endpunkt-Fix weiter tot: **FritzOS reserviert UDP 51820 für seinen
EIGENEN WireGuard-Stack** — die alte Portfreigabe 51820→UDM lief ins Leere
(erklärt auch die „invalid response"-Stürme im Juli: zwei WG-Stacks auf
einem Port). Fix: UDM-WG auf **51840** umgezogen + Freigabe angepasst.
3. Server + Schlüssel waren nie das Problem (WLAN-Handshake bewies beides).
Danach nur noch der ursprüngliche Blocker-Vermerk:
blockierte gitops#48 (Erreichbarkeits-Entscheidung „WireGuard statt exponieren")
sorb hatte einen funktionierenden VPN-Zugang fürs Handy ins Lab; seit einigen
Monaten „funktioniert das nicht mehr sauber" (Symptome noch zu präzisieren:
Handshake? Routing nur teilweise? DNS?). Setup-Rahmen laut sorb (2026-08-01):
UDM Pro hinter Fritzbox, ohne doppeltes NAT, statische Route in der Fritzbox
für das VLAN.
**Diagnose-Plan von VOR der Lösung** — ⚠️ abgearbeitet und überholt, steht hier
nur als Beleg, wie die Ursachen eingekreist wurden. Nichts davon ist zu tun:
1. Symptom präzisieren: Handshake kommt zustande? (`wg show` auf der UDM /
Client-Log) — trennt Portweiterleitungs- von Routing-Problemen
2. Fritzbox: Portfreigabe UDP (WireGuard-Port) → UDM noch vorhanden/korrekt?
(FritzOS-Updates werfen gern Freigaben/Exposed-Host-Einstellungen um)
3. ~~DS-Lite~~ **ausgeschlossen** (sorb 2026-08-01: Dualstack + feste IPs) —
damit auch kein DynDNS-Drift möglich. Verdacht konzentriert sich auf:
FritzOS-Update warf die UDP-Portfreigabe um, UniFi-OS-Update veränderte
WG-Server/Firewall, oder die statische Route griff nach Änderung nicht mehr.
4. (entfällt — feste IP)
5. UDM-Seite: WireGuard-Server-Config/Firewall-Regeln nach UniFi-OS-Updates
prüfen; statische Route Fritzbox → VLAN gegenchecken
6. Erst wenn 15 sauber: Client-Profil fürs Handy neu ausstellen, git.lab-DNS
(Lab-Resolver) in die AllowedIPs/DNS-Konfig aufnehmen — Erfolgsbeweis =
Issue-Board vom Handy über VPN erreichbar
**Verwandt:** gitops#48 (Cutover erst nach Lösung), perspektivisch ersetzt ein
funktionierender Roadwarrior auch Ad-hoc-Wünsche wie „GitLab exponieren".
## Zugehörige Issues — alle geschlossen
Seit dem Framework-Umbau (2026-08-01) leben offene Punkte als Issues im
[management-Projekt](https://git.lab/axion1337.chat/management/-/issues); die IDs bleiben in den Issue-Titeln erhalten.
Dieses File hält nur noch Bestand und Historie.
Zum Netz/VPN ist **nichts mehr offen** (Stand 2026-08-02):
| Issue | Thema | Stand |
|---|---|---|
| [#11](https://git.lab/axion1337.chat/management/-/issues/11) | LABNET-01-Rest — MacBook-WireGuard-Profil | geschlossen |
| [#12](https://git.lab/axion1337.chat/management/-/issues/12) | LABNET-02 — Site-to-Site-VPN (Design: [ADR-0004](../../adr/0004-site-to-site-vpn-hetzner-lab.md)) | geschlossen, Testreihe 17 protokolliert |
| [#16](https://git.lab/axion1337.chat/management/-/issues/16) | LABNET-04 — Feinschliff UniFi-Regeln | geschlossen |
Zwei Punkte tragen zwar LABNET im Text, gehören aber **nicht** zum VPN-Thema und
bleiben offen: [#13](https://git.lab/axion1337.chat/management/-/issues/13)
(LABNET-03, Rückbau der Gitea-Ausnahme für Übergabe-Issues — durch den Tunnel
erst möglich geworden, aber eine Repo-Frage) und
[#15](https://git.lab/axion1337.chat/management/-/issues/15) (CFGMON-15,
Widerruf der Einmal-Tokens aus der LABNET-02-Nacht — Credential-Hygiene, und der
Widerruf kann still einen Push-Mirror brechen, solange dessen hinterlegtes Token
unbekannt ist).
@@ -0,0 +1,64 @@
---
type: wiki-page
area: architecture
related:
- "docs/adr/0001-gitlab-kanonisch-push-mirror.md"
- "docs/adr/0002-issues-und-management-ins-lab.md"
- "docs/adr/0004-site-to-site-vpn-hetzner-lab.md"
- "docs/adr/0006-wikis-konsolidieren-docusaurus.md"
---
# Mirror-Topologie: Das Lab ist die Quelle der Wahrheit
Begründungsprosa übernommen aus der alten `CLAUDE.md` (Abschnitt
„Projektrealitäten", Wortlaut in der git-Historie vor der
neckbeard-Migration); die verbindlichen Regeln stehen in `AGENTS.md` §6
und den verlinkten ADRs. Erhalten per Feldtest-Befund F-013: Diese
Begründung samt Gegenargument und Rettungspfad ist der Wert — sie
erklärt, warum die Topologie so aussieht und so bleiben soll.
- Kanonische Repos liegen auf `git.lab/axion1337.chat/*` (nur im
Lab/VPN auflösbar). Gitea/rohana wird per **Push-Mirror** beliefert
und bleibt Flux-Source, Container-/npm-Registry und Release-Download
([ADR-0001](../../adr/0001-gitlab-kanonisch-push-mirror.md)).
- **Warum überhaupt zwei Orte — und warum das kein Altbestand ist:**
Auf git.lab liegen die *Baupläne*, auf Gitea eine Kopie, die der
Cluster **ohne verfügbares Lab** erreicht. Der Hetzner-Cluster muss
sich bauen und neu ausrollen lassen, wenn das Homelab aus ist, im
Umbau steckt oder niemand zu Hause ist — er darf deshalb nicht von
einem Host abhängen, der nur im Lab antwortet.
⚠️ **Die Flux-Quelle nicht „geradeziehen"** auf git.lab: Das sähe
aufgeräumter aus und würde die Verfügbarkeit der Produktion an das
Lab koppeln — genau das, was die Trennung verhindert.
- **Gespiegelt wird nur die Gruppe `axion1337.chat`** (die Produkt-Repos
und `management`). Die Gruppe **`homelab`** (`docs`, `wiki`,
`wiki-bookstack`) hat bewusst **keine Mirrors**: Sie beschreibt und
konfiguriert ausschließlich Lab-Infrastruktur, und seit dem
Site-to-Site-VPN ([ADR-0004](../../adr/0004-site-to-site-vpn-hetzner-lab.md))
erreichen auch Host-Sessions git.lab direkt — Tunnel einschalten
genügt. Betriebslehren, die von außen lesbar sein müssen, gehören
deshalb in die **AARs** unter `docs/aar/` (dieses Repo ist
gespiegelt), nicht nur in die READMEs der Lab-Repos.
- Landet doch ein Commit auf Gitea (z. B. aus einer Host-Session ohne
Lab-Route): **Kanonisierungs-Verfahren** in
[deploy-uebergabe](../deployment/deploy-uebergabe.md) — `.patch` von
Gitea ziehen, `git am` (erhält Autorschaft), Push über git.lab.
- ⚠️ gitops-Issue-Nummern haben sich beim Gitea-Umzug verschoben (Gitea
zählte PRs mit; z. B. Gitea#48 → GitLab#46) — alte „gitops#N"-Verweise
meinen die Gitea-Nummer; verbindlich ist der Migrations-Fußtext im
Issue ([ADR-0002](../../adr/0002-issues-und-management-ins-lab.md)).
- **Ausnahme** (bewusst entschieden, nur noch eine): der
TURN-Rotations-CronJob schreibt weiter nach Gitea, weil er im Cluster
läuft und git.lab nicht erreicht. **Die Rotation nicht von Hand
nachziehen und den PR nie auf Gitea mergen** — das erledigt seit
2026-08-02 der geplante CI-Job `canonize_rotation` im gitops-Repo
täglich von git.lab aus. Scheitert er, bleibt die Pipeline rot; diese
rote Pipeline **ist** der Alarm, einen zusätzlichen Termin gibt es
bewusst nicht.
- **Dokumentation** ([ADR-0006](../../adr/0006-wikis-konsolidieren-docusaurus.md)):
Das gitops-Wiki liegt seit 2026-08-02 auf git.lab (*Wiki*-Reiter im
Projekt); ⚠️ der `wiki`-**Branch** im gitops-Repo ist ein überholter
Mai-Abzug von `docs/` und nicht die gepflegte Fassung. Alle Quellen
zusammen erscheinen unter **axionwiki.lab**
([`homelab/wiki`](https://git.lab/homelab/wiki), Docusaurus) —
Inhalte werden beim Bau geholt, **Änderungen gehören ins Quell-Repo**.
+148
View File
@@ -0,0 +1,148 @@
---
type: wiki-page
area: architecture
related: []
---
# DNS-Zone `axion1337.de` und Mail-Policy
Übergreifend, weil die Zone alle Hosts abdeckt und die Mail-Policy sich nicht pro
Host trennen lässt.
| | |
|---|---|
| **Registrar / DNS** | IONOS (`ns1098.ui-dns.biz`, `ns1022.ui-dns.com`, `ns1070.ui-dns.org`, `ns1059.ui-dns.de`) |
| **Apex** | `217.160.0.140` / `2001:8d8:100f:f000::2e9` — IONOS-Hosting, nicht eigene Infrastruktur |
| **Mail** | IONOS (`mx00.ionos.de`, `mx01.ionos.de`) |
| **Stand** | 2026-08-06 (Mail-Records gemessen) |
**Ist-Stand der Mail-Härtung** (gemessen 2026-08-06 über DoH, um den Lab-Resolver
zu umgehen):
| Name | `www` | MX | SPF | `_dmarc` | Bewertung |
|---|---|---|---|---|---|
| `rohana` | löst auf ❌ | gelöscht | **gelöscht** ⚠️ | fehlt | ⚠️ schwächer als vorher |
| `selendis` | weg ✅ | IONOS ❌ | `~all` ❌ | fehlt | unangetastet |
| `matrix` | löst auf ❌ | IONOS ❌ | `~all` ❌ | fehlt | offen |
| `game` | löst auf ❌ | — | — | fehlt | teilweise |
| **Apex** | legitim ✅ | IONOS (genutzt) | `~all` | **`p=none`** ⚠️ | siehe ZONE-02 |
Kein einziger Name trägt bisher `_dmarc`, alle erben damit `p=none` vom Apex.
> **Die Aufstellung ist nicht garantiert vollständig.** Zonentransfer ist verweigert,
> DNS erlaubt kein Enumerieren. Die Records unten stammen aus gezielten Abfragen und
> der IONOS-Oberfläche. Für eine vollständige Prüfung entweder die ungefilterte
> IONOS-Liste durchgehen oder die Certificate-Transparency-Logs abfragen (zeigt alle
> Namen, für die je ein Cert ausgestellt wurde).
## Bekannter Bestand
| Name | A | AAAA | Ziel |
|---|---|---|---|
| `axion1337.de` | `217.160.0.140` | `2001:8d8:100f:f000::2e9` | IONOS-Hosting |
| `www` | `217.160.0.140` | dito | IONOS-Hosting — hier ist `www` **legitim** |
| `rohana` | `188.245.193.243` | `2a01:4f8:c17:93eb::1` | CFGMON, Gitea |
| `selendis` | `188.245.193.243` | `2a01:4f8:c17:93eb::1` | CFGMON, Grafana |
| `game` | `157.90.155.206` | — | Pterodactyl |
| `matrix` | `49.13.132.245` | — | Matrix-Homeserver |
| `ftp` | `217.160.233.227` | `2001:8d8:1000:30f5:…` | IONOS-Default |
| `www.rohana`, `www.selendis`, `www.game`, `www.matrix` | wie ohne `www` | teils | überflüssig, siehe ZONE-01 |
Mail-Records existieren auf `selendis` und `matrix` (MX ×2, SPF, DKIM-CNAMEs,
`autodiscover`), auf `rohana` und `game` nicht.
## Was die Mail-Records eigentlich tun
Damit die Rezepte in [ZONE-01](https://git.lab/axion1337.chat/management/-/issues/5)
nicht als Zahlensalat dastehen: Vier Mechanismen, die zusammenspielen. **Keiner
schützt allein.**
### Das Grundproblem
Der Absender einer Mail (`From:`) ist frei wählbar — SMTP prüft ihn nicht. Jeder
kann `rechnung@rohana.axion1337.de` in den Umschlag schreiben. Die folgenden
Records sind die Möglichkeit, dem widersprechen, **bevor** jemand darauf hereinfällt.
### SPF — „diese Server dürfen für mich senden"
TXT-Record am Namen selbst. Listet die berechtigten Absender-IPs.
Entscheidend ist das **Ende** des Eintrags:
| Endung | Bedeutung | Wirkung beim Empfänger |
|---|---|---|
| `~all` | Softfail | „war nicht auf der Liste" → wird meist **trotzdem zugestellt**, evtl. markiert |
| `-all` | Hardfail | „war nicht auf der Liste" → **ablehnen** |
`v=spf1 -all` ohne jeden Server davor heißt: *Für diesen Namen sendet niemand.*
Genau die richtige Aussage für `rohana`, `selendis`, `matrix` — die verschicken keine
Mail (für `matrix` verifiziert in MATRIX-01: weder Synapse noch MAS senden).
⚠️ **Es darf nur EIN SPF-Record je Name existieren.** Ein zweiter erzeugt
`PermError`, und dann prüfen viele Empfänger **gar nicht mehr** — die Härtung
schlägt ins Gegenteil um. Deshalb: bestehenden TXT **editieren**, nie einen zweiten
anlegen.
### DKIM — „diese Mail wurde unterwegs nicht verändert"
Signatur mit einem privaten Schlüssel, öffentlicher Teil als CNAME/TXT im DNS
(`s1-ionos._domainkey…`). Beweist Unversehrtheit und Herkunft.
Für Namen, die nicht senden, sind die DKIM-Einträge schlicht **Ballast** — sie
signieren nichts. Sie können weg.
### DMARC — „und das tust du, wenn SPF oder DKIM nicht passen"
TXT unter `_dmarc.<name>`. SPF und DKIM stellen nur **fest**; DMARC sagt, welche
**Konsequenz** das hat:
| Policy | Wirkung |
|---|---|
| `p=none` | nur beobachten — **kein Schutz**, Mail wird zugestellt |
| `p=quarantine` | in den Spam-Ordner |
| `p=reject` | ablehnen |
⚠️ **DMARC wird vererbt.** Fehlt `_dmarc.rohana`, gilt die Policy des
organisatorischen Namens `axion1337.de`. Die steht heute auf **`p=none`**
([ZONE-02](https://git.lab/axion1337.chat/management/-/issues/6)) — **damit erben
alle Subdomains „kein Schutz"**, egal wie sauber ihr SPF ist.
Das ist der wichtigste Hebel der ganzen Zone: **Der Apex wirkt auf alle Namen
gleichzeitig.** Mit `sp=` lässt sich für Subdomains sogar eine strengere Policy
setzen als für den Apex selbst.
### Null-MX — „hier nimmt niemand Mail an" (RFC 7505)
Ein MX-Record mit dem Ziel `.` (Punkt) und Priorität 0.
Der Grund, warum **Löschen nicht reicht**: Findet ein Absender **keinen** MX,
weicht er per RFC 5321 auf den **A/AAAA-Record** aus und versucht, direkt an den
Webserver zuzustellen. Der Name sieht dann nach „nimmt vielleicht Mail an" aus.
Null-MX sagt stattdessen ausdrücklich *nein* — Absender brechen sofort ab.
### Warum die Kombination
| Record | Beantwortet die Frage |
|---|---|
| **Null-MX** | Nimmt dieser Name Mail **an**? → nein |
| **SPF `-all`** | Darf jemand für diesen Namen **senden**? → niemand |
| **DMARC `p=reject`** | Was tun, wenn doch jemand behauptet, es zu dürfen? → ablehnen |
Erst zusammen ergeben sie eine Aussage, auf die sich ein Empfänger verlassen kann.
Einzeln bleibt jeweils eine Lücke — und **ersatzloses Löschen** hinterlässt die
größte: keine Aussage ist schwächer als eine schlechte Aussage.
**Real eingetreten:** Bei `rohana` sind MX und SPF gelöscht, die Ersatz-Records
fehlen (gemessen 2026-08-06). Vorher gab es wenigstens ein Softfail-SPF, jetzt gar
keine Aussage mehr — dazu die geerbte `p=none` vom Apex. Der Halbfertig-Zustand ist
schwächer als der Ausgangszustand.
## Offene Punkte → git.lab-Issues
Seit dem Framework-Umbau (2026-08-01) leben offene Punkte als Issues im
[management-Projekt](https://git.lab/axion1337.chat/management/-/issues); die IDs bleiben in den Issue-Titeln erhalten.
Dieses File hält nur noch Bestand und Historie.
- [ZONE-01 — IONOS-Default-Records bereinigen (Rezepte im Issue; rohana/selendis in Arbeit)](https://git.lab/axion1337.chat/management/-/issues/5)
- [ZONE-02 — Apex-DMARC ist `p=none` und schützt nichts](https://git.lab/axion1337.chat/management/-/issues/6)
+100
View File
@@ -0,0 +1,100 @@
---
type: wiki-page
area: deployment
related: []
---
# Verfahren: Deploy-Übergabe
Für die Konstellation „einer baut, ein anderer rollt aus". Zweck ist nicht mehr
Prozess, sondern **weniger Rückfragen und weniger stille Fehlschläge**.
Eingeführt am 2026-08-01 nach dem Deploy der CVE-Pipeline (`gitops#47`), siehe
[aar/2026-08-01-cve-pipeline-gitops47.md](../../aar/2026-08-01-cve-pipeline-gitops47.md).
## Ablauf
1. Wer baut, öffnet **auf git.lab** ein Issue aus der Vorlage **Deploy-Übergabe**
(`.gitlab/issue_templates/Deploy-Übergabe.md`, im Feld *Description template*).
2. Wer ausrollt, arbeitet die Prüfliste unten ab und deployt.
3. Wer ausrollt, hängt den **AAR** als Kommentar an dasselbe Issue
(Vorlage: [docs/aar/template.md](../../aar/template.md)). Bei Befunden ab MEDIUM
zusätzlich als Datei unter `docs/aar/`.
## Die vier Punkte, die den Unterschied machen
### 1. Mengengerüst vor dem Deploy
Bei allem, was etwas erzeugt — Nachrichten, Alarme, Zeitreihen, Requests —
gehört die erwartete Anzahl beim **ersten** Lauf in die Übergabe. Geschätzt ist
in Ordnung, gemessen ist besser; welches von beidem, muss dabeistehen.
Der Grund: „ein Alarm pro Fund" ist eine völlig unauffällige Zeile im Code und
harmlos bei 5 Funden. Bei 126 ist es ein Ausfall. Der Unterschied steht nirgends
im Diff — er ergibt sich erst aus den Daten, gegen die das Ding läuft.
Eine Stichprobe reicht: drei repräsentative Elemente von 29 messen und
hochrechnen kostet Minuten und liefert die Größenordnung.
### 2. Verifikation dort, wo der Dienst liest
Ein grüner Linter belegt, dass eine Datei **gültig** ist — nicht, dass sie
**geladen** wurde. Diese beiden Aussagen sind bei Bind-Mounts, Caches und
Reload-Semantiken regelmäßig verschieden.
Also im Container prüfen, in der laufenden API, im tatsächlich geladenen
Regelwerk. Ein Blick auf die Platte beweist nichts über den Prozess.
### 3. Deploy-Kommando vollständig übergeben
Inklusive Reload-, Restart- und Recreate-Schritten. Ein `docker compose up -d`,
das `Running` meldet und dabei nichts aktiviert, ist der häufigste stille
Fehlschlag: kein Fehler, kein Log, falscher Zustand.
Konkret auf dem Monitoring-Stack (CFGMON): Einzeldatei-Mounts hängen am Inode,
`git pull` benennt beim Schreiben um und erzeugt damit einen neuen — der
Container zeigt danach weiter auf die alte Datei. Es braucht
`--force-recreate`. Details: `gitops#52`.
### 4. Zustellwege stumm schalten statt Deploy verschieben
Datensammlung und Außenwirkung lassen sich fast immer getrennt scharf schalten.
Wenn der Zustellweg das Risiko ist, wird **er** abgeklemmt — nicht der ganze
Deploy verschoben.
So läuft die Datensammlung ab sofort, das Dashboard steht, echte Zahlen
ersetzen die Schätzung, und die Entscheidung über die Zustellung fällt auf
Basis von Messwerten statt Vermutungen. Wichtig: die Stummschaltung gehört
committet und dokumentiert, sonst ist sie in zwei Wochen ein Rätsel.
## Prüfliste für den Ausrollenden
<!-- pruefe-prosa:ok (Checkliste des Verfahrens, keine offene Aufgabe) -->
- [ ] Diff gelesen, nicht nur die Beschreibung
- [ ] Mengengerüst plausibel? Bei Zweifel an einer Stichprobe selbst messen
- [ ] Configs mit den jeweiligen Werkzeugen validiert (`promtool`, `amtool`,
`compose config`, `py_compile` …)
- [ ] Außenwirkung identifiziert — was verlässt beim ersten Lauf das System?
- [ ] Nach dem Deploy **im Container** verifiziert, dass die neue Config aktiv ist
- [ ] Geprüft, ob vorher gesunde Dinge noch gesund sind (keine stille Regression)
- [ ] AAR geschrieben, Folge-Issues angelegt, Stummschaltungen dokumentiert
## Abgrenzung
Das Verfahren gilt für Übergaben zwischen Personen. Wer baut **und** ausrollt,
braucht kein Issue — der AAR lohnt trotzdem, sobald es Befunde ab MEDIUM gab.
## Kanonisierung nach CFGMON-Deploys (Topologie-Pflichtschritt)
CFGMON erreicht git.lab nicht — Commits aus Deploy-Sessions landen zwangsläufig
direkt auf dem Gitea-Mirror und werden vom nächsten Mirror-Lauf **kommentarlos
überschrieben** (zweimal passiert: dfe04c4→6ffab68 am 01.08. nachts,
2b715ca→0bd77e2 am 01.08. nachmittags). Deshalb gehört zu jeder Übergabe:
1. **CFGMON-Seite** vermerkt den Commit-Hash im Übergabe-Issue (Feld „Stand").
2. **Mac-Seite** kanonisiert zeitnah: Patch per
`https://rohana.axion1337.de/sorb/<repo>/commit/<sha>.patch` ziehen
(verwaiste Objekte bleiben eine Weile abrufbar), `git am`, Push nach git.lab.
Der Hash ändert sich dabei — **Autorschaft und Inhalt bleiben erhalten**.
3. **CFGMON** vor dem nächsten Pull: `git fetch && git reset --hard origin/main`
(inhaltsgleich, nur neuer Hash).
+41
View File
@@ -0,0 +1,41 @@
---
type: wiki-page
area: index
related: []
---
# Wiki-Index
Areas sind Ordner; leere Ordner werden nie angelegt — Struktur entsteht
mit Inhalt. Projektfassung des neckbeard-Index (Original:
`docs/sources/upstream/neckbeard-v0.1.1/`), erweitert um die Area
`vision` (Alt-Wert „eine Datei je Linie", ADR-0005).
## Areas
| Area | Enthält | Stand |
|---|---|---|
| `admin/` | Betrieb: [cfgmon](admin/cfgmon.md) · [game](admin/game.md) · [matrix](admin/matrix.md) · [overmind](admin/overmind.md) · [Refinement & Retro](admin/refinement.md) · [Stillstandsprüfung](admin/stillstandspruefung.md) · [Textbausteine](admin/textbloecke.md) | belegt |
| `deployment/` | [Deploy-Übergabe](deployment/deploy-uebergabe.md) (Definition of Done, Kanonisierungs-Verfahren) | belegt |
| `architecture/` | [Mirror-Topologie](architecture/mirror-topologie.md) · [Lab-Netz](architecture/lab-netzwerk.md) · [DNS-Zone](architecture/zone-axion1337.md) · [Branding](architecture/branding.md) | belegt |
| `vision/` | Eine Datei je Linie: [axion1337.chat](vision/axion1337-chat.md) · [Homelab](vision/homelab.md) · [ThreadNet](vision/threadnet.md) | belegt |
| `user-guide/` | Für Nicht-Owner | entfällt — Gate 0: Publikum ist Owner + Sessions |
| `requirements/` | Eigenständige Anforderungssicht | nur bei echtem Bedarf |
| `faq/`, `stolpersteine/` | Nur aus AARs und geschlossenen Issues geerntet — nie auf Vorrat | leer, entsteht im Refinement |
## Seitenregeln
- Jede Seite trägt Frontmatter per `schema.yaml` (`type: wiki-page`,
`area`, `related`, ggf. `sources`).
- `sources` zitiert, worauf die Seite fußt — Dateien unter
`docs/sources/` (unveränderlich, agentenschreibgeschützt) oder
externe URLs.
- Nur Standard-Markdown-Links, Diagramme als Mermaid (ADR-0003
upstream); keine Wikilinks.
- **Aufgaben gehören nicht ins Wiki:** offene Arbeitspunkte sind Issues
(`docs/issues/`), Wiki-Seiten verweisen höchstens darauf
(Lehre F-004; Prüfung: `scripts/pruefe_prosa.py`).
- Widersprüche werden aufgelöst oder ausdrücklich als Konflikt markiert
— nie stillschweigend nebeneinander stehen gelassen.
- Kein „Stand:"-Etikett in Seitenkörpern — `git log` beantwortet das
(Lehre F-010).
+44
View File
@@ -0,0 +1,44 @@
---
type: wiki-page
area: vision
related: []
---
# Vision: aXion1337.chat — die Community
> **Getragene Fassung** — geschärft im Struktur-Workshop am 2026-08-06 (#17).
> Leitplanke von sorb:
> **„axion1337.chat ist die Community, ThreadNet ist das Tool."**
## Kern
Eine selbst betriebene, souveräne Kommunikationsplattform für die eigene
Community: Matrix-Homeserver mit eigenem Client, eigener Identität (Authentik),
eigenen Regeln — unabhängig von Discord & Co.
## Was dazugehört (Stand heute)
- Matrix/ESS-Stack mit Voice/Video (Element Call, 1440p-Defaults)
- Gäste sollen unkompliziert, aber kontrolliert reinkommen
(Invite-Workflow gitops-Issue, Freischaltung im Matrix-Raum, `@concierge`)
- Community-Funktionen über Chat hinaus: Raidplaner mit Fotoalbum (HumHub-Kandidat)
- Sicherheit als Feature: ClamAV beidseitig, CVE-Transparenz im Security-Raum
## Zielgruppe und Größe
**Kontrolliert wachsend** (entschieden 2026-08-06). Offen für Neue, aber **jeder
Eintritt wird freigegeben** — genau das, was der Invite-Workflow mit `@concierge`
und befristeten Gast-Accounts baut. Wachstum ist erwünscht, aber gedeckelt durch
das, was eine Person moderieren kann; der Ein-Node-Stack setzt denselben Rahmen.
Verworfen: **geschlossener Kreis** (dann wäre der Invite-Workflow überdimensioniert)
und **offene Registrierung** (verlangt Moderationsteam und Kapazität, die es nicht
gibt).
Was daraus folgt: Der Invite-Workflow ist kein Nice-to-have, sondern das Mittel,
mit dem diese Entscheidung durchgesetzt wird. Freie Registrierung bleibt aus.
## Verhältnis zum ThreadNet-Branding
Nicht mehr offen: Das Rebranding wird in **M4 zu Ende gebracht**, nicht separat
terminiert — siehe [`threadnet.md`](threadnet.md).
+41
View File
@@ -0,0 +1,41 @@
---
type: wiki-page
area: vision
related: []
---
# Vision: Homelab — die Plattform
> **Getragene Fassung** — geschärft im Struktur-Workshop am 2026-08-06 (#17).
## Kern
Das Lab (MorninglightMountain, Overmind, das Netz dazwischen) ist die
**Quelle der Wahrheit** und die Werkbank: kanonische Repos, Build-CI,
Verwaltung — alles, was nicht öffentlich erreichbar sein muss, lebt hier
(ADR-0001/0002).
## Prinzipien
- Prod hängt nie vom Lab ab: Hetzner-Hosts laufen bei Lab-Ausfall weiter,
nur Neues pausiert.
- Verbindungen ins Lab sind schaltbar und minimal (Roadwarrior für Menschen,
Site-to-Site für Server — ADR-0004), nie dauerhaft exponiert.
- Bestand wird dokumentiert, wo er lebt: [homelab/docs](https://git.lab/homelab/docs)
für Netz/Geräte, Betriebs-Repos je Host.
## Ausbaustufen
**Konsolidieren vor Ausbauen** (entschieden 2026-08-06). Kein neuer Dienst und
kein weiterer Host, solange die Absicherung des Bestehenden nicht steht.
Der Maßstab ist nicht „läuft es", sondern „überlebt es den Verlust der Maschine,
auf der es läuft". Ein Lab, das Quelle der Wahrheit ist, hat diese Frage zuerst zu
beantworten — jeder zusätzliche Dienst vergrößert sonst die Angriffsfläche
schneller als die Kontrolle.
**Nachtrag sorb (2026-08-06):** Die Git-Daten werden bereits in einen S3-Bucket
auf sein NAS gesichert. Das entschärft den ursprünglichen Befund aus
[#10](https://git.lab/axion1337.chat/management/-/issues/10) — offen bleibt
jedoch, was diese Sicherung **nicht** umfasst (Registry-Blobs, npm-Pakete,
Gitea-Datenbank). Siehe dort.
+52
View File
@@ -0,0 +1,52 @@
---
type: wiki-page
area: vision
related: []
---
# Vision: ThreadNet — das Tool
> **Getragene Fassung** — geschärft im Struktur-Workshop am 2026-08-06 (#17).
> Leitplanke: ThreadNet ist die Produkt-/Tool-Linie, axion1337.chat der Betrieb.
## Kern
Die Software-Artefakte, die die Community tragen — als eigenständige,
wiederverwendbare Produkte gedacht: ThreadNet-Web (Element-Web-Fork mit
Discord-artiger Raumliste), threadnet-call (Call-Fork), thread-net-git,
threadnet-operating.
## Prinzipien
- **Reproduzierbar für Dritte:** Deployment-Arbeit so bauen, dass eine andere
Community den Stack forken kann (stehendes Ziel von sorb, 2026-07-30) —
Instanzwerte getrennt von generischer Struktur.
- Fork-Pflege mit kleinem Delta: Upstream-Merges müssen billig bleiben,
chirurgische Patches statt Umbauten.
- CI beweist Releases: Tag → Pipeline → Artefakt, keine Handbuilds.
## Veröffentlichungsgrad
**Die Forks werden öffentlich** (entschieden 2026-08-06) — aber erst nach einem
**History-Audit**, und in dieser Reihenfolge: zuerst gitops und Doku, die Clients
später.
Der Grund für die Entscheidung steckt im Prinzip oben: „reproduzierbar für Dritte"
zahlt sich nur öffentlich aus. Bliebe alles privat, würde dauerhaft für einen Zweck
gebaut, den es nicht gibt — dann hätte das Prinzip gestrichen gehört.
⚠️ **Der Audit ist Bedingung, nicht Formsache.** In der Historie des gitops-Repos
steht Commit `51ea513` — *„remove plaintext TURN shared secret, rotate leaked
value"*. Der Wert ist rotiert und damit wertlos, aber er steht weiterhin in der
Historie, und er ist vermutlich nicht der einzige Fund. Vor dem Umschalten auf
public: Historie aller zu veröffentlichenden Repos auf Klartext-Geheimnisse prüfen
und entscheiden, ob bereinigt (History-Rewrite) oder bewusst akzeptiert wird.
## Rebranding
**Wird in M4 zu Ende gebracht** (entschieden 2026-08-06), ohne eigenen Termin.
Begründung: Halbfertig ist der schlechteste Zustand — der Desktop-Client heißt
seit `6b0261d` ThreadNet und trägt die eigene Marke, der Web-Client zeigt weiter
Element. Der Rest gehört zusammen mit Signing und Installer-Branding in
„Produktreife ThreadNet" (ThreadNet-Web#6, #7, #10).