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:
co-authored by
Claude Fable 5
parent
70e81e2ff1
commit
92b448fe30
@@ -1,25 +0,0 @@
|
||||
# Verfahren
|
||||
|
||||
Wiederkehrende Abläufe zwischen Personen und Hosts — dort festgehalten, wo sie
|
||||
nicht an einem einzelnen Projekt-Repo hängen.
|
||||
|
||||
| Datei | Inhalt |
|
||||
|---|---|
|
||||
| [deploy-uebergabe.md](deploy-uebergabe.md) | Ablauf und Prüfliste für „einer baut, ein anderer rollt aus" |
|
||||
| [aar-vorlage.md](aar-vorlage.md) | Vorlage für den After Action Report nach einem Deploy |
|
||||
| [aar/](aar/) | Abgelegte AARs, benannt `JJJJ-MM-TT-<vorhaben>.md` |
|
||||
|
||||
[`textbloecke.md`](textbloecke.md) hält kurze, kopierbare Blöcke, die man einer
|
||||
Session voranstellt — sie verweisen auf die Konventionen, statt sie zu wiederholen.
|
||||
**Baustein 4 (Abschluss) ist zugleich die Definition of Done für Änderungen ohne
|
||||
Deploy**; für Deployments gilt [deploy-uebergabe.md](deploy-uebergabe.md).
|
||||
|
||||
Die zugehörige Issue-Vorlage liegt unter
|
||||
`.gitlab/issue_templates/Deploy-Übergabe.md` und erscheint beim Anlegen eines
|
||||
Issues in diesem Repo im Auswahlfeld *Description template* als
|
||||
**Deploy-Übergabe**.
|
||||
|
||||
Abgrenzung zum Rest des Repos: `hosts/` und `shared/` halten **offene Punkte**,
|
||||
dieses Verzeichnis hält **wie wir arbeiten**. Ein Verfahren wird hier nur
|
||||
aufgenommen, wenn es mindestens einmal an einem echten Vorfall gescheitert
|
||||
oder bewährt ist — der auslösende AAR wird jeweils verlinkt.
|
||||
@@ -1,45 +0,0 @@
|
||||
# AAR — <Vorhaben> <Issue-Referenz>
|
||||
|
||||
**Datum:** JJJJ-MM-TT · **Host/Stack:** … · **Auftrag:** …
|
||||
|
||||
## 1. Ergebnis
|
||||
|
||||
Was ist live und verifiziert. Was ist bewusst **nicht** live, und warum.
|
||||
Diese Trennung steht ganz oben — sie ist die einzige Angabe, die jemand
|
||||
braucht, der nur eine Zeile liest.
|
||||
|
||||
## 2. Befunde
|
||||
|
||||
| # | Befund | Schwere | Status |
|
||||
|---|---|---|---|
|
||||
| 1 | … | HIGH / MEDIUM / LOW | abgefangen / behoben / notiert · Issue |
|
||||
|
||||
Schwere nach Auswirkung, nicht nach Aufwand. Zu jedem Befund gehört, **wie er
|
||||
sichtbar wurde** — falls das nicht offensichtlich ist, siehe Abschnitt 4.
|
||||
|
||||
## 3. Verdachtsfälle mit Entwarnung
|
||||
|
||||
Was geprüft und **nicht** bestätigt wurde, mit dem Messwert. Genauso wichtig
|
||||
wie die Befunde: verhindert, dass dieselbe Vermutung beim nächsten Mal erneut
|
||||
Zeit kostet.
|
||||
|
||||
## 4. Was die Befunde ermöglicht hat
|
||||
|
||||
Die Methode, nicht die Chronologie. Wenn ein Befund nur durch eine bestimmte
|
||||
Prüfung sichtbar wurde, gehört genau die hierher — das ist der Teil, der beim
|
||||
nächsten Deploy wiederverwendbar ist.
|
||||
|
||||
## 5. Offen
|
||||
|
||||
Was bleibt, wer entscheidet, was blockiert. Mit Issue-Referenz statt
|
||||
wiederholtem Inhalt.
|
||||
|
||||
---
|
||||
|
||||
Regeln:
|
||||
|
||||
- Nicht verifizierte Aussagen als solche kennzeichnen — eine Vermutung, die wie
|
||||
ein Befund aussieht, kostet später mehr Zeit als sie spart (Konvention aus
|
||||
der [README](../README.md)).
|
||||
- Zahlen statt Adjektive. „Viele Alarme" ist keine Angabe, „126 CRITICAL" ist eine.
|
||||
- Kurz. Ein AAR, der nicht gelesen wird, hat keinen Wert.
|
||||
@@ -1,66 +0,0 @@
|
||||
# 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](../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).
|
||||
@@ -1,185 +0,0 @@
|
||||
# AAR — LABNET-02, CFGMON-Seite (Übergabe `sorb/management#2`)
|
||||
|
||||
**Datum:** 2026-08-01 · **Host/Stack:** CFGMON, WireGuard-Client gegen UDM
|
||||
**Auftrag:** Schritte 1–3 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](../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 1–7 (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 1–7, Schlüsselrotation
|
||||
und das Repo-Zuhause der Config.
|
||||
@@ -1,80 +0,0 @@
|
||||
# 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 1–7 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 1–7 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.
|
||||
@@ -1,151 +0,0 @@
|
||||
# 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`](../../shared/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`).
|
||||
@@ -1,100 +0,0 @@
|
||||
# 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
|
||||
@@ -1,105 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,93 +0,0 @@
|
||||
# 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: [aar-vorlage.md](aar-vorlage.md)). Bei Befunden ab MEDIUM
|
||||
zusätzlich als Datei unter [aar/](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
|
||||
|
||||
- [ ] 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).
|
||||
@@ -1,47 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,94 +0,0 @@
|
||||
# Refinement und Retro — die Termine des Frameworks
|
||||
|
||||
Kanban braucht wenige, aber verlässliche Termine, sonst verkommt das Board zur
|
||||
Ablage. Festgelegt in [ADR-0005](../docs/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, 30–45 min |
|
||||
| **Retro light** | im **ersten Refinement des Monats**, +20–30 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, 30–45 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, 20–30 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 [`retro/`](retro/) abgelegt, eine Datei je Termin. Die
|
||||
erste: [2026-08-09](retro/2026-08-09.md).
|
||||
|
||||
## AAR (anlassbezogen)
|
||||
|
||||
Nach jedem Deploy mit Übergabe und nach jedem Incident, Vorlage in
|
||||
[aar-vorlage.md](aar-vorlage.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`](../CLAUDE.md) und
|
||||
sind über den Gitea-Mirror von überall lesbar.
|
||||
- Arbeit zwischen Sessions läuft über das
|
||||
[Deploy-Übergabe-Verfahren](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.
|
||||
@@ -1,126 +0,0 @@
|
||||
# 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
|
||||
@@ -1,71 +0,0 @@
|
||||
# Stillstandsprüfung
|
||||
|
||||
Sucht Dinge, die **leise aufgehört haben zu funktionieren**. Beschlossen in der
|
||||
[Retro 2026-08-09](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.
|
||||
@@ -1,116 +0,0 @@
|
||||
# Textbausteine für Sessions
|
||||
|
||||
Kurze, kopierbare Blöcke, die man einer Claude-/Agenten-Session voranstellt.
|
||||
|
||||
Die Konventionen stehen kanonisch in [`CLAUDE.md`](../CLAUDE.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](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:
|
||||
2–4 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; 3–5 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.
|
||||
Reference in New Issue
Block a user