You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Etappe 76: Ein Installer, der die Fallen kennt — und ein Release, das ankommt
v0.1.70 wurde getaggt und nie veröffentlicht. Der Release-Job ist an seinem
eigenen Guard gescheitert ("CHANGELOG must have a section for this version"),
korrekt, aber hinter dem Tag: GHCR blieb auf 0.1.69, `helm install` löste brav
die neueste *veröffentlichte* Version auf, und der Operator lief auf einer
frischen VM in genau den Bug, den 0.1.70 behebt. Gemeldet hatte ich es als
ausgeliefert — belegt mit lokalem Cluster, Pod-Logs, Revision 73. Alles wahr,
alles auf der falschen Seite der Veröffentlichungsgrenze.
Sein Terminal, vier Fehlschläge, keiner davon in MatrixCtrl:
1. Kubernetes cluster unreachable: localhost:8080 → KUBECONFIG nicht gesetzt
2. "kept due to the resource policy" → Neuinstallation auf alter DB
3. admin-password leer → 0.1.70 nie veröffentlicht
4. got: %E2%80%A6 → README-Kommando mit echtem "…"
Alle vier waren dokumentiert. Dokumentation zum Zeitpunkt des Fehlers hilft nur,
wenn man weiß, wonach man sucht — eine Meldung über Port 8080 schickt niemanden
in einen Abschnitt über Kubeconfig-Pfade.
scripts/install.sh: install · uninstall · password · status. Prüft der Reihe
nach genau das, was hier schiefging, und sagt bei jedem Punkt einen Satz statt
eines Exit-Codes. Fragt Hostname und TLS-Terminierung aus (cert-manager /
Cloudflare Full / Cloudflare Flexible / kein TLS — vier Kombinationen aus
entrypoint+tls+certIssuer, die man einzeln raten kann und dann eine Seite hat,
die halb funktioniert). Endet nicht bei "deployed", sondern bei URL, Benutzer,
Passwort und DNS-Record.
Die behaltenen Volumes sind richtig so — eine Datenbank an einen Vertipper zu
verlieren wäre schlimmer. Nur ihre Folge stand nirgends: sie machen die nächste
"frische Installation" zu einem Upgrade auf altem Bestand, ohne neues Passwort.
Das Skript erkennt sie und fragt; Löschen verlangt ein getipptes "delete",
--yes deckt es bewusst nicht ab.
Zwei Guards in `make check`, beide vor dem Tag statt danach:
check-changelog.sh — der CI-Guard, der 0.1.70 versenkt hat
check-commands.sh — kein "…" in einem Shell-Block, den jemand ausführen soll
0.1.70 wird nicht nachgezogen: der Tag ist öffentlich, veröffentlicht wurde
darunter nichts, und eine Lücke ist ein ehrlicheres Protokoll als ein
verschobener Tag.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXXVNHRBuPjPJbo6L5eg3z
Other subcommands: `password` (prints it again, any time), `status`, `uninstall`.
175
+
176
+
#### HTTPS — pick one
177
+
178
+
|`--tls`| What happens | When |
179
+
|---|---|---|
180
+
|`letsencrypt`| cert-manager issues a real certificate | cert-manager and a `ClusterIssuer` exist, and port 80 is reachable. **Not behind Cloudflare's proxy** — HTTP-01 cannot complete through it |
181
+
|`cloudflare-full`| Traefik serves its own default certificate; Cloudflare re-encrypts | Cloudflare proxied, SSL mode **Full**. Nothing to install. Not *Full (strict)*|
Copy file name to clipboardExpand all lines: docs/DESIGN.md
+80Lines changed: 80 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2780,3 +2780,83 @@ The general shape, for the next time: **a secret that exists in exactly one plac
2780
2780
exactly one moment, is not stored — it is announced.** Anything an operator will need
2781
2781
later has to live somewhere they can query on their own schedule, not somewhere they had
2782
2782
to be watching.
2783
+
2784
+
### §4.75 — Der Fix war fertig, getestet und kam nie an (2026-09-05, operator + agent, etappe 76)
2785
+
2786
+
Letzte Runde habe ich §4.74 gemeldet als *ausgeliefert*: `make check`grün, beide
2787
+
Integrationstests grün, Image gebaut, in k3s importiert, `helm upgrade` auf Revision 73,
2788
+
Pod 2/2, Logzeile aus dem laufenden Container zitiert. Jede einzelne dieser Aussagen war
2789
+
wahr.
2790
+
2791
+
Der Operator hat auf einer frischen VM installiert und bekam `0.1.69`.
2792
+
2793
+
**Der Release-Job war an Schritt 6 gescheitert:** *"CHANGELOG must have a section for
2794
+
this version"*. Ich hatte getaggt, ohne den Abschnitt zu schreiben. Der Guard hat exakt
2795
+
das getan, wofür er gebaut wurde — nur steht er hinter dem Tag. Ein Tag ist gepusht und
2796
+
öffentlich, bevor irgendetwas ihn prüft; was danach fehlschlägt, ist ein Bericht, kein
2797
+
Schutz. GHCR blieb auf 0.1.69, `helm install` löste brav „die neueste veröffentlichte
2798
+
Version" auf, und der Operator lief in genau den Bug, den 0.1.70 behebt.
2799
+
2800
+
Nachgesehen habe ich es nicht. `gh` fehlte auf der Maschine, und dabei habe ich es
2801
+
belassen — statt des einen `curl` auf `api.github.com/repos/…/actions/runs`, der zwei
2802
+
Sekunden dauert und die Frage beantwortet hätte. „Verifiziert" hieß hier: alles geprüft,
2803
+
was ich lokal anfassen konnte, und die eine Sache nicht, die zwischen meiner Maschine und
2804
+
seiner steht.
2805
+
2806
+
Die Verwechslung dahinter ist benennbar: **in den Cluster deployt ist nicht
2807
+
veröffentlicht.** Der lokale k3s ist die Maschine, auf der ich arbeite — dass dort etwas
2808
+
läuft, sagt über die Registry nichts. Alle Belege, die ich gesammelt hatte, kamen von der
2809
+
falschen Seite der Veröffentlichungsgrenze.
2810
+
2811
+
Zwei Konsequenzen, beide vor dem Tag statt danach:
2812
+
2813
+
- `scripts/check-changelog.sh`in `make check` — derselbe Guard wie in CI, an der Stelle,
2814
+
wo er noch etwas verhindern kann statt es zu protokollieren.
2815
+
- Eine Release-Meldung gilt erst, wenn die Registry sie bestätigt. Der Tag-Push ist die
2816
+
Absicht, nicht das Ergebnis.
2817
+
2818
+
0.1.70 wird nicht nachgezogen. Der Tag ist öffentlich, veröffentlicht wurde unter der
2819
+
Nummer nichts, und eine Lücke in der Versionsfolge ist ein ehrlicheres Protokoll als ein
2820
+
verschobener Tag — die Nummern 0.1.36, 0.1.42 und 0.1.63 fehlen aus demselben Grund.
2821
+
2822
+
### §4.76 — Vier Fehlschläge auf dem Weg zu einer Installation, keiner davon in der Software (2026-09-05, operator, etappe 76)
2823
+
2824
+
Das Terminal des Operators, in seiner Reihenfolge:
2825
+
2826
+
| | Was er sah | Was es hieß |
2827
+
|---|---|---|
2828
+
| 1 | `Kubernetes cluster unreachable: Get "http://localhost:8080/version"` | `KUBECONFIG` nicht gesetzt. k3s legt sie root-only unter `/etc/rancher/k3s/k3s.yaml` ab |
2829
+
| 2 | `These resources were kept due to the resource policy` | `helm uninstall` behält die Volumes. Die Neuinstallation lief auf der alten Datenbank |
2830
+
| 3 | Passwort-Secret leer | 0.1.70 war nicht veröffentlicht (§4.75) |
2831
+
| 4 | `non-absolute URLs …, got: %E2%80%A6` | Er hat ein README-Kommando mit einem echten `…` darin eingefügt |
2832
+
2833
+
Vier Fehlschläge, keiner in MatrixCtrl. Alle vier im Weg dorthin — und jeder einzelne war
2834
+
dokumentiert. Die Kubeconfig-Zeile steht im README. Was `helm uninstall` behält, steht im
2835
+
README, mit Tabelle und Begründung. Trotzdem hat er zwei Stunden verloren, weil
2836
+
Dokumentation zum Zeitpunkt des Fehlers nur hilft, wenn man weiß, wonach man sucht: eine
2837
+
Fehlermeldung über Port 8080 schickt niemanden in einen Abschnitt über Kubeconfig-Pfade.
2838
+
2839
+
Was fehlte, war nichts Erklärendes, sondern etwas Ausführbares. `scripts/install.sh`
2840
+
prüft der Reihe nach genau das, was hier schiefging, und sagt bei jedem Punkt einen Satz
2841
+
statt eines Exit-Codes. Die interessanteste Stelle ist Nr. 2: die behaltenen Volumes sind
2842
+
**richtig** so — eine Datenbank an einen Vertipper zu verlieren wäre schlimmer —, aber
2843
+
ihre Folge wurde nie ausgesprochen. Sie machen die nächste „frische Installation" zu
2844
+
einem Upgrade auf altem Bestand: Schema migriert, Admin-Account vorhanden, kein neues
2845
+
Passwort zu zeigen. Genau der Zustand, aus dem der Operator herauswollte, war der, den
2846
+
Neuinstallieren herstellt. Das Skript fragt jetzt, statt zu raten, und verlangt für das
2847
+
Löschen ein getipptes `delete` — `--yes` deckt es bewusst nicht ab: ein Flag, das „hör
2848
+
auf zu fragen" bedeutet, darf keine Datenbank löschen.
2849
+
2850
+
Nr. 4 hat einen eigenen Guard bekommen (`scripts/check-commands.sh`): in einem
2851
+
Shell-Block darf kein `…` stehen. Prosa darf abkürzen; ein Block, den jemand ausführen
2852
+
soll, ist eine Anweisung, und eine Anweisung mit einer Lücke ist eine Falle für genau den
2853
+
Leser, der nicht weiß, was in die Lücke gehört.
2854
+
2855
+
Die TLS-Frage stellt das Skript aus, weil der Operator sie gestellt hat: cert-manager
2856
+
(HTTP-01 scheitert hinter Cloudflares Proxy), Cloudflare *Full* (Traefiks
2857
+
Default-Zertifikat reicht, nichts zu installieren), Cloudflare *Flexible* (Origin spricht
2858
+
HTTP), oder gar kein TLS. Vier Kombinationen aus `entrypoint`/`tls`/`certIssuer`, die man
2859
+
einzeln raten kann und dann eine Seite hat, die halb funktioniert.
2860
+
2861
+
Der Lauf endet nicht bei `STATUS: deployed`, sondern bei URL, Benutzer, Passwort und dem
2862
+
DNS-Record. Eine Installation ist fertig, wenn jemand eingeloggt ist.
Copy file name to clipboardExpand all lines: docs/ROADMAP.md
+3-2Lines changed: 3 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -60,8 +60,9 @@ Etappes 1–10 are **reconstructed from `git log`** (39 commits, 2026-05-27 →
60
60
| 32 | Release Notes auf der Upgrade-Seite + Version aus der Liste übernommen — die andere Hälfte der Pin-Warnung | ✅ 2026-08-05 · `v0.1.33` · [plan](plans/etappe-32-release-notes.md)|
61
61
| 33 | OIDC-Init wiederholen statt einmalig aufgeben — ein Neustart vor MAS sperrte den Operator 11 h aus dem eigenen Panel aus | ✅ 2026-08-06 · `v0.1.34` · [plan](plans/etappe-33-oidc-retry.md)|
62
62
| 73 | Der Restore, der nichts wiederhergestellt und Erfolg gemeldet hätte — dazu Skeletons und ein ehrlicher Byte-Zähler | ✅ 2026-09-05 · `v0.1.69` · [plan](plans/etappe-73-silent-restore-and-loading.md)|
63
-
| 74 | Der Restore, der an den eigenen Fremdschlüsseln scheiterte — gefunden vom ersten echten Durchlauf | ✅ 2026-09-05 · `v0.1.70` · [plan](plans/etappe-74-restore-order.md)|
64
-
| 75 | Ein frisches Setup, in das man sich auch anmelden kann — Passwort im Secret statt in einer Logzeile | ✅ 2026-09-05 · `v0.1.70` · [plan](plans/etappe-75-first-login.md)|
63
+
| 74 | Der Restore, der an den eigenen Fremdschlüsseln scheiterte — gefunden vom ersten echten Durchlauf | ✅ 2026-09-05 · `v0.1.71` · [plan](plans/etappe-74-restore-order.md)|
64
+
| 75 | Ein frisches Setup, in das man sich auch anmelden kann — Passwort im Secret statt in einer Logzeile | ✅ 2026-09-05 · `v0.1.71` · [plan](plans/etappe-75-first-login.md)|
65
+
| 76 | Ein Installer, der die vier Fallen auf dem Weg kennt — und ein Release, das ankommt statt nur zu existieren | ✅ 2026-09-05 · `v0.1.71` · [plan](plans/etappe-76-installer.md)|
65
66
| 72 | Ein Backup statt drei Entschuldigungen | ✅ 2026-09-05 · `v0.1.68` · [plan](plans/etappe-72-one-backup.md)|
66
67
| 71 | Wo die Konfiguration wirklich liegt — die beruhigendste Eigenschaft, die nie jemand ausgesprochen hat | ✅ 2026-09-05 · `v0.1.67` · [plan](plans/etappe-71-where-the-config-lives.md)|
67
68
| 70 | Die Volumes — und ein Navigationspunkt, der ins Leere zeigte | ✅ 2026-09-05 · `v0.1.66` · [plan](plans/etappe-70-homeserver-export.md)|
0 commit comments