Merge the handoff
commit
6d35ab9f47
|
|
@ -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.**
|
||||
Loading…
Reference in New Issue