diff --git a/docs/handoffs/2026-07-31-stand-und-offene-punkte.md b/docs/handoffs/2026-07-31-stand-und-offene-punkte.md new file mode 100644 index 0000000..18822be --- /dev/null +++ b/docs/handoffs/2026-07-31-stand-und-offene-punkte.md @@ -0,0 +1,145 @@ +# Stand am 31. Juli 2026 und was offen ist + +Für die nächste Sitzung. Alles unten ist gemessen oder committet, nichts davon +ist Erinnerung. + +## Wo es steht + +`main` auf **v1.3.77**, alle Releases getaggt und gepusht, **2053 Tests grün**. +`feature/host-bootstrap` ist deckungsgleich mit `main`. + +Ausgeliefert in dieser Sitzung: der **Betriebsmodus** (Test-/Livebetrieb mit je +eigenem Platz pro Zugangsdatum, seit v1.3.64 — er lag fertig und ohne Tag +herum), das **Bootstrap-Skript** für die Host-Übernahme, der **Adminbereich** +dafür, der **Datei-Hostname** `files.clupilot.com`, die Seite **Betrieb → +Hostnamen** mit Zertifikatsübersicht, und mehrere Korrekturen an der +Bereitschaftsseite. + +## Offen 1 — Hetzner-DNS-Migration (dringend, blockiert Bestellungen) + +Steht vollständig in **`docs/handoffs/2026-07-31-hetzner-dns-api-migration.md`**: +Endpunkte aus `cloud.spec.json` gezogen, RRSet-Modell, beide Fallen im Namen. + +Kurz: die alte DNS-API ist abgeschaltet (301 auf die Weboberfläche). Der Token +des Betreibers ist bereits ein Cloud-Token und antwortet mit **200** an +`api.hetzner.cloud/v1/zones`. Es fehlt nur der Code — in `DnsTokenCheck` **und** +in `HttpHetznerDnsClient`. Solange das so steht, stirbt jede Bereitstellung in +`ConfigureDnsAndTls`, nachdem der Kunde bezahlt hat. + +**Das zuerst.** Es ist das Einzige auf dieser Liste, das laufendes Geschäft +verhindert. + +## Offen 2 — Die Bereitschaftsseite muss aus sich heraus behebbar sein + +Der Betreiber hat den Punkt gemacht, und er ist richtig: eine Seite, die Dinge +bemängelt, die man nur über eine Shell beheben kann, verschiebt die Arbeit +lediglich. Drei Stellen tun heute genau das. + +### a) Schlüsselpaar für Hosts erzeugen + +`ssh.private_key` liegt im Tresor und muss von Hand hineingelegt werden — es +gibt **keinen Erzeuger**. Heute: + +```bash +ssh-keygen -t ed25519 -N "" -C "clupilot" -f /tmp/clupilot-host +cat /tmp/clupilot-host # privater Teil in die Konsole +shred -u /tmp/clupilot-host /tmp/clupilot-host.pub +``` + +Ein Knopf **„Schlüsselpaar erzeugen"** neben dem Feld wäre klein und klar +begrenzt. `App\Services\Wireguard\Keypair::generate()` macht dasselbe für +WireGuard längst in PHP, mit der Begründung im Kopfkommentar — dort steht auch, +warum nicht in eine Shell ausgelagert wird. Für SSH braucht es ein +Ed25519-Paar; `sodium_crypto_sign_keypair()` liefert es, das OpenSSH-Format ist +die einzige Arbeit daran. + +Ein erzeugter privater Schlüssel darf **nicht** angezeigt werden — er geht +direkt in den Tresor, und angezeigt wird nur der öffentliche Teil. + +### b) Stripe-Katalog aus der Konsole abgleichen + +Der Befehl existiert und funktioniert: + +```bash +docker compose exec -T -u www-data app php artisan stripe:sync-catalogue --dry-run +``` + +Er soll aus der Konsole laufen. **Nicht synchron in einem Livewire-Aufruf**: er +spricht mit Stripe, legt Produkte und Preise an und kann Sekunden bis Minuten +brauchen. Also als Auftrag in die Warteschlange, mit einem Ergebnis, das die +Seite anzeigt — dasselbe Muster wie `UpdateChannel`, nur ohne den Agenten, denn +hier braucht es kein root. + +Der Trockenlauf gehört mit in die Oberfläche: erst zeigen, was entstünde, dann +ein zweiter Knopf. Der Befehl kann das schon, es fehlt nur der Weg dorthin. + +**Achtung:** `SyncStripeCatalogue` verweigert den Dienst, wenn der gespeicherte +Katalog zu einem anderen Konto gehört (Betriebsmodus). Diese Fehlermeldung muss +die Konsole wörtlich zeigen — sie ist eine Warnung vor dem Leeren eines +Katalogs, an dem laufende Verträge hängen. + +### c) Der Rest + +Stripe-Schlüssel, Signaturschlüssel, Firmendaten, Mailtransport: alles Felder, +die **Beheben** schon richtig verlinkt. Da ist nichts zu bauen. + +Beim Mailtransport reicht `mail.default` ungleich `log`/`array` — die +Arbeiterprozesse starten nach dem Speichern von selbst neu, weil sie ihre +Umgebung nur beim Start lesen. + +## Offen 3 — Die Abnahme des Bootstrap-Skripts + +**Von den zehn Aufgaben ist keine einzige auf echter Hardware gelaufen.** Der +Plan (`docs/superpowers/plans/2026-07-30-host-uebernahme-bootstrap-skript.md`) +hat 19 abgehakte und 12 offene Punkte, und alle zwölf sind Hardware-Abnahmen. + +Zwei bestehen ausdrücklich darauf und sind sonst bloße Behauptungen: + +- Die **Selbstrücknahme der Brücke** muss absichtlich ausgelöst werden — eine + Brücke bauen, die die Maschine vom Netz nimmt, und zusehen, wie sie von selbst + zurückkommt. +- Die **Vorlage** muss wirklich geklont, gestartet und mit `occ status` als + `www-data` befragt werden. + +Die Vorbedingungen stehen inzwischen: `files.clupilot.com` liefert das Archiv +aus (404 ohne gültigen Code, gemessen), der Adminbereich zeigt die Zeile, und +`--fqdn` wird mitgegeben. Was fehlt, ist eine dedizierte Maschine mit +`/dev/kvm` im Rettungssystem. + +Was beim ersten Lauf stehenbleibt, gehört ins Runbook +(`docs/runbooks/host-bootstrap.md`) — dessen letzter Abschnitt sagt selbst, dass +er aus dem Skript geschrieben ist und nicht aus Vorfällen. + +## Zwei lose Enden + +- **Die zwei Phantom-Hosts** stehen weiter im Pool. In dieser Sitzung nicht + angefasst. +- **`feature/betriebsmodus`** ist inhaltlich in `main` (über + `feature/host-bootstrap`), der Zweig selbst kann weg. + +## Was diese Sitzung über das Ausliefern gelernt hat + +Steht auch in den Projektnotizen, aber hier, weil es zweimal Geld gekostet hat: + +**Release-Schritte nie mit `&&` verketten.** Mergen, Version setzen, committen, +taggen, pushen — getrennte Aufrufe, mit `git log --oneline -1` dazwischen. In +einer Kette hat ein gescheiterter Merge trotzdem einen Tag am falschen Commit +erzeugt und gepusht. Betraf v1.3.66 und v1.3.69; beide gelöscht, beide Nummern +übersprungen. + +**Vor jedem Release `pwd` UND `git branch --show-current`.** `cd` hält zwischen +Aufrufen nicht. v1.3.68 wurde im Worktree geschnitten und landete auf dem +Feature-Zweig statt auf `main`. + +**Ein falscher Tag wird gelöscht und die Nummer übersprungen**, nie verschoben. + +## Wiederkehrendes Muster in den Funden + +Fast jeder Fehler dieser Sitzung war dieselbe Sorte: **eine Anzeige, die +zuversichtlicher war als ihre Grundlage.** Grünes „Erfüllt" über rotem „nicht in +Ordnung". „In diesem Konto liegt keine Zone", hergeleitet aus einer HTML-Seite. +Ein Kommentar, der behauptete, der Installer lege eine Datei an, die er nie +angelegt hat. + +R19 nennt das beim Namen, und es lohnt sich, beim Lesen dieses Codes danach zu +suchen: **eine Zusage, die keine ist, hält den Nächsten vom Nachsehen ab.**