Commit Graph

10 Commits (3e47e02882340879a4c7d3e61e7a4cafdde311ac)

Author SHA1 Message Date
nexxo 8c1985d924 Sechs Folgepunkte aus den heutigen Pruefungen ins Register
tests / pest (push) Has been cancelled Details
tests / assets (push) Has been cancelled Details
tests / release (push) Has been cancelled Details
Sie standen bisher nur in Arbeitsdateien unter .superpowers/, und die sind
nicht Teil des Repos — beim naechsten Aufraeumen waeren sie weg. Das Register
ist der Ort, an dem der Betreiber sie sieht.

Zwei davon sind echte Fehler im laufenden Betrieb:

Die Zahlungssperre ruft shutdownVm() unbedingt, ohne zu fragen, ob der Gast
laeuft. Ein Kunde, dessen Maschine aus anderem Grund steht, bekommt seine
Sperre nie — der Lauf wirft, jede Nacht neu, und die Nachricht geht nie hinaus.
Gefunden, als eine geschaerfte Proxmox-Attrappe aufdeckte, dass neun
Mahnwesen-Pruefungen nur deshalb gruen waren, weil die alte Attrappe die Bitte
gegen eine stehende Maschine klaglos annahm.

Und ein abgebrochener vzdump laeuft nach dem Wurf weiter und schreibt sein
Vollarchiv fertig — auf dieselbe Ablage, auf der die naechtlichen Sicherungen
aller anderen Kunden dieses Hosts liegen.

Damit stehen zwoelf Punkte im Register, aber KEINER mehr in der Gruppe
"verkauft und nicht geliefert". Das war heute Morgen anders.
2026-08-04 17:43:34 +02:00
nexxo bf95f9da8a Zweiter Sicherungsort ebenfalls aus dem Verkauf nehmen, eigener Schalter
tests / pest (push) Has been cancelled Details
tests / assets (push) Has been cancelled Details
tests / release (push) Has been cancelled Details
extra_backups verspricht einen zweiten, getrennten Sicherungsort, den es
nicht gibt (nur die erste, tägliche Sicherung läuft). Gesperrt nach dem
Muster von collabora_pro, aber mit eigener Konstante
(EXTRA_BACKUPS_ON_SALE statt OFFICE_ON_SALE) und eigenem Abweisungssatz,
weil beide Module aus unterschiedlichen Gründen fehlen und zu
unterschiedlichen Zeitpunkten zurückkommen. Keine Bestandsbuchung
betroffen (subscription_addons enthielt auf dieser Maschine keine Zeile
dazu). OpenWork-Eintrag von SOLD auf PLANNED umgestellt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 17:28:03 +02:00
nexxo d3ac839b1e Office (collabora_pro) auch über die Abrechnungsseite sperren
tests / pest (push) Has been cancelled Details
tests / assets (push) Has been cancelled Details
tests / release (push) Has been cancelled Details
Der Vortag nahm Office nur von der Preistafel — ein eingeloggter Team-
oder Business-Kunde hätte collabora_pro technisch weiterhin über
Billing::purchase() buchen können, weil AddonCatalogue den Preis
unverändert kannte und die Abrechnungsseite nie danach fragte. Der
Schalter (OFFICE_ON_SALE) wandert deshalb vom LandingController in
AddonCatalogue, der einzigen Stelle, die Preistafel UND Abrechnungsseite
kennen; eine neue Methode saleRefusal() weist die Buchung an beiden
Enden ab (Kauf-Aufruf und Karten-Anzeige), ohne BookAddon/GrantAddon
anzufassen — Bestandsfälle und Geschenke des Betreibers bleiben möglich.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 17:10:07 +02:00
nexxo 42fd059fdd Office aus dem Verkauf nehmen, abschaltbar statt gelöscht
tests / pest (push) Has been cancelled Details
tests / assets (push) Has been cancelled Details
tests / release (push) Has been cancelled Details
Office (ONLYOFFICE über einen künftigen gemeinsamen Dokumentenserver) wurde
an drei Stellen beworben, ohne dass irgendwo im Repository ein Dokumentenserver
existiert: der Kachel "Office im Browser" auf der Preistafel, dem Paketmerkmal
`office` in Team und Business, und dem Zusatzmodul `collabora_pro` für
22,80 €/Monat. Alle drei sind jetzt stillgelegt, aber nicht gelöscht — ein
einziger dokumentierter Schalter (LandingController::OFFICE_ON_SALE) plus eine
neu veröffentlichte Planversion holen das Versprechen zurück, sobald der
Dokumentenserver steht.

Das Paketmerkmal wird über das im Katalog bereits etablierte Handover-Muster
entfernt (neue Migration, analog zu switch_to_new_plan_ladder): die laufende
Version von Team/Business wird geschlossen und durch eine identische ohne
`office` ersetzt. Bestehende Verträge bleiben auf ihrer alten, eingefrorenen
Version stehen und behalten das Merkmal unverändert.

Die dritte Planversion für Team/Business hat 24 Bestandstests berührt, die
eine feste Versionsnummer oder eine feste Preis-/Versionszahl annahmen —
repariert, überwiegend durch dynamisches Lesen der aktuellen Version statt
eines eingetippten Werts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 16:49:32 +02:00
nexxo 5fe33553ad Zertifikats-Spec auf Weg B umgeschrieben, Registereintrag gestrichen
tests / pest (push) Has been cancelled Details
tests / assets (push) Has been cancelled Details
tests / release (push) Has been cancelled Details
Die Spec fuer die Host-Konsole (docs/superpowers/specs/2026-08-01-host-
konsole-zertifikat-design.md) war auf einer eigenen DNS-Zone und Proxmox'
Alias-Modus fuer DNS-01 gebaut. Hetzner kennt keine Subzonen (docs.hetzner.
com/networking/dns/faq/zones/, Artikel NE-7597D: "Subzones are not
supported") -- damit entfallen eigene Zone, CNAME je Host, Alias-Modus und
der pro Host verteilte DNS-Token ersatzlos. Der Grund bleibt in der Spec
stehen, statt geloescht zu werden, damit niemand denselben Weg in einem
halben Jahr erneut vorschlaegt.

Weg B, wie im Register vorgegeben: CluPilot stellt zentral aus, DNS-01
ueber den vorhandenen kontoweiten Hetzner-Token, Zertifikat per SSH
(RemoteShell::putFile + `pvenode cert set --force --restart`, geprueft
gegen die Proxmox-Dokumentation) auf den Host, Erneuerung als geplanter Job
auf der Bereitstellungs-Warteschlange (dieselbe Grenze wie SyncVpnPeers --
nur queue-provisioning erreicht einen Host ueber den Tunnel). Dazu ein
Vergleich mit der Kundeninstanz (ConfigureDnsAndTls, HTTP-01) und eine
genaue Bestandsaufnahme der Bereitschaftsseite: sie kennt heute kein
Zertifikat, weder fuer Hosts noch, trotz ersten Anscheins, uebertragbar
fuer die Plattform -- CertificateSweep/ProxyHost misst nur oeffentlich
erreichbare Namen und laeuft im falschen Container fuer einen Host-FQDN.

Im Code bestaetigt und in der Spec vermerkt: RrsetId::zone() ist heute fest
auf die Kundenzone verdrahtet, ein Host-FQDN liegt aber in der
Plattformzone -- das ist Bauarbeit, keine offene Entscheidung. Offen bleibt
nur, welches Werkzeug das ACME-Protokoll auf CluPilot-Seite spricht (keine
Bibliothek/kein Tool dafuer im Repo) und ob Plattform- und Kundenzone im
selben Hetzner-Projekt liegen -- beides als offene Fragen benannt, keine
davon blockiert den Rest des Ablaufs.

Registereintrag in OpenWork.php gestrichen: die Spec beschreibt keinen
toten Weg mehr, und genau das war der einzige Punkt, den der Eintrag
festhielt.

Getestet: php artisan test --filter=OpenWork, 8 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 15:31:28 +02:00
nexxo fd216be623 Der Hostnamen-Zähler übersteht jetzt das Löschen eines Rechenzentrums
Der Zähler lag auf der Rechenzentrums-ZEILE (next_host_number). Ein leeres
Rechenzentrum liess sich löschen - bewusst so entschieden, was nichts mehr
hält, soll entfernbar bleiben -, aber die Zeile nahm den Zähler mit. Wer
denselben Code neu anlegte, bekam eine frische Zeile mit dem Schema-Default
1, und der nächste Host hiess wieder <code>-01, obwohl dieser Name schon in
alten Protokollen, Sicherungen und DNS-Zwischenspeichern auf eine ANDERE
Maschine zeigt.

Der Zähler zieht deshalb in eine eigene Tabelle host_name_sequences um,
geführt über den rohen Code statt über die id der Rechenzentrums-Zeile.
ConfirmDeleteDatacenter bleibt unangetastet: das Löschen war nie das
Problem, nur was es mitriss. Die Migration überträgt den Bestand (fsn/hel)
vor dem Löschen der alten Spalte und ist gegen echtes MariaDB in beide
Richtungen geprüft (hoch, Werte kontrolliert, zurück, wieder hoch).

Neuer Test in HostNamingTest stellt den ganzen Bruch nach: Rechenzentrum
anlegen, Host vergeben, Host entfernen, über den echten Bestätigungsdialog
löschen, mit demselben Code neu anlegen - der nächste Name bleibt fortlaufend
statt wieder bei 01 zu beginnen. Gegen den unveränderten Code lief er rot
(HostName::preview lieferte nbg-01 statt nbg-02).

Registereintrag "Ein Zähler kann durch Löschen eines Rechenzentrums
zurückfallen" gestrichen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 15:11:58 +02:00
nexxo 507636f38f Die abgebuchte Domain wird jetzt wirklich von der Maschine genommen
tests / pest (push) Has been cancelled Details
tests / assets (push) Has been cancelled Details
tests / release (push) Has been cancelled Details
Der Registereintrag nannte den falschen Grund: das Deaktivieren startet sehr
wohl eine Provisionierung. CustomDomainAccess::deactivate() ruft seit Langem
ReapplyInstanceAddress, das legt einen Lauf der `address`-Pipeline an und
schickt AdvanceRunJob auf die provisioning-Warteschlange; ConfigureNextcloud
loescht dort trusted_domains 2 und ConfigureDnsAndTls schreibt den Router ohne
den Namen neu. Das ist gebaut und geprueft.

Der Schaden war trotzdem echt, nur eine Tuer weiter. Erreicht wurde deactivate()
allein ueber PlanChange::settleCustomDomain, also ueber den Paketwechsel. Der
zweite und haeufigere Weg, auf dem das Recht endet — der Kunde bucht das Modul
in der Abrechnung ab, clupilot:end-cancelled-addons haelt den Termin am Ende des
bezahlten Zeitraums — ging an dieser Stelle vorbei: BookAddon::cancel() lieferte
Speicher nach und sprach mit Stripe, fragte aber niemanden nach der Adresse. Die
Domain verschwand aus jeder Ansicht und blieb auf der Maschine stehen.

BookAddon::cancel() fragt jetzt CustomDomainAccess::enforce() — die ganze Regel,
nicht den Modulschluessel: wer von Team auf Business aufgestuft hat und sein
altes Modul loswird, behaelt die Domain, weil das Paket sie selbst traegt.

Und der Anstoss darf die Entscheidung nicht kippen. deactivate() faengt jetzt
einen Fehlschlag der Nachfuehrung ab und schreibt ihn als Fehler ins Log: die
Wahrheit steht in der Datenbank, die Maschine zieht nach, und eine Kuendigung
haengt nicht daran, ob ein fremder Host gerade antwortet.

Die Gegenrichtung brauchte nichts: der Entzug loescht die Domain-Spalte, also
traegt der Kunde sie nach der Neubuchung neu ein und weist sie neu nach — und
genau dort haengt seit jeher der Lauf, der sie wieder ausliefert. Ein Test haelt
das fest, damit es keine Einbahnstrasse wird.

Registereintrag gestrichen.

Rot gesehen: ohne den settleCustomDomain-Aufruf fallen drei der vier neuen
Tests; ohne das try/catch faellt der vierte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 14:50:46 +02:00
nexxo 29f13275f8 Das Register behauptete etwas Falsches: der Plan-Wechsel IST gebaut
"Ein Plan-Wechsel wird nirgends angewendet" stimmt seit laengerem nicht mehr.
ApplyPlanChange faehrt die plan-change-Pipeline, aufgerufen vom OrderObserver
bei einer Aufstufung und von clupilot:apply-due-plan-changes bei einer
Abstufung zum Laufzeitende. settleCustomDomain() hat sehr wohl einen Aufrufer
(ApplyPlanChange:256), und sieben Testdateien mit 43 Pruefungen decken den Weg.

Aufgeschrieben, weil der Kopfkommentar dieser Datei genau das ausschliesst:
"Ein Punkt verschwindet, wenn die Arbeit im selben Commit fertig wird, der ihn
streicht — und damit kann die Liste nicht behaupten, etwas sei offen, das es
laengst nicht mehr ist." Genau das ist passiert. Wer eine Liste fuehrt, deren
einziger Zweck Ehrlichkeit ist, muss sie mit der Arbeit streichen, nicht
danach.

Gepruefte Restliste: neun Punkte. Zwei davon (zweiter Sicherungsort, Office
Pro) haengen an Infrastruktur, die es noch nicht gibt; einer (Hostnamen-
Abnahme) an der echten Anlage; einer (Support-Mail) an einem SMTP-Konto, das
der Betreiber anlegen muss.
2026-08-04 14:29:21 +02:00
nexxo d7bb0f2e63 Offene Punkte: nach Dringlichkeit gruppiert, in der Formensprache der Bereitschaftsseite 2026-08-02 16:35:14 +02:00
nexxo 07c51474d3 Update drehte sich im Kreis: es ersetzte sich selbst mitten im Lauf; dazu eine Seite fuer offene Punkte 2026-08-02 02:02:25 +02:00