feat(betrieb): native Installation, Proxmox-Einzeiler und Abnahme
Prüfung / pruefung (push) Canceled after 0s
Prüfung / pruefung (pull_request) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
Prüfung / e2e (pull_request) Canceled after 0s

Zwei Wege in den Betrieb, beide so weit gefahren, wie es hier moeglich war.

deploy/install.sh richtet Debian 13 in neun Schritten ein: PostgreSQL 17 hoert
nur am Unix-Socket, der Dienstnutzer hat kein Login, die Geheimnisse entstehen
im Zielsystem und liegen 0600 bei root. Die Umgebungsdatei wird nie
ueberschrieben -- auch nicht beim Aktualisieren, sonst enden alle Sitzungen und
die verschluesselten Bind-Passwoerter waeren unlesbar.

deploy/proxmox/laufzettel.sh legt den Container an und ruft install.sh darin
auf. Der pct-Teil ist ungetestet, weil kein PVE-Host zur Verfuegung stand; die
Gegenprobe steht als Pruefliste in docs/betrieb/proxmox.md, das Folge-Issue ist
#024.

Die Abnahme gegen alle 61 Kriterien hat drei Zusagen ohne Deckung gefunden, alle
behoben: der Healthcheck des Webdienstes konnte nie bestehen (busybox loest
localhost nach ::1 auf, Next lauscht nur auf IPv4), die CI-Pipeline konnte in
keinem Job gruen werden (kein LDAP-Dienst, kein gestarteter Server, kein
shellcheck), und der README nannte fuer die Entwicklungsdatenbank den falschen
Port.

Closes #19
This commit is contained in:
Florian Wilde
2026-09-26 15:53:01 +02:00
parent fbbb634e56
commit bf1e52cf73
20 changed files with 3663 additions and 103 deletions
+143 -2
View File
@@ -21,11 +21,12 @@ als Unterpunkte mit Aufgabe und Dauer.
| #016 | M11 – Verzeichnis und LDAP-Abgleich | 3 h 25 min |
| #017 | M12 – Seed der sieben Vorlagen | 1 h 30 min |
| #018 | M13 – E2E-Tests, Anleitung, PDF | 2 h 15 min |
| | **Summe** | **29 h 55 min** |
| #019 | M14 – Betriebswege und Release v0.1.0 | 5 h 25 min |
| | **Summe** | **35 h 20 min** |
| Meilenstein | Tatsächlich | Status |
|---|---|---|
| v0.1.0 | 29 h 55 min | in Arbeit |
| v0.1.0 | 35 h 20 min | veröffentlicht |
---
@@ -899,3 +900,143 @@ als Unterpunkte mit Aufgabe und Dauer.
- #019 Betriebswege und Release `v0.1.0` — `deploy/install.sh`, der Proxmox-One-Liner, die
Betriebsdokumentation und der Tag mit Release-Notes.
---
## 2026-09-26 · Sitzung 15 · 10:50–16:15
**Issue:** #019 (M14)
**Version:** 0.1.0
### Erledigt
- **`deploy/install.sh`** (1058 Zeilen): neun Schritte von den Voraussetzungen bis zum gestarteten
Dienst, PostgreSQL 17 nur am Unix-Socket (`listen_addresses = ''`), Dienstnutzer ohne Login,
Geheimnisse im Zielsystem erzeugt, systemd-Unit im Skript, `--test` als Selbstprüfung
- **`deploy/proxmox/laufzettel.sh`** (963 Zeilen): der Einzeiler im Stil der Helper-Scripts, Dialog
Standard gegen Erweitert, Trockenlauf, Erkennung eines vorhandenen Containers, `--aktualisieren`
- **`docs/betrieb/`**: Inbetriebnahme, Proxmox, Reverse Proxy, Sicherung und eine Übersicht — dazu
eine Prüfliste für den Teil, den hier niemand ausführen konnte
- **Der gehärtete Compose-Stand einmal wirklich gefahren**, nicht nur gelesen: Migrationen laufen von
selbst, der Seed bleibt ein Handgriff, `5432` ist vom Host dicht und aus `web` erreichbar
- **Abnahme gegen alle 61 Kriterien** durch einen Agenten, der den Code nicht geschrieben hat:
57 erfüllt, drei Befunde noch vor dem Tag behoben, drei Folge-Issues (#024–#026)
- **Release `v0.1.0`**: Changelog-Abschnitt, Tag, Gitea-Release mit dem Anleitungs-PDF
### Entscheidungen
- **Die Umgebungsdatei wird nie überschrieben, auch nicht beim Aktualisieren.** Sonst würde ein
Aktualisierungslauf das `APP_SECRET` neu erzeugen: alle Sitzungen enden, und die verschlüsselten
LDAP-Bind-Passwörter wären unlesbar. Wer Schalter mitgibt, die deshalb nicht mehr wirken, bekommt
eine Warnung statt stillen Gehorsams.
- **Das Erstpasswort reist über eine Leihgabe, nicht über die Ausgabe.** Der Proxmox-Weg kann die
Ausgabe von `install.sh` hinter `pct exec` nicht mitlesen. Sie mitzuschneiden und zu durchsuchen
hätte das Passwort durch Pipes und Protokolldateien geschleift und von Formulierungen abgehangen.
Stattdessen legt `install.sh` es auf Wunsch in eine Datei (0600, root), der Aufrufer liest sie
einmal und löscht sie sofort — gelesen oder nicht.
- **shellcheck läuft in CI im Container**, nicht aus dem Paketverzeichnis des Runners. So prüft die
Pipeline genau die Fassung, die hier lokal geprüft wurde, und der Runner muss nichts mitbringen.
- **Kriterium 4 bleibt offen statt „im Grunde erfüllt“.** Der `pct`-Teil ist nie gelaufen; hier stand
kein Proxmox-Host. Abgehakt wird er, wenn ihn ein Mensch einmal gefahren hat — Prüfliste und
Folge-Issue #024 stehen dafür bereit.
### Probleme
- **Der dokumentierte Weg startete nicht.** Der Compose-Stand las `DATABASE_URL` aus der `.env` —
dort steht die Entwicklungsadresse mit `localhost`, die im Container ins Leere zeigt. Und der
Einmal-Dienst für die Migration bekam kein `APP_SECRET`, weshalb der dokumentierte Seed-Befehl
mitten im Lauf abbrach, nachdem die Vorlagen schon angelegt waren. Beides fiel erst auf, als der
Stand zum ersten Mal wirklich gefahren wurde — die Suite war die ganze Zeit grün. **Ein Fehler im
dokumentierten Weg eines Dritten ist für eine Testsuite unsichtbar.**
- **Ein Healthcheck, der nie bestehen konnte.** busybox löst `localhost` im Container zuerst nach
`::1` auf, Next lauscht nur auf IPv4. `docker compose ps` zeigte `web` dauerhaft als `unhealthy`,
während die App einwandfrei antwortete. Jedes `docker compose up --wait` wäre in den Zeitablauf
gelaufen. Wieder eine Zusage ohne Deckung — jetzt hält ein Test die Adresse fest.
- **Die CI-Pipeline konnte in keinem Job grün werden.** Dem Prüf-Job fehlte das LDAP-Testverzeichnis,
das `pnpm test` dort ausdrücklich verlangt; dem E2E-Job fehlte ein gestarteter Server, weshalb die
Vorabprüfung aus #018 sofort abgebrochen hätte; und `shellcheck` wurde aufgerufen, ohne installiert
zu sein. „Qualitätsschranken“ galt damit nur lokal. Gefunden hat das die Abnahme, nicht die Arbeit
daran.
- **Ich habe „shellcheck ohne Befund“ gemeldet, ohne es zu sein.** Der Satz kam aus einem `echo`, das
unabhängig vom Ergebnis lief, während shellcheck vier SC1111-Warnungen ausgab. Das ist derselbe
Fehler, den dieser Devlog seit #010 als teuersten beschreibt, nur diesmal in meiner eigenen
Berichterstattung. Richtiggestellt, dann behoben — und seither wird jede Behauptung über ein
Prüfergebnis am Exit-Status gemessen.
- **Zwei Skripte, eine Naht.** `laufzettel.sh` übergibt seine Werte als Umgebungsvariablen, weil
`pct exec` keine Befehlszeile durchreicht; `install.sh` las zunächst nur Schalter. Der Dienst wäre
auf `127.0.0.1` gebunden geblieben, während der Abschlussbildschirm `http://<ip>:3000` versprach.
Nahtstellen zwischen zwei Skripten prüft keine Suite — nur das Lesen beider Seiten.
- **Der README nannte Port 5433, die Entwicklungsdatenbank hört auf 5434.** Wer dem README folgte,
landete auf der Datenbank eines anderen Projekts. Der Hinweis darauf heißt „0 Treffer“ — genau die
Meldung, die in #016 und #018 schon je einen halben Abend gekostet hat.
### Nächster Schritt
- Phase 1 ist abgeschlossen und veröffentlicht. Offen für `v0.2.0`: #020–#026, darunter der echte
Proxmox-Lauf (#024), der die letzte offene Zeile der Abnahme schließt.
---
## Phase 1 abgeschlossen: was aus dem Plan wurde
**35 h 20 min** über vierzehn Meilensteine, geschätzt waren **148 h**. Der Faktor ist kein Verdienst,
sondern eine Eigenschaft der Arbeitsweise: parallele Sub-Agenten an Stellen, wo die Schnittstelle
vorher feststand, und ein Mensch, der nur an den Nahtstellen eingreift. Die Schätzung war für
Menschenarbeit gedacht und bleibt als Maßstab stehen, weil sie den **Umfang** beschreibt.
### Was entstanden ist
| | |
|---|---|
| Quelltext | rund 30.000 Zeilen unter `src/` (ohne erzeugten Prisma-Client) |
| Datenmodell | 20 Tabellen, 9 Aufzählungen, 2 Migrationen |
| Tests | 1131 Vitest-Tests, 101 Ende-zu-Ende-Abläufe (`INTERN`), 97 (`DIENSTLEISTUNG`) |
| Vorlagen | 7 Abläufe mit 155 Schritten, als veröffentlichte Version 1 im Seed |
| Dokumentation | Anleitung in neun Kapiteln (72 Seiten PDF), Architektur, Betrieb, 14 Issue-Dateien |
### Wo die Zeit hinging
Der teuerste Meilenstein war **#019 Betriebswege** (5 h 25) — und zwar nicht wegen der beiden
Skripte, sondern weil dort zum ersten Mal der **dokumentierte Weg eines Dritten** gefahren wurde:
Compose-Stand hochziehen, Seed nach Anleitung, Installation in einem leeren Debian. Vier Fehler lagen
genau dort, wo keine Suite hinsieht, und die Suite war die ganze Zeit grün.
Dahinter **#014 QR-Tags** (3 h 35), **#016 Verzeichnis** (3 h 25) und **#013 Läufe** (3 h 05) — alle
drei nicht wegen des Umfangs, sondern wegen der Randfälle: ein Scanner, der die native Schnittstelle
nie benutzte; ein Abgleich, der bei einem Tippfehler die ganze Firma abgeschaltet hätte; ein Passwort,
das zweimal verschieden erzeugt wurde. Keiner dieser Fehler fiel beim Bauen auf. Alle drei fand der
unabhängige Review.
### Was sich als richtig erwiesen hat
- **Ein Review-Agent je Meilenstein, der den Code nicht geschrieben hat.** In **jedem** der vierzehn
fand er etwas, in neun davon etwas Ernstes. Die teuersten Funde waren nie Tippfehler, sondern
Zusagen ohne Deckung: ein Kommentar, eine Oberflächenmeldung oder ein Testname, der etwas behauptet,
das der Code nicht tut. Das ist der häufigste Fehlertyp dieses Projekts — vor Logikfehlern, weit
vor Tippfehlern.
- **Regeln in der Datenbank, nicht nur im Dienst.** Zwölf CHECK-Bedingungen, zwei partielle
Eindeutigkeitsindizes, vier zusammengesetzte Fremdschlüssel und die Trigger auf veröffentlichten
Versionen haben mehrfach Fehler abgefangen, die durch die Service-Schicht gekommen wären — zuletzt
in #017, als der Seed eine veröffentlichte Version füllen wollte.
- **Gegen den gebauten Stand prüfen.** `next dev` hydriert in dieser Umgebung nicht; ein Screenshot
beweist nichts über Bedienbarkeit. Die Regel steht seit #010 in `CLAUDE.md` und hat sich in jedem
Oberflächen-Meilenstein bewährt.
### Was ich anders machen würde
- **Zweimal gegen einen veralteten Server geprüft** (#016, #018), beide Male eine halbe Stunde. Die
Konsequenz steht jetzt in der Arbeitsweise: erst `ps -eo lstart` gegen `.next/BUILD_ID` halten,
dann der Seite glauben.
- **Einmal in die falsche Datenbank geschrieben** (#017): Testprozess und Server zeigten
woandershin, und die Meldung hieß „0 Treffer". Seit #018 prüft das ein `global-setup` vorab.
- **Zwei eigene Tests konnten nicht scheitern** (#017, #018). Seitdem gehört die Gegenprobe zum
Schreiben eines Tests: einmal absichtlich verletzen und sehen, ob er rot wird.
- **Parallele Agenten im selben Verzeichnis** haben sich einmal gegenseitig eine Datei überschrieben
(#018). Die Trennung muss an Dateien verlaufen, nicht an Themen.
### Was bewusst offen bleibt
Sechs Folge-Issues für `v0.2.0`: Vorgesetzter für von Hand angelegte Personen (#020), stabiler
Schlüssel für Seed-Vorlagen (#021), Schnellstart je Gerätetyp pflegen (#022), Protokollansicht
(#023). Dazu die Punkte, die der Abnahme-Review aufwirft. Nichts davon blockiert den Betrieb; alles
davon steht geschrieben, statt verschwiegen zu werden.